| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254 |
- /*
- ** 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. //
- // //
- ////////////////////////////////////////////////////////////////////////////////
- // ChinookAIUpdate.cpp //////////
- #include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
- #define DEFINE_VETERANCY_NAMES // for TheVeterancyNames[]
- #include "Common/ActionManager.h"
- #include "Common/DrawModule.h"
- #include "Common/GameState.h"
- #include "Common/GlobalData.h"
- #include "Common/RandomValue.h"
- #include "Common/Team.h"
- #include "Common/ThingFactory.h"
- #include "Common/ThingTemplate.h"
- #include "Common/Xfer.h"
- #include "GameClient/Drawable.h"
- #include "GameClient/GameClient.h"
- #include "GameLogic/AIPathfind.h"
- #include "GameLogic/Locomotor.h"
- #include "GameLogic/Module/ContainModule.h"
- #include "GameLogic/Module/ChinookAIUpdate.h"
- #include "GameLogic/Module/PhysicsUpdate.h"
- #include "GameLogic/PartitionManager.h"
- const Real BIGNUM = 99999.0f;
- #ifdef _INTERNAL
- // for occasional debugging...
- //#pragma optimize("", off)
- //#pragma MESSAGE("************************************** WARNING, optimization disabled for debugging purposes")
- #endif
- //-------------------------------------------------------------------------------------------------
- enum ChinookAIStateType
- {
- // note that these must be distinct (numerically) from AIStateType. ick.
- ChinookAIStateType_FIRST = 1000,
- TAKING_OFF,
- LANDING,
- MOVE_TO_AND_LAND,
-
- MOVE_TO_AND_EVAC,
- LAND_AND_EVAC,
- EVAC_AND_TAKEOFF,
-
- MOVE_TO_AND_EVAC_AND_EXIT,
- LAND_AND_EVAC_AND_EXIT,
- EVAC_AND_EXIT,
- TAKEOFF_AND_EXIT,
- HEAD_OFF_MAP,
-
- MOVE_TO_COMBAT_DROP,
- DO_COMBAT_DROP
- };
- //-------------------------------------------------------------------------------------------------
- //-------------------------------------------------------------------------------------------------
- //-------------------------------------------------------------------------------------------------
- //-------------------------------------------------------------------------------------------------
- static Real calcDistSqr(const Coord3D& a, const Coord3D& b)
- {
- return sqr(a.x-b.x) + sqr(a.y-b.y) + sqr(a.z-b.z);
- }
- //-------------------------------------------------------------------------------------------------
- static Object* getPotentialRappeller(Object* obj)
- {
- const ContainedItemsList* items = obj->getContain() ? obj->getContain()->getContainedItemsList() : NULL;
- if (items)
- {
- for (ContainedItemsList::const_iterator it = items->begin(); it != items->end(); ++it )
- {
- Object* rider = *it;
- if (rider->isKindOf(KINDOF_CAN_RAPPEL))
- {
- return rider;
- }
- }
- }
- return NULL;
- }
- //----------------------------------------------------------------------------------------------------------
- class ChinookEvacuateState : public State
- {
- MEMORY_POOL_GLUE_WITH_USERLOOKUP_CREATE(ChinookEvacuateState, "ChinookEvacuateState")
- protected:
- // snapshot interface STUBBED - no member vars to save. jba.
- virtual void crc( Xfer *xfer ){};
- virtual void xfer( Xfer *xfer ){};
- virtual void loadPostProcess(){};
- public:
- ChinookEvacuateState( StateMachine *machine ) : State( machine, "ChinookEvacuateState" ) { }
- StateReturnType onEnter()
- {
- Object* obj = getMachineOwner();
- if( obj->getContain() )
- {
- obj->getContain()->removeAllContained(FALSE);
- }
- obj->getTeam()->setActive(); // why? I don't know.
- return STATE_SUCCESS;
- }
- virtual StateReturnType update()
- {
- return STATE_SUCCESS;
- }
- };
- EMPTY_DTOR(ChinookEvacuateState)
- //-------------------------------------------------------------------------------------------------
- class ChinookHeadOffMapState : public State
- {
- MEMORY_POOL_GLUE_WITH_USERLOOKUP_CREATE(ChinookHeadOffMapState, "ChinookHeadOffMapState")
- //I'm outta here
- protected:
- // snapshot interface STUBBED - no member vars to save. jba.
- virtual void crc( Xfer *xfer ){};
- virtual void xfer( Xfer *xfer ){};
- virtual void loadPostProcess(){};
- public:
- ChinookHeadOffMapState( StateMachine *machine ) : State( machine, "ChinookHeadOffMapState" ) {}
- StateReturnType onEnter() // Give move order out of town
- {
- Object *owner = getMachineOwner();
- AIUpdateInterface *ai = owner->getAIUpdateInterface();
- // 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->aiMoveToPosition( &exitCoord, CMD_FROM_AI );
- ai->getCurLocomotor()->setAllowInvalidPosition(true);
- return STATE_CONTINUE;
- }
- StateReturnType update()
- {
- Object *owner = getMachineOwner();
- Region3D mapRegion;
- TheTerrainLogic->getExtentIncludingBorder( &mapRegion );
- if (!mapRegion.isInRegionNoZ( owner->getPosition() ))
- {
- TheGameLogic->destroyObject(owner);
- return STATE_SUCCESS;
- }
- return STATE_CONTINUE;
- }
- };
- EMPTY_DTOR(ChinookHeadOffMapState)
- //-------------------------------------------------------------------------------------------------
- class ChinookTakeoffOrLandingState : public State
- {
- MEMORY_POOL_GLUE_WITH_USERLOOKUP_CREATE(ChinookTakeoffOrLandingState, "ChinookTakeoffOrLandingState")
- private:
- Coord3D m_destLoc;
- Bool m_landing;
- protected:
- // snapshot interface
- virtual void crc( Xfer *xfer )
- {
- // empty
- }
- virtual void xfer( Xfer *xfer )
- {
- // version
- XferVersion currentVersion = 1;
- XferVersion version = currentVersion;
- xfer->xferVersion( &version, currentVersion );
- xfer->xferCoord3D(&m_destLoc);
- xfer->xferBool(&m_landing);
- }
- virtual void loadPostProcess()
- {
- // empty
- }
- public:
- ChinookTakeoffOrLandingState( StateMachine *machine, Bool landing ) : m_landing(landing), State( machine, "ChinookTakeoffOrLandingState" )
- {
- m_destLoc.zero();
- }
- virtual StateReturnType onEnter()
- {
- Object* obj = getMachineOwner();
- ChinookAIUpdate* ai = (ChinookAIUpdate*)obj->getAIUpdateInterface();
- ai->friend_setFlightStatus(m_landing ? CHINOOK_LANDING : CHINOOK_TAKING_OFF);
- if( m_landing )
- {
- // A chinook given transport duty loses his supplies.
- while( ai->loseOneBox() );
- }
- // kill any drift...
- obj->getPhysics()->scrubVelocity2D(0);
- ai->chooseLocomotorSet(LOCOMOTORSET_NORMAL);
- Locomotor* loco = ai->getCurLocomotor();
- loco->setUsePreciseZPos(true);
- loco->setUltraAccurate(true);
- m_destLoc = *obj->getPosition();
- const Bool onlyHealthyBridges = true; // ignore dead bridges.
- PathfindLayerEnum layerAtDest = TheTerrainLogic->getHighestLayerForDestination(&m_destLoc, onlyHealthyBridges);
- m_destLoc.z = TheTerrainLogic->getLayerHeight(m_destLoc.x, m_destLoc.y, layerAtDest);
- if (m_landing)
- {
- Coord3D tmp;
- FindPositionOptions options;
- options.maxRadius = obj->getGeometryInfo().getBoundingCircleRadius() * 100.0f;
- if (ThePartitionManager->findPositionAround(&m_destLoc, &options, &tmp))
- {
- m_destLoc = tmp;
- TheAI->pathfinder()->adjustToLandingDestination(obj, &m_destLoc);
- }
- // recalc, since it may have changed. note that findPositionAround() will ALWAYS
- // return a position on the ground proper, so if our initial search start pos was
- // above a bridge, this will put us below the bridge, which would be unfortunate.
- // so recheck to be sure. (note that the partitionmgr is 2d-only, so if it sez that
- // the position on the ground at that xy is clear, it will be clear for both the
- // ground proper and the bridge itself.) also note: don't call objectInteractsWithBridgeLayer(),
- // since it assumes that things that aren't close in z shouldn't interact.
- tmp = m_destLoc;
- tmp.z = obj->getPosition()->z;
- layerAtDest = TheTerrainLogic->getHighestLayerForDestination(&tmp, onlyHealthyBridges);
- m_destLoc.z = TheTerrainLogic->getLayerHeight(m_destLoc.x, m_destLoc.y, layerAtDest);
- obj->setLayer(layerAtDest);
- }
- else
- {
- m_destLoc.z += loco->getPreferredHeight();
- obj->setLayer(LAYER_GROUND);
- }
- return STATE_CONTINUE;
- }
- virtual StateReturnType update()
- {
- Object* obj = getMachineOwner();
- if (obj->isEffectivelyDead())
- return STATE_FAILURE;
- ChinookAIUpdate* ai = (ChinookAIUpdate*)obj->getAIUpdateInterface();
- ai->setLocomotorGoalPositionExplicit(m_destLoc);
- const Real THRESH = 3.0f;
- const Real THRESH_SQR = THRESH*THRESH;
- if (calcDistSqr(*obj->getPosition(), m_destLoc) <= THRESH_SQR)
- return STATE_SUCCESS;
- return STATE_CONTINUE;
- }
- virtual void onExit( StateExitType status )
- {
- Object* obj = getMachineOwner();
- ChinookAIUpdate* ai = (ChinookAIUpdate*)obj->getAIUpdateInterface();
- ai->friend_setFlightStatus(m_landing ? CHINOOK_LANDED : CHINOOK_FLYING);
- // Paranoia checks - sometimes onExit is called when we are
- // shutting down, and not all pieces are valid. CurLocomotor
- // is definitely null in some cases. jba.
- Locomotor* loco = ai->getCurLocomotor();
- if (loco)
- {
- loco->setUsePreciseZPos(false);
- loco->setUltraAccurate(false);
- // don't restore lift if dead -- this may fight with JetSlowDeathBehavior!
- if (!obj->isEffectivelyDead())
- loco->setMaxLift(BIGNUM);
- }
- if (m_landing)
- {
- ai->chooseLocomotorSet(LOCOMOTORSET_TAXIING);
- }
- else
- {
- // when takeoff is complete, always go back to layer-ground, rather than
- // some bridge layer.
- obj->setLayer(LAYER_GROUND);
- }
- }
- };
- EMPTY_DTOR(ChinookTakeoffOrLandingState)
- //-------------------------------------------------------------------------------------------------
- class ChinookCombatDropState : public State
- {
- MEMORY_POOL_GLUE_WITH_USERLOOKUP_CREATE(ChinookCombatDropState, "ChinookCombatDropState")
- private:
- struct RopeInfo
- {
- Drawable* ropeDrawable;
- DrawableID ropeID; // used only during save-load process
- Matrix3D dropStartMtx;
- Real ropeSpeed;
- Real ropeLen;
- Real ropeLenMax;
- UnsignedInt nextDropTime;
- std::list<ObjectID> rappellerIDs;
- };
- std::vector<RopeInfo> m_ropes;
- void removeDoneRappellers()
- {
- for (std::vector<RopeInfo>::iterator it = m_ropes.begin(); it != m_ropes.end(); ++it)
- {
- for (std::list<ObjectID>::iterator oit = it->rappellerIDs.begin(); oit != it->rappellerIDs.end(); )
- {
- Object* rappeller = TheGameLogic->findObjectByID(*oit);
- if (rappeller == NULL || rappeller->isEffectivelyDead() || !rappeller->isAboveTerrain())
- {
- oit = it->rappellerIDs.erase(oit);
- }
- else
- {
- ++oit;
- }
- }
- }
- }
- static void initRopeParms(Drawable* rope, Real length, Real width, const RGBColor& color, Real wobbleLen, Real wobbleAmp, Real wobbleRate)
- {
- RopeDrawInterface* tdi = NULL;
- for (DrawModule** d = rope->getDrawModules(); *d; ++d)
- {
- if ((tdi = (*d)->getRopeDrawInterface()) != NULL)
- {
- tdi->initRopeParms(length, width, color, wobbleLen, wobbleAmp, wobbleRate);
- }
- }
- }
- static void setRopeCurLen(Drawable* rope, Real length)
- {
- RopeDrawInterface* tdi = NULL;
- for (DrawModule** d = rope->getDrawModules(); *d; ++d)
- {
- if ((tdi = (*d)->getRopeDrawInterface()) != NULL)
- {
- tdi->setRopeCurLen(length);
- }
- }
- }
- static void setRopeSpeed(Drawable* rope, Real curSpeed, Real maxSpeed, Real accel)
- {
- RopeDrawInterface* tdi = NULL;
- for (DrawModule** d = rope->getDrawModules(); *d; ++d)
- {
- if ((tdi = (*d)->getRopeDrawInterface()) != NULL)
- {
- tdi->setRopeSpeed(curSpeed, maxSpeed, accel);
- }
- }
- }
- protected:
- // snapshot interface
- virtual void crc( Xfer *xfer )
- {
- // empty
- }
- virtual void xfer( Xfer *xfer )
- {
- // version
- const XferVersion currentVersion = 2;
- XferVersion version = currentVersion;
- xfer->xferVersion( &version, currentVersion );
- Int numRopes = m_ropes.size();
- xfer->xferInt(&numRopes);
- if (version >= 2)
- {
- if (xfer->getXferMode() == XFER_LOAD)
- {
- if (!m_ropes.empty())
- {
- DEBUG_CRASH(( "ChinookCombatDropState - ropes should be empty\n" ));
- throw SC_INVALID_DATA;
- }
- m_ropes.resize(numRopes);
- }
-
- for (Int i = 0; i < numRopes; ++i)
- {
- RopeInfo info;
- if (xfer->getXferMode() == XFER_SAVE)
- {
- info = m_ropes[i];
- // always overwrite this, since it's probably stale
- info.ropeID = info.ropeDrawable ? info.ropeDrawable->getID() : INVALID_DRAWABLE_ID;
- }
- xfer->xferDrawableID(&info.ropeID);
- xfer->xferMatrix3D(&info.dropStartMtx);
- xfer->xferReal(&info.ropeSpeed);
- xfer->xferReal(&info.ropeLen);
- xfer->xferReal(&info.ropeLenMax);
- xfer->xferUnsignedInt(&info.nextDropTime);
- xfer->xferSTLObjectIDList(&info.rappellerIDs);
- if (xfer->getXferMode() == XFER_LOAD)
- {
- info.ropeDrawable = NULL; // filled in via loadPostProcess
- m_ropes[i] = info;
- }
- }
- }
- }
- virtual void loadPostProcess()
- {
- for (std::vector<RopeInfo>::iterator it = m_ropes.begin(); it != m_ropes.end(); ++it)
- {
- it->ropeDrawable = TheGameClient->findDrawableByID(it->ropeID);
- // always nuke this, since we're done with it till we save/load again
- it->ropeID = INVALID_DRAWABLE_ID;
- }
- }
- public:
- ChinookCombatDropState( StateMachine *machine ): State( machine, "ChinookCombatDropState" ) { }
- // --------------
- virtual StateReturnType onEnter()
- {
- Object* obj = getMachineOwner();
- Drawable* draw = obj->getDrawable();
- if (draw == NULL)
- return STATE_FAILURE;
- ChinookAIUpdate* ai = (ChinookAIUpdate*)obj->getAIUpdateInterface();
- const ChinookAIUpdateModuleData* d = ai->friend_getData();
- obj->setDisabled( DISABLED_HELD );
- ai->friend_setFlightStatus(CHINOOK_DOING_COMBAT_DROP);
- // A chinook given combat drop duty also loses his supplies.
- while( ai->loseOneBox() );
- UnsignedInt now = TheGameLogic->getFrame();
- const ThingTemplate* ropeTmpl = TheThingFactory->findTemplate(d->m_ropeName);
-
- const Int MAX_BONES = 32;
- Coord3D ropePos[MAX_BONES];
- Matrix3D dropMtx[MAX_BONES];
- Int ropeCount = draw->getPristineBonePositions("RopeStart", 1, ropePos, NULL, MAX_BONES);
- Int dropCount = draw->getPristineBonePositions("RopeEnd", 1, NULL, dropMtx, MAX_BONES);
- Int numRopes = d->m_numRopes;
- if (numRopes > ropeCount) numRopes = ropeCount;
- if (numRopes > dropCount) numRopes = dropCount;
- if (numRopes <= 0)
- return STATE_FAILURE;
- m_ropes.clear();
- for (Int i = 0; i < numRopes; ++i)
- {
- RopeInfo info;
- obj->convertBonePosToWorldPos( NULL, &dropMtx[i], NULL, &info.dropStartMtx );
-
- info.ropeDrawable = ropeTmpl ? TheThingFactory->newDrawable(ropeTmpl) : NULL;
- if (info.ropeDrawable)
- {
- obj->convertBonePosToWorldPos( &ropePos[i], NULL, &ropePos[i], NULL );
- info.ropeDrawable->setPosition(&ropePos[i]);
- info.ropeSpeed = 0.0f;
- info.ropeLen = 1.0f;
- const Bool onlyHealthyBridges = true; // ignore dead bridges.
- PathfindLayerEnum layerAtDest = TheTerrainLogic->getHighestLayerForDestination(&ropePos[i], onlyHealthyBridges);
- info.ropeLenMax = ropePos[i].z - TheTerrainLogic->getLayerHeight(ropePos[i].x, ropePos[i].y, layerAtDest) - d->m_ropeFinalHeight;
- initRopeParms(info.ropeDrawable, info.ropeLenMax, d->m_ropeWidth, d->m_ropeColor, d->m_ropeWobbleLen, d->m_ropeWobbleAmp, d->m_ropeWobbleRate);
- }
- info.nextDropTime = now + GameLogicRandomValue(d->m_perRopeDelayMin, d->m_perRopeDelayMax) - d->m_perRopeDelayMin;
- info.rappellerIDs.clear();
- m_ropes.push_back(info);
- }
- return STATE_CONTINUE;
- }
- // --------------
- virtual StateReturnType update()
- {
- Object* obj = getMachineOwner();
- ChinookAIUpdate* ai = (ChinookAIUpdate*)obj->getAIUpdateInterface();
- const ChinookAIUpdateModuleData* d = ai->friend_getData();
-
- if (obj->isEffectivelyDead())
- {
- return STATE_FAILURE;
- }
- // first, eliminate "done" rappellers
- removeDoneRappellers();
- UnsignedInt now = TheGameLogic->getFrame();
- // ok, now check each rope: if it's empty, or we're at the next drop time, spawn a new rappeller
- Int numRopesInUse = 0;
- for (std::vector<RopeInfo>::iterator it = m_ropes.begin(); it != m_ropes.end(); ++it)
- {
- if (it->ropeLen < it->ropeLenMax)
- {
- it->ropeSpeed += fabs(TheGlobalData->m_gravity);
- if (it->ropeSpeed > d->m_ropeDropSpeed)
- it->ropeSpeed = d->m_ropeDropSpeed;
- it->ropeLen += it->ropeSpeed;
- setRopeCurLen(it->ropeDrawable, it->ropeLen);
- if (d->m_waitForRopesToDrop)
- {
- // can't use this rope till it's dropped all the way
- ++it->nextDropTime;
- continue;
- }
- }
- if (now >= it->nextDropTime)
- {
- Object* rappeller = getPotentialRappeller(obj);
- if (rappeller != NULL)
- {
- ExitInterface *exitInterface = obj->getObjectExitInterface();
- ExitDoorType exitDoor = exitInterface ? exitInterface->reserveDoorForExit(rappeller->getTemplate(), rappeller) : DOOR_NONE_AVAILABLE;
- if(exitDoor != DOOR_NONE_AVAILABLE)
- {
- exitInterface->exitObjectViaDoor(rappeller, exitDoor);
- }
- else
- {
- DEBUG_CRASH(("rappeller is not free to exit... what?"));
- }
- rappeller->setTransformMatrix(&it->dropStartMtx);
- AIUpdateInterface* rappellerAI = rappeller ? rappeller->getAIUpdateInterface() : NULL;
- if (rappellerAI)
- {
- rappellerAI->setDesiredSpeed(d->m_rappelSpeed);
- rappellerAI->aiRappelInto(getMachineGoalObject(), *getMachineGoalPosition(), CMD_FROM_AI);
- }
- it->rappellerIDs.push_back(rappeller->getID());
- it->nextDropTime = now + GameLogicRandomValue(d->m_perRopeDelayMin, d->m_perRopeDelayMax);
- }
- }
- if (!it->rappellerIDs.empty())
- {
- ++numRopesInUse;
- }
- }
- if (numRopesInUse == 0 && getPotentialRappeller(obj) == NULL)
- {
- // we're done!
- return STATE_SUCCESS;
- }
- return STATE_CONTINUE;
- }
- // --------------
- virtual void onExit( StateExitType status )
- {
- Object* obj = getMachineOwner();
- ChinookAIUpdate* ai = (ChinookAIUpdate*)obj->getAIUpdateInterface();
- const ChinookAIUpdateModuleData* d = ai->friend_getData();
- obj->clearDisabled( DISABLED_HELD );
- ai->friend_setFlightStatus(CHINOOK_FLYING);
- if (obj->isEffectivelyDead())
- {
- // oops. drop the rangers.
- for (std::vector<RopeInfo>::iterator it = m_ropes.begin(); it != m_ropes.end(); ++it)
- {
- for (std::list<ObjectID>::iterator oit = it->rappellerIDs.begin(); oit != it->rappellerIDs.end(); ++oit)
- {
- Object* rappeller = TheGameLogic->findObjectByID(*oit);
- AIUpdateInterface* rappellerAI = rappeller ? rappeller->getAIUpdateInterface() : NULL;
- if (rappellerAI != NULL)
- {
- rappellerAI->aiIdle(CMD_FROM_AI);
- }
- }
- }
- }
- UnsignedInt now = TheGameLogic->getFrame();
- for (Int i = 0; i < m_ropes.size(); ++i)
- {
- if (m_ropes[i].ropeDrawable)
- {
- const UnsignedInt ROPE_EXPIRATION_TIME = LOGICFRAMES_PER_SECOND * 5;
- const Real initialSpeed = TheGlobalData->m_gravity * 30; // give it a little kick
- setRopeSpeed(m_ropes[i].ropeDrawable, initialSpeed, d->m_ropeDropSpeed, TheGlobalData->m_gravity);
- m_ropes[i].ropeDrawable->setExpirationDate(now + ROPE_EXPIRATION_TIME);
- m_ropes[i].ropeDrawable = NULL; // we're done with it, so null it so we won't save it
- }
- }
- m_ropes.clear();
- }
- };
- EMPTY_DTOR(ChinookCombatDropState)
- //-----------------------------------------------------------------------------------------------------------
- /**
- * Move to the GoalPosition, or GoalObject.
- */
- class ChinookMoveToBldgState : public AIMoveToState
- {
- MEMORY_POOL_GLUE_WITH_USERLOOKUP_CREATE(ChinookMoveToBldgState, "ChinookMoveToBldgState")
- private:
- Real m_oldPreferredHeight;
- Real m_newPreferredHeight;
- Real m_destZ;
- protected:
- // snapshot interface
- virtual void crc( Xfer *xfer )
- {
- // empty
- }
- virtual void xfer( Xfer *xfer )
- {
- // version
- XferVersion currentVersion = 1;
- XferVersion version = currentVersion;
- xfer->xferVersion( &version, currentVersion );
- xfer->xferReal(&m_oldPreferredHeight);
- xfer->xferReal(&m_newPreferredHeight);
- xfer->xferReal(&m_destZ);
- }
- virtual void loadPostProcess()
- {
- // empty
- }
- public:
- ChinookMoveToBldgState( StateMachine *machine ): AIMoveToState( machine ) { }
- virtual StateReturnType onEnter()
- {
- Object* obj = getMachineOwner();
- ChinookAIUpdate* ai = (ChinookAIUpdate*)obj->getAIUpdateInterface();
- const ChinookAIUpdateModuleData* d = ai->friend_getData();
- Locomotor* loco = ai->getCurLocomotor();
- loco->setUltraAccurate(true);
- m_oldPreferredHeight = loco->getPreferredHeight();
- m_newPreferredHeight = m_oldPreferredHeight;
- const Coord3D* destPos;
- Object* bldg = getMachineGoalObject();
- if (bldg != NULL && !bldg->isEffectivelyDead() && bldg->isKindOf(KINDOF_STRUCTURE))
- {
- destPos = bldg->getPosition();
- m_newPreferredHeight = bldg->getGeometryInfo().getMaxHeightAbovePosition() + d->m_minDropHeight;
- if (m_newPreferredHeight < m_oldPreferredHeight)
- m_newPreferredHeight = m_oldPreferredHeight;
- }
- else
- {
- destPos = getMachineGoalPosition();
- }
- loco->setPreferredHeight(m_newPreferredHeight);
- m_destZ = TheTerrainLogic->getGroundHeight(destPos->x, destPos->y) + m_newPreferredHeight;
- return AIMoveToState::onEnter();
- }
- virtual StateReturnType update()
- {
- Object* obj = getMachineOwner();
- // the normal moveto state will bail when 2d pos matches; we need z, too
- StateReturnType status = AIMoveToState::update();
- const Real THRESH = 3.0f;
- if (status != STATE_CONTINUE && fabs(obj->getPosition()->z - m_destZ) > THRESH)
- status = STATE_CONTINUE;
- return status;
- }
- virtual void onExit( StateExitType status )
- {
- Object* obj = getMachineOwner();
- ChinookAIUpdate* ai = (ChinookAIUpdate*)obj->getAIUpdateInterface();
- Locomotor* loco = ai->getCurLocomotor();
- loco->setPreferredHeight(m_oldPreferredHeight);
- loco->setUltraAccurate(false);
- AIMoveToState::onExit(status);
- }
- };
- EMPTY_DTOR(ChinookMoveToBldgState)
- //-------------------------------------------------------------------------------------------------
- //-------------------------------------------------------------------------------------------------
- //-------------------------------------------------------------------------------------------------
- //-------------------------------------------------------------------------------------------------
- class ChinookAIStateMachine : public AIStateMachine
- {
- MEMORY_POOL_GLUE_WITH_USERLOOKUP_CREATE( ChinookAIStateMachine, "ChinookAIStateMachine" );
- public:
- ChinookAIStateMachine( Object *owner, AsciiString name );
- };
- //-------------------------------------------------------------------------------------------------
- ChinookAIStateMachine::ChinookAIStateMachine(Object *owner, AsciiString name) : AIStateMachine(owner, name)
- {
- defineState( TAKING_OFF, newInstance(ChinookTakeoffOrLandingState)( this, false ), AI_IDLE, AI_IDLE );
- defineState( LANDING, newInstance(ChinookTakeoffOrLandingState)( this, true ), AI_IDLE, AI_IDLE );
- defineState( MOVE_TO_COMBAT_DROP, newInstance(ChinookMoveToBldgState)( this ), DO_COMBAT_DROP, AI_IDLE );
- defineState( DO_COMBAT_DROP, newInstance(ChinookCombatDropState)( this ), AI_IDLE, AI_IDLE );
- defineState( MOVE_TO_AND_LAND, newInstance(AIMoveToState)( this ), LANDING, AI_IDLE );
- defineState( MOVE_TO_AND_EVAC, newInstance(AIMoveToState)( this ), LAND_AND_EVAC, AI_IDLE );
- defineState( LAND_AND_EVAC, newInstance(ChinookTakeoffOrLandingState)( this, true ), EVAC_AND_TAKEOFF, AI_IDLE );
- defineState( EVAC_AND_TAKEOFF, newInstance(ChinookEvacuateState)( this ), TAKING_OFF, AI_IDLE );
- defineState( MOVE_TO_AND_EVAC_AND_EXIT, newInstance(AIMoveToState)( this ), LAND_AND_EVAC_AND_EXIT, AI_IDLE );
- defineState( LAND_AND_EVAC_AND_EXIT, newInstance(ChinookTakeoffOrLandingState)( this, true ), EVAC_AND_EXIT, AI_IDLE );
- defineState( EVAC_AND_EXIT, newInstance(ChinookEvacuateState)( this ), TAKEOFF_AND_EXIT, AI_IDLE );
- defineState( TAKEOFF_AND_EXIT, newInstance(ChinookTakeoffOrLandingState)( this, false ), HEAD_OFF_MAP, AI_IDLE );
- defineState( HEAD_OFF_MAP, newInstance(ChinookHeadOffMapState)( this ), AI_IDLE, AI_IDLE );
- }
- //-------------------------------------------------------------------------------------------------
- ChinookAIStateMachine::~ChinookAIStateMachine()
- {
- }
- //-------------------------------------------------------------------------------------------------
- //-------------------------------------------------------------------------------------------------
- //-------------------------------------------------------------------------------------------------
- //-------------------------------------------------------------------------------------------------
- ChinookAIUpdateModuleData::ChinookAIUpdateModuleData()
- {
- m_numRopes = 4;
- m_ropeWidth = 0.5f;
- m_ropeColor.red = 0.9f;
- m_ropeColor.green = 0.8f;
- m_ropeColor.blue = 0.7f;
- m_perRopeDelayMin = 0x7fffffff;
- m_perRopeDelayMax = 0x7fffffff;
- m_ropeName = "GenericRope";
- m_waitForRopesToDrop = true;
- m_minDropHeight = 30.0f;
- m_ropeFinalHeight = 0.0f;
- m_ropeDropSpeed = 1e10f; // um, fast.
- m_rappelSpeed = fabs(TheGlobalData->m_gravity) * LOGICFRAMES_PER_SECOND * 0.5f;
- m_ropeWobbleLen = 10.0f;
- m_ropeWobbleAmp = 1.0f;
- m_ropeWobbleRate = 0.1f;
- }
- //-------------------------------------------------------------------------------------------------
- /*static*/ void ChinookAIUpdateModuleData::buildFieldParse(MultiIniFieldParse& p)
- {
- SupplyTruckAIUpdateModuleData::buildFieldParse(p);
- static const FieldParse dataFieldParse[] =
- {
- { "RappelSpeed", INI::parseVelocityReal, 0, offsetof(ChinookAIUpdateModuleData, m_rappelSpeed) },
- { "RopeDropSpeed", INI::parseVelocityReal, 0, offsetof(ChinookAIUpdateModuleData, m_ropeDropSpeed) },
- { "RopeName", INI::parseAsciiString, 0, offsetof(ChinookAIUpdateModuleData, m_ropeName) },
- { "RopeFinalHeight", INI::parseReal, 0, offsetof(ChinookAIUpdateModuleData, m_ropeFinalHeight) },
- { "RopeWidth", INI::parseReal, 0, offsetof(ChinookAIUpdateModuleData, m_ropeWidth) },
- { "RopeWobbleLen", INI::parseReal, 0, offsetof(ChinookAIUpdateModuleData, m_ropeWobbleLen) },
- { "RopeWobbleAmplitude", INI::parseReal, 0, offsetof(ChinookAIUpdateModuleData, m_ropeWobbleAmp) },
- { "RopeWobbleRate", INI::parseAngularVelocityReal, 0, offsetof(ChinookAIUpdateModuleData, m_ropeWobbleRate) },
- { "RopeColor", INI::parseRGBColor, 0, offsetof(ChinookAIUpdateModuleData, m_ropeColor) },
- { "NumRopes", INI::parseUnsignedInt, 0, offsetof(ChinookAIUpdateModuleData, m_numRopes) },
- { "PerRopeDelayMin", INI::parseDurationUnsignedInt, 0, offsetof(ChinookAIUpdateModuleData, m_perRopeDelayMin) },
- { "PerRopeDelayMax", INI::parseDurationUnsignedInt, 0, offsetof(ChinookAIUpdateModuleData, m_perRopeDelayMax) },
- { "MinDropHeight", INI::parseReal, 0, offsetof(ChinookAIUpdateModuleData, m_minDropHeight) },
- { "WaitForRopesToDrop", INI::parseBool, 0, offsetof(ChinookAIUpdateModuleData, m_waitForRopesToDrop) },
- { 0, 0, 0, 0 }
- };
- p.add(dataFieldParse);
- }
- //-------------------------------------------------------------------------------------------------
- //-------------------------------------------------------------------------------------------------
- //-------------------------------------------------------------------------------------------------
- //-------------------------------------------------------------------------------------------------
- AIStateMachine* ChinookAIUpdate::makeStateMachine()
- {
- return newInstance(ChinookAIStateMachine)( getObject(), "ChinookAIStateMachine");
- }
- //-------------------------------------------------------------------------------------------------
- ChinookAIUpdate::ChinookAIUpdate( Thing *thing, const ModuleData* moduleData ) : SupplyTruckAIUpdate( thing, moduleData )
- {
- m_hasPendingCommand = false;
- m_flightStatus = CHINOOK_FLYING; // yep, that's right, even if we start "on ground"
- m_airfieldForHealing = INVALID_ID;
- }
- //-------------------------------------------------------------------------------------------------
- ChinookAIUpdate::~ChinookAIUpdate()
- {
- }
- //-------------------------------------------------------------------------------------------------
- static ParkingPlaceBehaviorInterface* getPP(ObjectID id)
- {
- Object* airfield = TheGameLogic->findObjectByID( id );
- if (airfield == NULL || airfield->isEffectivelyDead() || !airfield->isKindOf(KINDOF_AIRFIELD))
- return NULL;
- ParkingPlaceBehaviorInterface* pp = NULL;
- for (BehaviorModule** i = airfield->getBehaviorModules(); *i; ++i)
- {
- if ((pp = (*i)->getParkingPlaceBehaviorInterface()) != NULL)
- break;
- }
- return pp;
- }
- //-------------------------------------------------------------------------------------------------
- void ChinookAIUpdate::setAirfieldForHealing(ObjectID id)
- {
- // make sure we de-register with current one, if any
- if (m_airfieldForHealing != INVALID_ID && m_airfieldForHealing != id)
- {
- ParkingPlaceBehaviorInterface* pp = getPP(m_airfieldForHealing);
- if (pp != NULL)
- {
- pp->setHealee(getObject(), false);
- }
- }
- m_airfieldForHealing = id;
- }
- //-------------------------------------------------------------------------------------------------
- Bool ChinookAIUpdate::isIdle() const
- {
- // we need to do this because we enter an idle state briefly between takeoff/landing in these cases,
- // but scripting relies on us never claiming to be "idle"...
- if (m_hasPendingCommand)
- return false;
- Bool result = SupplyTruckAIUpdate::isIdle();
- if (result && m_flightStatus == CHINOOK_LANDED)
- {
- // ditto: if we are waiting to disgorge some folks, we aren't 'idle'
- ContainModuleInterface* contain = getObject()->getContain();
- if (contain && contain->hasObjectsWantingToEnterOrExit())
- result = false;
- }
- return result;
- }
- //-------------------------------------------------------------------------------------------------
- Bool ChinookAIUpdate::isCurrentlyFerryingSupplies() const
- {
- return SupplyTruckAIUpdate::isCurrentlyFerryingSupplies();
- }
- //-------------------------------------------------------------------------------------------------
- Bool ChinookAIUpdate::isAvailableForSupplying() const
- {
- if (!SupplyTruckAIUpdate::isAvailableForSupplying())
- return false;
- ContainModuleInterface* contain = getObject()->getContain();
- if( !contain || contain->hasObjectsWantingToEnterOrExit() || contain->getContainCount())
- return false;
- return true;
- }
- //-------------------------------------------------------------------------------------------------
- Bool ChinookAIUpdate::isAllowedToAdjustDestination() const
- {
- if (m_flightStatus == CHINOOK_LANDED)
- return false;
-
- return SupplyTruckAIUpdate::isAllowedToAdjustDestination();
- }
- //-------------------------------------------------------------------------------------------------
- ObjectID ChinookAIUpdate::getBuildingToNotPathAround() const
- {
- if (getAIStateType() == MOVE_TO_COMBAT_DROP || getAIStateType() == DO_COMBAT_DROP)
- {
- const Object* goalObj = getStateMachine()->getGoalObject();
- if (goalObj)
- return goalObj->getID();
- }
- return INVALID_ID;
- }
- //-------------------------------------------------------------------------------------------------
- AIFreeToExitType ChinookAIUpdate::getAiFreeToExit(const Object* exiter) const
- {
- if (m_flightStatus == CHINOOK_LANDED
- || (m_flightStatus == CHINOOK_DOING_COMBAT_DROP && exiter->isKindOf(KINDOF_CAN_RAPPEL)))
- return FREE_TO_EXIT;
-
- return WAIT_TO_EXIT;
- }
- //-------------------------------------------------------------------------------------------------
- Bool ChinookAIUpdate::chooseLocomotorSet(LocomotorSetType wst)
- {
- if (m_flightStatus == CHINOOK_LANDED)
- wst = LOCOMOTORSET_TAXIING;
- return SupplyTruckAIUpdate::chooseLocomotorSet(wst);
- }
- //-------------------------------------------------------------------------------------------------
- UpdateSleepTime ChinookAIUpdate::update()
- {
- ParkingPlaceBehaviorInterface* pp = getPP(m_airfieldForHealing);
- if (pp != NULL)
- {
- if (m_flightStatus == CHINOOK_LANDED &&
- !m_hasPendingCommand &&
- getObject()->getBodyModule()->getHealth() == getObject()->getBodyModule()->getMaxHealth())
- {
- // we're completely healed, so take off again
- pp->setHealee(getObject(), false);
- setMyState(TAKING_OFF, NULL, NULL, CMD_FROM_AI);
- }
- else
- {
- pp->setHealee(getObject(), m_flightStatus == CHINOOK_LANDED);
- }
- }
- else
- {
- setAirfieldForHealing(INVALID_ID);
- }
- // have to call our parent's isIdle, because we override it to never return true
- // when we have a pending command...
- if (SupplyTruckAIUpdate::isIdle())
- {
- ContainModuleInterface* contain = getObject()->getContain();
- if( contain )
- {
- Bool waitingToEnterOrExit = contain->hasObjectsWantingToEnterOrExit();
- if (m_hasPendingCommand)
- {
- AICommandParms parms(AICMD_MOVE_TO_POSITION, CMD_FROM_AI); // values don't matter, will be wiped by next line
- m_pendingCommand.reconstitute(parms);
- m_hasPendingCommand = false;
- aiDoCommand(&parms);
- }
- else if (waitingToEnterOrExit && m_flightStatus != CHINOOK_LANDED)
- {
- setMyState(LANDING, NULL, NULL, CMD_FROM_AI);
- }
- else if (!waitingToEnterOrExit && m_flightStatus == CHINOOK_LANDED && m_airfieldForHealing == INVALID_ID)
- {
- setMyState(TAKING_OFF, NULL, NULL, CMD_FROM_AI);
- }
- }
- }
- return SupplyTruckAIUpdate::update();
- }
- //-------------------------------------------------------------------------------------------------
- void ChinookAIUpdate::setMyState( StateID cmd, Object* target, const Coord3D* pos, CommandSourceType cmdSource )
- {
- getStateMachine()->clear();
- getStateMachine()->setGoalObject( target );
- setGoalPositionClipped(pos, cmdSource); // yeah, null is ok here.
- setLastCommandSource( cmdSource );
- getStateMachine()->setState( cmd );
- }
- //----------------------------------------------------------------------------------------
- /**
- * Get repaired at the repair depot
- */
- void ChinookAIUpdate::privateGetRepaired( Object *repairDepot, CommandSourceType cmdSource )
- {
- // we are already landing. just ignore it.
- if (m_flightStatus == CHINOOK_LANDING || m_flightStatus == CHINOOK_LANDED)
- return;
- // sanity, if we can't get repaired from here get out of here
- if( TheActionManager->canGetRepairedAt( getObject(), repairDepot, cmdSource ) == FALSE )
- return;
- setAirfieldForHealing(repairDepot->getID());
- Coord3D pos = *repairDepot->getPosition();
- Coord3D tmp;
- FindPositionOptions options;
- options.maxRadius = repairDepot->getGeometryInfo().getBoundingCircleRadius() * 100.0f;
- if (ThePartitionManager->findPositionAround(&pos, &options, &tmp))
- pos = tmp;
- setMyState(MOVE_TO_AND_LAND, NULL, &pos, cmdSource);
- }
- //-------------------------------------------------------------------------------------------------
- void ChinookAIUpdate::privateCombatDrop( Object* target, const Coord3D& pos, CommandSourceType cmdSource )
- {
- //
- // when there is a target present, we must verify that we can logically do the action when
- // we get commands from players (we'll assume AI knows what its doing)
- //
- if( target != NULL && cmdSource == CMD_FROM_PLAYER &&
- TheActionManager->canEnterObject( getObject(), target, cmdSource, COMBATDROP_INTO ) == FALSE )
- return;
- Coord3D localPos = pos;
- if (target == NULL)
- {
- // if target is null, we are dropping at a pos, not into a bldg.
- // in this case, ensure there is no structure at the pos... this can happen
- // if you combat-drop into a spot in the fog-of-war.
- Coord3D tmp;
- FindPositionOptions options;
- options.maxRadius = getObject()->getGeometryInfo().getBoundingCircleRadius() * 100.0f;
- if (ThePartitionManager->findPositionAround(&localPos, &options, &tmp))
- {
- localPos = tmp;
- }
- }
- // start the combat drop process
- setMyState(MOVE_TO_COMBAT_DROP, target, &localPos, cmdSource);
- }
- //-------------------------------------------------------------------------------------------------
- void ChinookAIUpdate::aiDoCommand(const AICommandParms* parms)
- {
- // this gets reset every time a command is issued.
- setAirfieldForHealing(INVALID_ID);
- if (!isAllowedToRespondToAiCommands(parms))
- return;
- if (m_flightStatus == CHINOOK_TAKING_OFF ||
- m_flightStatus == CHINOOK_LANDING ||
- m_flightStatus == CHINOOK_DOING_COMBAT_DROP)
- {
- // have to wait for takeoff or landing (or rappel) to complete, just store the sucker.
- m_pendingCommand.store(*parms);
- m_hasPendingCommand = true;
- return;
- }
- Bool passItThru = true;
- switch (parms->m_cmd)
- {
- case AICMD_IDLE:
- case AICMD_BUSY:
- case AICMD_FOLLOW_EXITPRODUCTION_PATH:
- {
- // don't need (or want) to take off for these...
- // just pass it thru.
- }
- break;
- case AICMD_MOVE_TO_POSITION_AND_EVACUATE:
- case AICMD_MOVE_TO_POSITION_AND_EVACUATE_AND_EXIT:
- {
- const Real THRESH = 3.0f;
- const Real THRESH_SQR = THRESH*THRESH;
- if (calcDistSqr(*getObject()->getPosition(), parms->m_pos) > THRESH_SQR &&
- m_flightStatus == CHINOOK_LANDED)
- {
- // gotta take off first!
- m_pendingCommand.store(*parms);
- m_hasPendingCommand = true;
- setMyState(TAKING_OFF, NULL, NULL, CMD_FROM_AI);
- passItThru = false;
- }
- else
- {
- // do this INSTEAD of the standard stuff
- setMyState(
- (parms->m_cmd == AICMD_MOVE_TO_POSITION_AND_EVACUATE) ? MOVE_TO_AND_EVAC : MOVE_TO_AND_EVAC_AND_EXIT,
- NULL, &parms->m_pos, CMD_FROM_AI);
- passItThru = false;
- }
- }
- break;
- case AICMD_EXIT:
- case AICMD_EVACUATE:
- {
- if (m_flightStatus != CHINOOK_LANDED)
- {
- // gotta land first!
- m_pendingCommand.store(*parms);
- m_hasPendingCommand = true;
- setMyState(LANDING, NULL, NULL, CMD_FROM_AI);
- passItThru = false;
- }
- }
- break;
- default:
- {
- if (m_flightStatus != CHINOOK_FLYING)
- {
- // gotta take off first!
- m_pendingCommand.store(*parms);
- m_hasPendingCommand = true;
- setMyState(TAKING_OFF, NULL, NULL, CMD_FROM_AI);
- passItThru = false;
- }
- }
- break;
- }
- if (passItThru)
- {
- m_hasPendingCommand = false;
- SupplyTruckAIUpdate::aiDoCommand(parms);
- }
- }
- // ------------------------------------------------------------------------------------------------
- /** CRC */
- // ------------------------------------------------------------------------------------------------
- void ChinookAIUpdate::crc( Xfer *xfer )
- {
- SupplyTruckAIUpdate::crc(xfer);
- } // end crc
- // ------------------------------------------------------------------------------------------------
- /** Xfer method
- * Version Info:
- * 1: Initial version */
- // ------------------------------------------------------------------------------------------------
- void ChinookAIUpdate::xfer( Xfer *xfer )
- {
- // version
- XferVersion currentVersion = 1;
- XferVersion version = currentVersion;
- xfer->xferVersion( &version, currentVersion );
- SupplyTruckAIUpdate::xfer(xfer);
- xfer->xferBool(&m_hasPendingCommand);
- if (m_hasPendingCommand) {
- m_pendingCommand.doXfer(xfer);
- }
- xfer->xferUser(&m_flightStatus, sizeof(m_flightStatus));
- xfer->xferObjectID(&m_airfieldForHealing);
- // extend base class
- } // end xfer
- // ------------------------------------------------------------------------------------------------
- /** Load post process */
- // ------------------------------------------------------------------------------------------------
- void ChinookAIUpdate::loadPostProcess( void )
- {
- SupplyTruckAIUpdate::loadPostProcess();
- } // end loadPostProcess
|