| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629 |
- /*
- ** Command & Conquer Generals Zero Hour(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. //
- // //
- ////////////////////////////////////////////////////////////////////////////////
- // JetAIUpdate.cpp //////////
- #include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
- #define DEFINE_LOCOMOTORSET_NAMES
- #include "Common/ActionManager.h"
- #include "Common/GlobalData.h"
- #include "Common/MiscAudio.h"
- #include "Common/ThingFactory.h"
- #include "Common/ThingTemplate.h"
- #include "GameClient/Drawable.h"
- #include "GameClient/GameClient.h"
- #include "GameLogic/ExperienceTracker.h"
- #include "GameLogic/Locomotor.h"
- #include "GameLogic/Module/BodyModule.h"
- #include "GameLogic/Module/CountermeasuresBehavior.h"
- #include "GameLogic/Module/JetAIUpdate.h"
- #include "GameLogic/Module/ParkingPlaceBehavior.h"
- #include "GameLogic/Module/PhysicsUpdate.h"
- #include "GameLogic/Object.h"
- #include "GameLogic/AIPathfind.h"
- #include "GameLogic/PartitionManager.h"
- #include "GameLogic/Weapon.h"
- const Real BIGNUM = 99999.0f;
- #ifdef _INTERNAL
- // for occasional debugging...
- //#pragma optimize("", off)
- //#pragma MESSAGE("************************************** WARNING, optimization disabled for debugging purposes")
- #endif
- //-------------------------------------------------------------------------------------------------
- enum TaxiType
- {
- FROM_HANGAR,
- FROM_PARKING,
- TO_PARKING
- };
- //-------------------------------------------------------------------------------------------------
- enum JetAIStateType
- {
- // note that these must be distinct (numerically) from AIStateType. ick.
- JETAISTATETYPE_FIRST = 1000,
- TAXI_FROM_HANGAR,
- TAKING_OFF_AWAIT_CLEARANCE,
- TAXI_TO_TAKEOFF,
- PAUSE_BEFORE_TAKEOFF,
- TAKING_OFF,
- LANDING_AWAIT_CLEARANCE,
- LANDING,
- TAXI_FROM_LANDING,
- ORIENT_FOR_PARKING_PLACE,
- RELOAD_AMMO,
- RETURNING_FOR_LANDING,
- RETURN_TO_DEAD_AIRFIELD,
- CIRCLING_DEAD_AIRFIELD,
- JETAISTATETYPE_LAST
- };
- //-------------------------------------------------------------------------------------------------
- Bool JetAIUpdate::getFlag( FlagType f ) const
- {
- return (m_flags & (1<<f)) != 0;
- }
- //-------------------------------------------------------------------------------------------------
- void JetAIUpdate::setFlag( FlagType f, Bool v)
- {
- if (v)
- m_flags |= (1<<f);
- else
- m_flags &= ~(1<<f);
- }
- //-------------------------------------------------------------------------------------------------
- Bool JetAIUpdate::isOutOfSpecialReloadAmmo() const
- {
- const Object* jet = getObject();
- // if we have at least one special reload weapon,
- // AND all such weapons are out of ammo,
- // return true.
- Int specials = 0;
- Int out = 0;
- for( Int i = 0; i < WEAPONSLOT_COUNT; i++ )
- {
- const Weapon* weapon = jet->getWeaponInWeaponSlot((WeaponSlotType)i);
- if (weapon == NULL || weapon->getReloadType() != RETURN_TO_BASE_TO_RELOAD)
- continue;
- ++specials;
- if (weapon->getStatus() == OUT_OF_AMMO)
- ++out;
- }
- return specials > 0 && out == specials;
- }
- //-------------------------------------------------------------------------------------------------
- static ParkingPlaceBehaviorInterface* getPP(ObjectID id, Object** airfieldPP = NULL)
- {
- if (airfieldPP)
- *airfieldPP = NULL;
- Object* airfield = TheGameLogic->findObjectByID( id );
- if (airfield == NULL || airfield->isEffectivelyDead() || !airfield->isKindOf(KINDOF_FS_AIRFIELD) || airfield->testStatus(OBJECT_STATUS_SOLD))
- return NULL;
- if (airfieldPP)
- *airfieldPP = airfield;
- ParkingPlaceBehaviorInterface* pp = NULL;
- for (BehaviorModule** i = airfield->getBehaviorModules(); *i; ++i)
- {
- if ((pp = (*i)->getParkingPlaceBehaviorInterface()) != NULL)
- break;
- }
- return pp;
- }
- //-------------------------------------------------------------------------------------------------
- class PartitionFilterHasParkingPlace : public PartitionFilter
- {
- private:
- ObjectID m_id;
- public:
- PartitionFilterHasParkingPlace(ObjectID id) : m_id(id) { }
- protected:
- #if defined(_DEBUG) || defined(_INTERNAL)
- virtual const char* debugGetName() { return "PartitionFilterHasParkingPlace"; }
- #endif
- virtual Bool allow(Object *objOther)
- {
- ParkingPlaceBehaviorInterface* pp = getPP(objOther->getID());
- if (pp != NULL && pp->reserveSpace(m_id, 0.0f, NULL))
- return true;
- return false;
- }
- };
- //-------------------------------------------------------------------------------------------------
- static Object* findSuitableAirfield(Object* jet)
- {
- PartitionFilterAcceptByKindOf filterKind(MAKE_KINDOF_MASK(KINDOF_FS_AIRFIELD), KINDOFMASK_NONE);
- PartitionFilterRejectByObjectStatus filterStatus( MAKE_OBJECT_STATUS_MASK( OBJECT_STATUS_UNDER_CONSTRUCTION ), OBJECT_STATUS_MASK_NONE );
- PartitionFilterRejectByObjectStatus filterStatusTwo( MAKE_OBJECT_STATUS_MASK( OBJECT_STATUS_SOLD ), OBJECT_STATUS_MASK_NONE ); // Independent to make it an OR
- PartitionFilterRelationship filterTeam(jet, PartitionFilterRelationship::ALLOW_ALLIES);
- PartitionFilterAlive filterAlive;
- PartitionFilterSameMapStatus filterMapStatus(jet);
- PartitionFilterHasParkingPlace filterPP(jet->getID());
- PartitionFilter *filters[16];
- Int numFilters = 0;
- filters[numFilters++] = &filterKind;
- filters[numFilters++] = &filterStatus;
- filters[numFilters++] = &filterStatusTwo;
- filters[numFilters++] = &filterTeam;
- filters[numFilters++] = &filterAlive;
- filters[numFilters++] = &filterPP;
- filters[numFilters++] = &filterMapStatus;
- filters[numFilters] = NULL;
- return ThePartitionManager->getClosestObject( jet, HUGE_DIST, FROM_CENTER_2D, filters );
- }
- //-------------------------------------------------------------------------------------------------
- //-------------------------------------------------------------------------------------------------
- //-------------------------------------------------------------------------------------------------
- //-------------------------------------------------------------------------------------------------
- /*
- Success: we have runway clearance
- Failure: no runway clearance
- */
- class JetAwaitingRunwayState : public State
- {
- MEMORY_POOL_GLUE_WITH_USERLOOKUP_CREATE(JetAwaitingRunwayState, "JetAwaitingRunwayState")
- protected:
- // snapshot interface STUBBED.
- virtual void crc( Xfer *xfer ){};
- virtual void xfer( Xfer *xfer ){XferVersion cv = 1; XferVersion v = cv; xfer->xferVersion( &v, cv );}
- virtual void loadPostProcess(){};
- private:
- const Bool m_landing;
- public:
- JetAwaitingRunwayState( StateMachine *machine, Bool landing ) : m_landing(landing), State( machine, "JetAwaitingRunwayState") { }
- virtual StateReturnType onEnter()
- {
- Object* jet = getMachineOwner();
- JetAIUpdate* jetAI = (JetAIUpdate*)jet->getAIUpdateInterface();
- if( !jetAI )
- return STATE_FAILURE;
- jetAI->friend_setTakeoffInProgress(!m_landing);
- jetAI->friend_setLandingInProgress(m_landing);
- jetAI->friend_setAllowCircling(true);
- return STATE_CONTINUE;
- }
- virtual StateReturnType update()
- {
- Object* jet = getMachineOwner();
- if (jet->isEffectivelyDead())
- return STATE_FAILURE;
- JetAIUpdate* jetAI = (JetAIUpdate*)jet->getAIUpdateInterface();
- if( !jetAI )
- return STATE_FAILURE;
- ParkingPlaceBehaviorInterface* pp = getPP(jet->getProducerID());
- if (pp == NULL)
- {
- // no producer? just skip this step.
- return STATE_SUCCESS;
- }
- // gotta reserve a space in order to reserve a runway
- if (!pp->reserveSpace(jet->getID(), jetAI->friend_getParkingOffset(), NULL))
- {
- DEBUG_ASSERTCRASH(m_landing, ("hmm, this should never happen for taking-off things"));
- return STATE_FAILURE;
- }
-
- if (pp->reserveRunway(jet->getID(), m_landing))
- {
- return STATE_SUCCESS;
- }
- else if( jet->testStatus( OBJECT_STATUS_DECK_HEIGHT_OFFSET ) && !m_landing )
- {
- //If we're trying to take off an aircraft carrier and fail to reserve a
- //runway, it's because we need to be at the front of the carrier queue.
- //Therefore, we need to move forward whenever possible until we are in
- //the front.
- Coord3D bestPos;
- if( pp->calcBestParkingAssignment( jet->getID(), &bestPos ) )
- {
- jetAI->friend_setTaxiInProgress(true);
- jetAI->friend_setAllowAirLoco(false);
- jetAI->chooseLocomotorSet(LOCOMOTORSET_TAXIING);
-
- jetAI->destroyPath();
- Path *movePath;
- movePath = newInstance(Path);
- Coord3D pos = *jet->getPosition();
- movePath->prependNode( &pos, LAYER_GROUND );
- movePath->markOptimized();
- movePath->appendNode( &bestPos, LAYER_GROUND );
- TheAI->pathfinder()->setDebugPath(movePath);
- jetAI->friend_setPath( movePath );
- DEBUG_ASSERTCRASH(jetAI->getCurLocomotor(), ("no loco"));
- jetAI->getCurLocomotor()->setUsePreciseZPos(true);
- jetAI->getCurLocomotor()->setUltraAccurate(true);
- jetAI->getCurLocomotor()->setAllowInvalidPosition(true);
- jetAI->ignoreObstacleID(jet->getProducerID());
- }
- }
- // can't get a runway? gotta wait.
- jetAI->setLocomotorGoalNone();
- return STATE_CONTINUE;
- }
- virtual void onExit(StateExitType status)
- {
- Object* jet = getMachineOwner();
- JetAIUpdate* jetAI = (JetAIUpdate*)jet->getAIUpdateInterface();
- if (jetAI)
- {
- jetAI->friend_setTakeoffInProgress(false);
- jetAI->friend_setLandingInProgress(false);
- jetAI->friend_setAllowCircling(false);
- }
- }
- };
- EMPTY_DTOR(JetAwaitingRunwayState)
- //-------------------------------------------------------------------------------------------------
- /*
- Success: a new suitable airfield has appeared
- Failure: shouldn't normally happen
- */
- class JetOrHeliCirclingDeadAirfieldState : public State
- {
- MEMORY_POOL_GLUE_WITH_USERLOOKUP_CREATE(JetOrHeliCirclingDeadAirfieldState, "JetOrHeliCirclingDeadAirfieldState")
- protected:
- // snapshot interface STUBBED.
- // The state will check immediately after a load game, but I think that's ok. jba.
- virtual void crc( Xfer *xfer ){};
- virtual void xfer( Xfer *xfer ){XferVersion cv = 1; XferVersion v = cv; xfer->xferVersion( &v, cv );}
- virtual void loadPostProcess(){};
- private:
- Int m_checkAirfield;
- enum
- {
- // only recheck for new airfields every second or so
- HOW_OFTEN_TO_CHECK = LOGICFRAMES_PER_SECOND
- };
- public:
- JetOrHeliCirclingDeadAirfieldState( StateMachine *machine ) :
- State( machine, "JetOrHeliCirclingDeadAirfieldState"),
- m_checkAirfield(0) { }
- virtual StateReturnType onEnter()
- {
- Object* jet = getMachineOwner();
- JetAIUpdate* jetAI = (JetAIUpdate*)jet->getAIUpdateInterface();
- if( !jetAI )
- {
- return STATE_FAILURE;
- }
-
- // obscure case: if the jet wasn't spawned, but just placed directly on the map,
- // it might not have an owning airfield, and it might be trying to return
- // simply due to being idle, not out of ammo. so check and don't die in that
- // case, but just punt back out to idle.
- if (!jetAI->isOutOfSpecialReloadAmmo() && jet->getProducerID() == INVALID_ID)
- {
- return STATE_FAILURE;
- }
- // just stay where we are.
- jetAI->setLocomotorGoalNone();
- m_checkAirfield = HOW_OFTEN_TO_CHECK;
- //Play the "low fuel" voice whenever the craft is circling above the airfield.
- AudioEventRTS soundToPlay = *jet->getTemplate()->getPerUnitSound( "VoiceLowFuel" );
- soundToPlay.setObjectID( jet->getID() );
- TheAudio->addAudioEvent( &soundToPlay );
- return STATE_CONTINUE;
- }
- virtual StateReturnType update()
- {
- Object* jet = getMachineOwner();
- JetAIUpdate* jetAI = (JetAIUpdate*)jet->getAIUpdateInterface();
- if( !jetAI )
- {
- return STATE_FAILURE;
- }
- // just stay where we are.
- jetAI->setLocomotorGoalNone();
-
- Real damageRate = jetAI->friend_getOutOfAmmoDamagePerSecond();
- if (damageRate > 0)
- {
- // convert to damage/sec to damage/frame
- damageRate *= SECONDS_PER_LOGICFRAME_REAL;
- // since it's a percentage, multiply times the max health
- damageRate *= jet->getBodyModule()->getMaxHealth();
- DamageInfo damageInfo;
- damageInfo.in.m_damageType = DAMAGE_UNRESISTABLE;
- damageInfo.in.m_deathType = DEATH_NORMAL;
- damageInfo.in.m_sourceID = INVALID_ID;
- damageInfo.in.m_amount = damageRate;
- jet->attemptDamage( &damageInfo );
- }
- if (--m_checkAirfield <= 0)
- {
- m_checkAirfield = HOW_OFTEN_TO_CHECK;
- Object* airfield = findSuitableAirfield( jet );
- if (airfield)
- {
- jet->setProducer(airfield);
- return STATE_SUCCESS;
- }
- }
- return STATE_CONTINUE;
- }
- };
- EMPTY_DTOR(JetOrHeliCirclingDeadAirfieldState)
- //-------------------------------------------------------------------------------------------------
- /*
- Success: we returned to the dead-airfield location
- Failure: shouldn't normally happen
- */
- class JetOrHeliReturningToDeadAirfieldState : public AIInternalMoveToState
- {
- MEMORY_POOL_GLUE_WITH_USERLOOKUP_CREATE(JetOrHeliReturningToDeadAirfieldState, "JetOrHeliReturningToDeadAirfieldState")
- public:
- JetOrHeliReturningToDeadAirfieldState( StateMachine *machine ) : AIInternalMoveToState( machine, "JetOrHeliReturningToDeadAirfieldState") { }
- virtual StateReturnType onEnter()
- {
- Object* jet = getMachineOwner();
- JetAIUpdate* jetAI = (JetAIUpdate*)jet->getAIUpdateInterface();
- if( !jetAI )
- {
- return STATE_FAILURE;
- }
- setAdjustsDestination(true);
- m_goalPosition = *jetAI->friend_getProducerLocation();
- return AIInternalMoveToState::onEnter();
- }
- };
- EMPTY_DTOR(JetOrHeliReturningToDeadAirfieldState)
- //-------------------------------------------------------------------------------------------------
- // This solution uses the
- // http://www.faqs.org/faqs/graphics/algorithms-faq/
- // Subject 1.03
- static Bool intersectInfiniteLine2D
- (
- Real ax, Real ay, Real ao,
- Real cx, Real cy, Real co,
- Real& ix, Real& iy
- )
- {
- Real bx = ax + Cos(ao);
- Real by = ay + Sin(ao);
- Real dx = cx + Cos(co);
- Real dy = cy + Sin(co);
- Real denom = ((bx - ax) * (dy - cy) - (by - ay) * (dx - cx));
- if (denom == 0.0f)
- {
- // the lines are parallel.
- return false;
- }
- // The lines intersect.
- Real r = ((ay - cy) * (dx - cx) - (ax - cx) * (dy - cy) ) / denom;
- ix = ax + r * (bx - ax);
- iy = ay + r * (by - ay);
- return true;
- }
- //-------------------------------------------------------------------------------------------------
- /*
- Success: we are on the ground at the runway start
- Failure: we are unable to get on the ground
- */
- class JetOrHeliTaxiState : public AIMoveOutOfTheWayState
- {
- MEMORY_POOL_GLUE_WITH_USERLOOKUP_CREATE(JetOrHeliTaxiState, "JetOrHeliTaxiState")
- private:
- TaxiType m_taxiMode;
- public:
- JetOrHeliTaxiState( StateMachine *machine, TaxiType m ) : m_taxiMode(m), AIMoveOutOfTheWayState( machine ) { }
- virtual StateReturnType onEnter()
- {
- Object* jet = getMachineOwner();
- JetAIUpdate* jetAI = (JetAIUpdate*)jet->getAIUpdateInterface();
- if( !jetAI )
- return STATE_FAILURE;
-
- jetAI->setCanPathThroughUnits(true);
- jetAI->friend_setTakeoffInProgress(m_taxiMode != TO_PARKING);
- jetAI->friend_setLandingInProgress(m_taxiMode == TO_PARKING);
- jetAI->friend_setTaxiInProgress(true);
-
- if( m_taxiMode == TO_PARKING )
- {
- //Instantly reload flares.
- CountermeasuresBehaviorInterface *cbi = jet->getCountermeasuresBehaviorInterface();
- if( cbi )
- {
- cbi->reloadCountermeasures();
- }
- }
- jetAI->friend_setAllowAirLoco(false);
- jetAI->chooseLocomotorSet(LOCOMOTORSET_TAXIING);
- DEBUG_ASSERTCRASH(jetAI->getCurLocomotor(), ("no loco"));
- Object* airfield;
- ParkingPlaceBehaviorInterface* pp = getPP(jet->getProducerID(), &airfield);
- if (pp == NULL)
- return STATE_SUCCESS; // no airfield? just skip this step.
-
- ParkingPlaceBehaviorInterface::PPInfo ppinfo;
- if (!pp->reserveSpace(jet->getID(), jetAI->friend_getParkingOffset(), &ppinfo))
- return STATE_FAILURE; // full?
-
- Coord3D intermedPt;
- Bool intermed = false;
- Real orient = atan2(ppinfo.runwayPrep.y - ppinfo.parkingSpace.y, ppinfo.runwayPrep.x - ppinfo.parkingSpace.x);
- if (fabs(stdAngleDiff(orient, ppinfo.parkingOrientation)) > PI/128)
- {
- intermedPt.z = (ppinfo.parkingSpace.z + ppinfo.runwayPrep.z) * 0.5f;
- intermed = intersectInfiniteLine2D(
- ppinfo.parkingSpace.x, ppinfo.parkingSpace.y, ppinfo.parkingOrientation,
- ppinfo.runwayPrep.x, ppinfo.runwayPrep.y, ppinfo.parkingOrientation + PI/2,
- intermedPt.x, intermedPt.y);
- }
- jetAI->destroyPath();
- Path *movePath;
- movePath = newInstance(Path);
- Coord3D pos = *jet->getPosition();
- movePath->prependNode( &pos, LAYER_GROUND );
- movePath->markOptimized();
- if (m_taxiMode == TO_PARKING)
- {
- if( jet->testStatus( OBJECT_STATUS_DECK_HEIGHT_OFFSET ) )
- {
- //We're on an aircraft carrier.
- const std::vector<Coord3D> *pTaxiLocations = pp->getTaxiLocations( jet->getID() );
- if( pTaxiLocations )
- {
- std::vector<Coord3D>::const_iterator it;
- for( it = pTaxiLocations->begin(); it != pTaxiLocations->end(); it++ )
- {
- movePath->appendNode( it, LAYER_GROUND );
- }
- }
- //We just landed... see if we can get a better space forward so we don't stop and pause
- //at our initially assigned spot.
- Coord3D pos;
- pp->calcBestParkingAssignment( jet->getID(), &pos );
- movePath->appendNode( &pos, LAYER_GROUND );
- }
- else
- {
- //We're on a normal airfield
- movePath->appendNode( &ppinfo.runwayPrep, LAYER_GROUND );
- if (intermed)
- movePath->appendNode( &intermedPt, LAYER_GROUND );
- movePath->appendNode( &ppinfo.parkingSpace, LAYER_GROUND );
- }
- }
- else if (m_taxiMode == FROM_PARKING)
- {
- if( jet->testStatus( OBJECT_STATUS_DECK_HEIGHT_OFFSET ) )
- {
- if( !(ppinfo.runwayStart == ppinfo.runwayPrep) )
- {
- movePath->appendNode( &ppinfo.runwayStart, LAYER_GROUND );
- }
- }
- else
- {
- if (intermed)
- movePath->appendNode( &intermedPt, LAYER_GROUND );
- movePath->appendNode( &ppinfo.runwayPrep, LAYER_GROUND );
- movePath->appendNode( &ppinfo.runwayStart, LAYER_GROUND );
- }
- }
- else if (m_taxiMode == FROM_HANGAR)
- {
- if( jet->testStatus( OBJECT_STATUS_DECK_HEIGHT_OFFSET ) )
- {
- //Aircraft carrier
- if( jet->testStatus( OBJECT_STATUS_REASSIGN_PARKING ) )
- {
- //This status means we are being reassigned a parking space. We're not actually moving from the
- //hangar. So simply move to the new parking spot which was just switched from under us in
- //FlightDeckBehavior::update()
- jet->clearStatus( MAKE_OBJECT_STATUS_MASK( OBJECT_STATUS_REASSIGN_PARKING ) );
- movePath->appendNode( &ppinfo.runwayPrep, LAYER_GROUND );
- }
- else
- {
- const std::vector<Coord3D> *pCreationLocations = pp->getCreationLocations( jet->getID() );
- if( !pCreationLocations )
- {
- DEBUG_CRASH( ("No creation locations specified for runway for JetAIBehavior -- taxiing from hanger (Kris).") );
- return STATE_FAILURE;
- }
- std::vector<Coord3D>::const_iterator it;
- Bool firstNode = TRUE;
- for( it = pCreationLocations->begin(); it != pCreationLocations->end(); it++ )
- {
- if( firstNode )
- {
- //Skip the first node because it's the creation location.
- firstNode = FALSE;
- continue;
- }
- movePath->appendNode( it, LAYER_GROUND );
- }
- movePath->appendNode( &ppinfo.runwayPrep, LAYER_GROUND );
- }
- }
- else
- {
- //Airfield
- movePath->appendNode( &ppinfo.parkingSpace, LAYER_GROUND );
- }
- }
- m_waitingForPath = FALSE;
- TheAI->pathfinder()->setDebugPath(movePath);
- setAdjustsDestination(false); // precision is necessary
- jetAI->friend_setPath( movePath );
- DEBUG_ASSERTCRASH(jetAI->getCurLocomotor(), ("no loco"));
- jetAI->getCurLocomotor()->setUsePreciseZPos(true);
- jetAI->getCurLocomotor()->setUltraAccurate(true);
- jetAI->getCurLocomotor()->setAllowInvalidPosition(true);
- jetAI->ignoreObstacleID(jet->getProducerID());
- StateReturnType ret = AIMoveOutOfTheWayState::onEnter();
- return ret;
- }
- virtual StateReturnType update()
- {
- Object* jet = getMachineOwner();
- if (jet->isEffectivelyDead())
- return STATE_FAILURE;
- JetAIUpdate* jetAI = (JetAIUpdate*)jet->getAIUpdateInterface();
- if( !jetAI )
- return STATE_FAILURE;
- if( m_taxiMode == TO_PARKING || m_taxiMode == FROM_HANGAR )
- {
- //Keep checking to see if there is a better spot as it moves forward. If we find a better spot, then
- //append the position to our move.
- ParkingPlaceBehaviorInterface* pp = getPP(jet->getProducerID());
- Coord3D bestPos;
- Int oldIndex, newIndex;
- // Check pp for null, as it is possible for your airfield to get destroyed while taxiing.jba [8/27/2003]
- if( pp!=NULL && pp->calcBestParkingAssignment( jet->getID(), &bestPos, &oldIndex, &newIndex ) )
- {
- Path *path = jetAI->friend_getPath();
- if( path )
- {
- path->appendNode( &bestPos, LAYER_GROUND );
- }
- }
- }
- return AIMoveOutOfTheWayState::update();
- }
- virtual void onExit( StateExitType status )
- {
- Object* jet = getMachineOwner();
- JetAIUpdate* jetAI = (JetAIUpdate*)jet->getAIUpdateInterface();
- if (jetAI)
- {
- jetAI->getCurLocomotor()->setUsePreciseZPos(false);
- jetAI->getCurLocomotor()->setUltraAccurate(false);
- jetAI->getCurLocomotor()->setAllowInvalidPosition(false);
- jetAI->friend_setTakeoffInProgress(false);
- jetAI->friend_setLandingInProgress(false);
- jetAI->friend_setTaxiInProgress(false);
- jetAI->setCanPathThroughUnits(false);
- }
- AIMoveOutOfTheWayState::onExit(status);
- }
- };
- EMPTY_DTOR(JetOrHeliTaxiState)
- //-------------------------------------------------------------------------------------------------
- /*
- Success: we are on the ground at the runway start
- Failure: we are unable to get on the ground
- */
- class JetTakeoffOrLandingState : public AIFollowPathState
- {
- MEMORY_POOL_GLUE_WITH_USERLOOKUP_CREATE(JetTakeoffOrLandingState, "JetTakeoffOrLandingState")
- private:
- Real m_maxLift;
- Real m_maxSpeed;
- #ifdef CIRCLE_FOR_LANDING
- Coord3D m_circleForLandingPos;
- #endif
- Bool m_landing;
- Bool m_landingSoundPlayed;
- public:
- JetTakeoffOrLandingState( StateMachine *machine, Bool landing ) : m_landing(landing), AIFollowPathState( machine, "JetTakeoffOrLandingState" ) { }
- virtual StateReturnType onEnter()
- {
- Object* jet = getMachineOwner();
- JetAIUpdate* jetAI = (JetAIUpdate*)jet->getAIUpdateInterface();
- if (!jetAI)
- return STATE_FAILURE;
- if (jet->isEffectivelyDead())
- return STATE_FAILURE;
- jetAI->friend_setTakeoffInProgress(!m_landing);
- jetAI->friend_setLandingInProgress(m_landing);
- jetAI->friend_setAllowAirLoco(true);
- jetAI->chooseLocomotorSet(LOCOMOTORSET_NORMAL);
- Locomotor* loco = jetAI->getCurLocomotor();
- DEBUG_ASSERTCRASH(loco, ("no loco"));
- loco->setMaxLift(BIGNUM);
- BodyDamageType bdt = jet->getBodyModule()->getDamageState();
- m_maxLift = loco->getMaxLift(bdt);
- m_maxSpeed = loco->getMaxSpeedForCondition(bdt);
- m_landingSoundPlayed = FALSE;
- if (m_landing)
- {
- loco->setMaxSpeed(loco->getMinSpeed());
- }
- else
- {
- loco->setMaxLift(0);
- }
- loco->setUsePreciseZPos(true);
- loco->setUltraAccurate(true);
- jetAI->ignoreObstacleID(jet->getProducerID());
- ParkingPlaceBehaviorInterface* pp = getPP(jet->getProducerID());
- if (pp == NULL)
- return STATE_SUCCESS; // no airfield? just skip this step
-
- ParkingPlaceBehaviorInterface::PPInfo ppinfo;
- if (!pp->reserveSpace(jet->getID(), jetAI->friend_getParkingOffset(), &ppinfo))
- {
- // it's full.
- return STATE_FAILURE;
- }
-
- // only check this for landing; we might have already given up the reservation to the guy behind us for takeoff
- if (m_landing)
- {
- if (!pp->reserveRunway(jet->getID(), m_landing))
- {
- DEBUG_CRASH(("we should never get to this state unless we have a runway available"));
- return STATE_FAILURE;
- }
- }
-
- std::vector<Coord3D> path;
- if (m_landing)
- {
- #ifdef CIRCLE_FOR_LANDING
- m_circleForLandingPos = ppinfo.runwayApproach;
- m_circleForLandingPos.z = (ppinfo.runwayEnd.z + ppinfo.runwayApproach.z)*0.5f;
- #else
- path.push_back(ppinfo.runwayApproach);
- #endif
- if( jet->testStatus( OBJECT_STATUS_DECK_HEIGHT_OFFSET ) )
- {
- //Assigned to an aircraft carrier which has separate landing strips.
- path.push_back( ppinfo.runwayLandingStart );
- path.push_back( ppinfo.runwayLandingEnd );
- }
- else
- {
- //Assigned to an airstrip -- land the same way we took off but in reverse.
- path.push_back(ppinfo.runwayEnd);
- path.push_back(ppinfo.runwayStart);
- }
- }
- else
- {
- ppinfo.runwayEnd.z = ppinfo.runwayApproach.z;
- path.push_back(ppinfo.runwayEnd);
- path.push_back(ppinfo.runwayExit);
- }
- setAdjustsDestination(false); // precision is necessary
- setAdjustFinalDestination(false); // especially at the endpoint!
- jetAI->friend_setGoalPath( &path );
- StateReturnType ret = AIFollowPathState::onEnter();
-
- return ret;
- }
- virtual StateReturnType update()
- {
- Object* jet = getMachineOwner();
- if (jet->isEffectivelyDead())
- return STATE_FAILURE;
- JetAIUpdate* jetAI = (JetAIUpdate*)jet->getAIUpdateInterface();
- if( !jetAI )
- return STATE_FAILURE;
-
- if (m_landing)
- {
- #ifdef CIRCLE_FOR_LANDING
- if (jet->getPosition()->z > m_circleForLandingPos.z)
- {
- const Real THRESH = 4.0f;
- jetAI->getCurLocomotor()->setAltitudeChangeThresholdForCircling(THRESH);
- jetAI->setLocomotorGoalPositionExplicit(m_circleForLandingPos);
- return STATE_CONTINUE;
- }
- else
- #endif
- {
- jetAI->getCurLocomotor()->setMaxLift(BIGNUM);
- #ifdef CIRCLE_FOR_LANDING
- jetAI->getCurLocomotor()->setAltitudeChangeThresholdForCircling(0);
- #endif
- }
- if( !m_landingSoundPlayed )
- {
- ParkingPlaceBehaviorInterface* pp = getPP(jet->getProducerID());
- Real zPos = jet->getPosition()->z;
- Real zSlop = 0.25f;
- PathfindLayerEnum layer = TheTerrainLogic->getHighestLayerForDestination( jet->getPosition() );
- Real groundZ = TheTerrainLogic->getLayerHeight( jet->getPosition()->x, jet->getPosition()->y, layer );
- if( pp )
- {
- groundZ += pp->getLandingDeckHeightOffset();
- }
-
- if( zPos - zSlop <= groundZ )
- {
- m_landingSoundPlayed = TRUE;
- AudioEventRTS soundToPlay = TheAudio->getMiscAudio()->m_aircraftWheelScreech;
- soundToPlay.setPosition( jet->getPosition() );
- TheAudio->addAudioEvent( &soundToPlay );
- }
- }
- }
- else
- {
- ParkingPlaceBehaviorInterface* pp = getPP(jet->getProducerID());
- if (pp)
- pp->transferRunwayReservationToNextInLineForTakeoff(jet->getID());
- //Calculate the distance of the jet from the end of the runway as a ratio from the start.
- //As it approaches the end of the runway, the plane will gain more lift, even if it's already
- //going quickly. Using speed for lift is bad in the case of the aircraft carrier, because
- //we don't want it to take off quickly.
- ParkingPlaceBehaviorInterface::PPInfo ppinfo;
- pp->calcPPInfo( jet->getID(), &ppinfo );
- Coord3D vector = ppinfo.runwayEnd;
- vector.sub( jet->getPosition() );
- Real dist = vector.length();
-
- Real ratio = 1.0f - (dist / ppinfo.runwayTakeoffDist);
- ratio *= ratio; //dampen it....
- if (ratio < 0.0f) ratio = 0.0f;
- if (ratio > 1.0f) ratio = 1.0f;
- jetAI->getCurLocomotor()->setMaxLift(m_maxLift * ratio);
- }
- StateReturnType ret = AIFollowPathState::update();
- return ret;
- }
- virtual void onExit( StateExitType status )
- {
- AIFollowPathState::onExit(status);
- // just in case.
- Object* jet = getMachineOwner();
- JetAIUpdate* jetAI = (JetAIUpdate*)jet->getAIUpdateInterface();
- if( !jetAI )
- return;
- jetAI->friend_setTakeoffInProgress(false);
- jetAI->friend_setLandingInProgress(false);
- jetAI->friend_enableAfterburners(false);
- // 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 = jetAI->getCurLocomotor();
- if (loco)
- {
- loco->setUsePreciseZPos(false);
- loco->setUltraAccurate(false);
- // don't restore lift if dead -- this may fight with JetSlowDeathBehavior!
- if (!jet->isEffectivelyDead())
- loco->setMaxLift(BIGNUM);
- #ifdef CIRCLE_FOR_LANDING
- loco->setAltitudeChangeThresholdForCircling(0);
- #endif
- }
- jetAI->ignoreObstacleID(INVALID_ID);
- ParkingPlaceBehaviorInterface* pp = getPP(jet->getProducerID());
- if (!m_landing)
- {
- if (pp && !jetAI->friend_keepsParkingSpaceWhenAirborne())
- pp->releaseSpace(jet->getID());
- }
- if (pp)
- pp->releaseRunway(jet->getID());
- }
- };
- EMPTY_DTOR(JetTakeoffOrLandingState)
- //-------------------------------------------------------------------------------------------------
- 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);
- }
- //-------------------------------------------------------------------------------------------------
- /*
- Success: we are on the ground at the runway start
- Failure: we are unable to get on the ground
- */
- class HeliTakeoffOrLandingState : public State
- {
- MEMORY_POOL_GLUE_WITH_USERLOOKUP_CREATE(HeliTakeoffOrLandingState, "HeliTakeoffOrLandingState")
- protected:
- // snapshot interface
- virtual void crc( Xfer *xfer )
- {
- // empty. jba.
- }
- virtual void xfer( Xfer *xfer )
- {
- // version
- XferVersion currentVersion = 1;
- XferVersion version = currentVersion;
- xfer->xferVersion( &version, currentVersion );
- // set on create. xfer->xferBool(&m_landing);
- xfer->xferCoord3D(&m_path[0]);
- xfer->xferCoord3D(&m_path[1]);
- xfer->xferInt(&m_index);
- xfer->xferCoord3D(&m_parkingLoc);
- xfer->xferReal(&m_parkingOrientation);
- }
- virtual void loadPostProcess()
- {
- // empty. jba.
- }
- private:
- Coord3D m_path[2];
- Int m_index;
- Coord3D m_parkingLoc;
- Real m_parkingOrientation;
- Bool m_landing;
- public:
- HeliTakeoffOrLandingState( StateMachine *machine, Bool landing ) : m_landing(landing),
- State( machine, "HeliTakeoffOrLandingState" ), m_index(0)
- {
- m_parkingLoc.zero();
- }
- virtual StateReturnType onEnter()
- {
- Object* jet = getMachineOwner();
- JetAIUpdate* jetAI = (JetAIUpdate*)jet->getAIUpdateInterface();
- if( !jetAI )
- return STATE_FAILURE;
- jetAI->friend_setTakeoffInProgress(!m_landing);
- jetAI->friend_setLandingInProgress(m_landing);
- jetAI->friend_setAllowAirLoco(true);
- jetAI->chooseLocomotorSet(LOCOMOTORSET_NORMAL);
- Locomotor* loco = jetAI->getCurLocomotor();
- DEBUG_ASSERTCRASH(loco, ("no loco"));
- loco->setUsePreciseZPos(true);
- loco->setUltraAccurate(true);
- jetAI->ignoreObstacleID(jet->getProducerID());
- Object* airfield;
- ParkingPlaceBehaviorInterface* pp = getPP(jet->getProducerID(), &airfield);
- if (pp == NULL)
- return STATE_SUCCESS; // no airfield? just skip this step
-
- Coord3D landingApproach;
- if (jet->isKindOf(KINDOF_PRODUCED_AT_HELIPAD))
- {
- if (m_landing)
- {
- m_parkingLoc = jetAI->friend_getLandingPosForHelipadStuff();
- m_parkingOrientation = jet->getOrientation();
- }
- else
- {
- m_parkingOrientation = jet->getOrientation();
- m_parkingLoc = *jet->getPosition();
- }
- landingApproach = m_parkingLoc;
- landingApproach.z += pp->getApproachHeight() + pp->getLandingDeckHeightOffset();
- }
- else
- {
- ParkingPlaceBehaviorInterface::PPInfo ppinfo;
- if (!pp->reserveSpace(jet->getID(), jetAI->friend_getParkingOffset(), &ppinfo))
- return STATE_FAILURE;
- m_parkingLoc = ppinfo.parkingSpace;
- m_parkingOrientation = ppinfo.parkingOrientation;
- landingApproach = m_parkingLoc;
- landingApproach.z += (ppinfo.runwayApproach.z - ppinfo.runwayEnd.z);
- }
-
- if (m_landing)
- {
- m_path[0] = landingApproach;
- m_path[1] = m_parkingLoc;
- }
- else
- {
- m_path[0] = m_parkingLoc;
- m_path[1] = landingApproach;
- m_path[1].z = landingApproach.z;
- }
- m_index = 0;
- return STATE_CONTINUE;
- }
- virtual StateReturnType update()
- {
- Object* jet = getMachineOwner();
- if (jet->isEffectivelyDead())
- return STATE_FAILURE;
- JetAIUpdate* jetAI = (JetAIUpdate*)jet->getAIUpdateInterface();
- if( !jetAI )
- return STATE_FAILURE;
- // I have disabled this because it is no longer necessary and is a bit funky lookin' (srj)
- #ifdef NOT_IN_USE
- // magically position it correctly.
- jet->getPhysics()->scrubVelocity2D(0);
- Coord3D hoverloc = m_path[m_index];
- hoverloc.z = jet->getPosition()->z;
- #if 1
- Coord3D pos = *jet->getPosition();
- Real dx = hoverloc.x - pos.x;
- Real dy = hoverloc.y - pos.y;
- Real dSqr = dx*dx+dy*dy;
- const Real DARN_CLOSE = 0.25f;
- if (dSqr < DARN_CLOSE)
- {
- jet->setPosition(&hoverloc);
- }
- else
- {
- Real dist = sqrtf(dSqr);
- if (dist<1) dist = 1;
- pos.x += PATHFIND_CELL_SIZE_F*dx/(dist*LOGICFRAMES_PER_SECOND);
- pos.y += PATHFIND_CELL_SIZE_F*dy/(dist*LOGICFRAMES_PER_SECOND);
- jet->setPosition(&pos);
- }
- #else
- jet->setPosition(&hoverloc);
- #endif
- jet->setOrientation(m_parkingOrientation);
- #endif
- if (jet->isKindOf(KINDOF_PRODUCED_AT_HELIPAD) || !m_landing)
- {
- TheAI->pathfinder()->adjustDestination(jet, jetAI->getLocomotorSet(), &m_path[m_index]);
- TheAI->pathfinder()->updateGoal(jet, &m_path[m_index], LAYER_GROUND);
- }
- jetAI->setLocomotorGoalPositionExplicit(m_path[m_index]);
- const Real THRESH = 3.0f;
- const Real THRESH_SQR = THRESH*THRESH;
- const Coord3D* a = jet->getPosition();
- const Coord3D* b = &m_path[m_index];
- Real distSqr = calcDistSqr(*a, *b);
- if (distSqr <= THRESH_SQR)
- ++m_index;
-
- if (m_index >= 2)
- return STATE_SUCCESS;
- return STATE_CONTINUE;
- }
- virtual void onExit( StateExitType status )
- {
- // just in case.
- Object* jet = getMachineOwner();
- JetAIUpdate* jetAI = (JetAIUpdate*)jet->getAIUpdateInterface();
- if( !jetAI )
- return;
- jetAI->friend_setTakeoffInProgress(false);
- jetAI->friend_setLandingInProgress(false);
- // 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 = jetAI->getCurLocomotor();
- if (loco)
- {
- loco->setUsePreciseZPos(false);
- loco->setUltraAccurate(false);
- // don't restore lift if dead -- this may fight with JetSlowDeathBehavior!
- if (!jet->isEffectivelyDead())
- loco->setMaxLift(BIGNUM);
- }
- jetAI->ignoreObstacleID(INVALID_ID);
- ParkingPlaceBehaviorInterface* pp = getPP(jet->getProducerID());
- if (m_landing)
- {
- jetAI->friend_setAllowAirLoco(false);
- jetAI->chooseLocomotorSet(LOCOMOTORSET_TAXIING);
- }
- else
- {
- if (pp && !jetAI->friend_keepsParkingSpaceWhenAirborne())
- pp->releaseSpace(jet->getID());
- }
- }
- };
- EMPTY_DTOR(HeliTakeoffOrLandingState)
- //-------------------------------------------------------------------------------------------------
- class JetOrHeliParkOrientState : public State
- {
- MEMORY_POOL_GLUE_WITH_USERLOOKUP_CREATE(JetOrHeliParkOrientState, "JetOrHeliParkOrientState")
- protected:
- // snapshot interface STUBBED.
- virtual void crc( Xfer *xfer ){};
- virtual void xfer( Xfer *xfer ){XferVersion cv = 1; XferVersion v = cv; xfer->xferVersion( &v, cv );}
- virtual void loadPostProcess(){};
- public:
- JetOrHeliParkOrientState( StateMachine *machine ) : State( machine, "JetOrHeliParkOrientState") { }
- virtual StateReturnType onEnter()
- {
- Object* jet = getMachineOwner();
- JetAIUpdate* jetAI = (JetAIUpdate*)jet->getAIUpdateInterface();
- if( !jetAI )
- return STATE_FAILURE;
- if (jet->isKindOf(KINDOF_PRODUCED_AT_HELIPAD))
- {
- return STATE_SUCCESS;
- }
- jetAI->friend_setTakeoffInProgress(false);
- jetAI->friend_setLandingInProgress(true);
- jetAI->ignoreObstacleID(jet->getProducerID());
- return STATE_CONTINUE;
- }
- virtual StateReturnType update()
- {
- Object* jet = getMachineOwner();
- if (jet->isEffectivelyDead())
- return STATE_FAILURE;
- JetAIUpdate* jetAI = (JetAIUpdate*)jet->getAIUpdateInterface();
- if( !jetAI )
- {
- return STATE_FAILURE;
- }
- ParkingPlaceBehaviorInterface* pp = getPP(jet->getProducerID());
- if (pp == NULL)
- return STATE_FAILURE;
-
- ParkingPlaceBehaviorInterface::PPInfo ppinfo;
- if (!pp->reserveSpace(jet->getID(), jetAI->friend_getParkingOffset(), &ppinfo))
- return STATE_FAILURE;
- const Real THRESH = 0.001f;
- if (fabs(stdAngleDiff(jet->getOrientation(), ppinfo.parkingOrientation)) <= THRESH)
- return STATE_SUCCESS;
- // magically position it correctly.
- jet->getPhysics()->scrubVelocity2D(0);
- Coord3D hoverloc = ppinfo.parkingSpace;
- if( jet->testStatus( OBJECT_STATUS_DECK_HEIGHT_OFFSET ) )
- {
- hoverloc = ppinfo.runwayPrep;
- }
- hoverloc.z = jet->getPosition()->z;
- jet->setPosition(&hoverloc);
- jetAI->setLocomotorGoalOrientation(ppinfo.parkingOrientation);
- return STATE_CONTINUE;
- }
- virtual void onExit( StateExitType status )
- {
- Object* jet = getMachineOwner();
- JetAIUpdate* jetAI = (JetAIUpdate*)jet->getAIUpdateInterface();
- if( !jetAI )
- return;
- jetAI->friend_setTakeoffInProgress(false);
- jetAI->friend_setLandingInProgress(false);
- jetAI->ignoreObstacleID(INVALID_ID);
- }
- };
- EMPTY_DTOR(JetOrHeliParkOrientState)
- //-------------------------------------------------------------------------------------------------
- class JetPauseBeforeTakeoffState : public AIFaceState
- {
- MEMORY_POOL_GLUE_WITH_USERLOOKUP_CREATE(JetPauseBeforeTakeoffState, "JetPauseBeforeTakeoffState")
- protected:
- // snapshot interface
- virtual void crc( Xfer *xfer )
- {
- // empty. jba.
- }
- virtual void xfer( Xfer *xfer )
- {
- // version
- XferVersion currentVersion = 1;
- XferVersion version = currentVersion;
- xfer->xferVersion( &version, currentVersion );
- // set on create. xfer->xferBool(&m_landing);
- xfer->xferUnsignedInt(&m_when);
- xfer->xferUnsignedInt(&m_whenTransfer);
- xfer->xferBool(&m_afterburners);
- xfer->xferBool(&m_resetTimer);
- xfer->xferObjectID(&m_waitedForTaxiID);
- }
- virtual void loadPostProcess()
- {
- // empty. jba.
- }
- private:
- UnsignedInt m_when;
- UnsignedInt m_whenTransfer;
- ObjectID m_waitedForTaxiID;
- Bool m_resetTimer;
- Bool m_afterburners;
- Bool findWaiter()
- {
- Object* jet = getMachineOwner();
- ParkingPlaceBehaviorInterface* pp = getPP(getMachineOwner()->getProducerID());
- if (pp)
- {
- Int count = pp->getRunwayCount();
- for (Int i = 0; i < count; ++i)
- {
- Object* otherJet = TheGameLogic->findObjectByID( pp->getRunwayReservation( i, RESERVATION_TAKEOFF ) );
- if (otherJet == NULL || otherJet == jet)
- continue;
- AIUpdateInterface* ai = otherJet->getAIUpdateInterface();
- if (ai == NULL)
- continue;
- if (ai->getCurrentStateID() == TAXI_TO_TAKEOFF)
- {
- if (m_waitedForTaxiID == INVALID_ID)
- {
- m_waitedForTaxiID = otherJet->getID();
- }
- return true;
- }
- }
- }
- return false;
- }
- public:
- JetPauseBeforeTakeoffState( StateMachine *machine ) :
- AIFaceState(machine, false),
- m_when(0),
- m_whenTransfer(0),
- m_waitedForTaxiID(INVALID_ID),
- m_resetTimer(false),
- m_afterburners(false)
- {
- // nothing
- }
- virtual StateReturnType onEnter()
- {
- Object* jet = getMachineOwner();
- JetAIUpdate* jetAI = (JetAIUpdate*)jet->getAIUpdateInterface();
- if( !jetAI )
- return STATE_FAILURE;
- jetAI->friend_setTakeoffInProgress(true);
- jetAI->friend_setLandingInProgress(false);
- m_when = 0;
- m_whenTransfer = 0;
- m_waitedForTaxiID = INVALID_ID;
- m_resetTimer = false;
- m_afterburners = false;
- ParkingPlaceBehaviorInterface* pp = getPP(jet->getProducerID());
- if (pp == NULL)
- return STATE_SUCCESS; // no airfield? just skip this step.
-
- ParkingPlaceBehaviorInterface::PPInfo ppinfo;
- if (!pp->reserveSpace(jet->getID(), jetAI->friend_getParkingOffset(), &ppinfo))
- return STATE_SUCCESS; // full?
- getMachine()->setGoalPosition(&ppinfo.runwayEnd);
-
- return AIFaceState::onEnter();
- }
- virtual StateReturnType update()
- {
- Object* jet = getMachineOwner();
- JetAIUpdate* jetAI = (JetAIUpdate*)jet->getAIUpdateInterface();
- if (jet->isEffectivelyDead())
- return STATE_FAILURE;
- // always call this.
- StateReturnType superStatus = AIFaceState::update();
-
- if (findWaiter())
- return STATE_CONTINUE;
- UnsignedInt now = TheGameLogic->getFrame();
- if (!m_resetTimer)
- {
- // we had to wait, but now everyone else is ready, so restart our countdown.
- m_when = now + jetAI->friend_getTakeoffPause();
- if (m_waitedForTaxiID == INVALID_ID)
- {
- m_waitedForTaxiID = jet->getID(); // just so we don't pick up anyone else
- m_whenTransfer = now + 1;
- }
- else
- {
- m_whenTransfer = now + 2; // 2 seems odd, but is correct
- }
- m_resetTimer = true;
- }
- if (!m_afterburners)
- {
- jetAI->friend_enableAfterburners(true);
- m_afterburners = true;
- }
- DEBUG_ASSERTCRASH(m_when != 0, ("hmm"));
- DEBUG_ASSERTCRASH(m_whenTransfer != 0, ("hmm"));
- // once we start the final wait, release the runways for guys behind us, so they can start taxiing
- ParkingPlaceBehaviorInterface* pp = getPP(jet->getProducerID());
- if (pp && now >= m_whenTransfer)
- {
- pp->transferRunwayReservationToNextInLineForTakeoff(jet->getID());
- }
- if (now >= m_when)
- return superStatus;
- return STATE_CONTINUE;
- }
- virtual void onExit(StateExitType status)
- {
- Object* jet = getMachineOwner();
- JetAIUpdate* jetAI = (JetAIUpdate*)jet->getAIUpdateInterface();
- jetAI->friend_setTakeoffInProgress(false);
- jetAI->friend_setLandingInProgress(false);
- AIFaceState::onExit(status);
- }
- };
- EMPTY_DTOR(JetPauseBeforeTakeoffState)
- //-------------------------------------------------------------------------------------------------
- class JetOrHeliReloadAmmoState : public State
- {
- MEMORY_POOL_GLUE_WITH_USERLOOKUP_CREATE(JetOrHeliReloadAmmoState, "JetOrHeliReloadAmmoState")
- private:
- UnsignedInt m_reloadTime;
- UnsignedInt m_reloadDoneFrame;
- protected:
- // snapshot interface
- virtual void crc( Xfer *xfer )
- {
- // empty. jba.
- }
- virtual void xfer( Xfer *xfer )
- {
- // version
- XferVersion currentVersion = 1;
- XferVersion version = currentVersion;
- xfer->xferVersion( &version, currentVersion );
- // set on create. xfer->xferBool(&m_landing);
- xfer->xferUnsignedInt(&m_reloadTime);
- xfer->xferUnsignedInt(&m_reloadDoneFrame);
- }
- virtual void loadPostProcess()
- {
- // empty. jba.
- }
- public:
- JetOrHeliReloadAmmoState( StateMachine *machine ) : State( machine, "JetOrHeliReloadAmmoState") { }
- virtual StateReturnType onEnter()
- {
- Object* jet = getMachineOwner();
- JetAIUpdate* jetAI = (JetAIUpdate*)jet->getAIUpdateInterface();
- if( !jetAI )
- return STATE_FAILURE;
- jetAI->friend_setTakeoffInProgress(false);
- jetAI->friend_setLandingInProgress(false);
- jetAI->friend_setUseSpecialReturnLoco(false);
-
- m_reloadTime = 0;
- for (Int i = 0; i < WEAPONSLOT_COUNT; ++i)
- {
- const Weapon* w = jet->getWeaponInWeaponSlot((WeaponSlotType)i);
- if (w == NULL)
- continue;
-
- Int remaining = w->getRemainingAmmo();
- Int clipSize = w->getClipSize();
- Int rt = w->getClipReloadTime(jet);
- if (clipSize > 0)
- {
- // bias by amount empty.
- Int needed = clipSize - remaining;
- rt = (rt * needed) / clipSize;
- }
- if (rt > m_reloadTime)
- m_reloadTime = rt;
- }
-
- if (m_reloadTime < 1)
- m_reloadTime = 1;
- m_reloadDoneFrame = m_reloadTime + TheGameLogic->getFrame();
- return STATE_CONTINUE;
- }
- virtual StateReturnType update()
- {
- Object* jet = getMachineOwner();
- UnsignedInt now = TheGameLogic->getFrame();
- Bool allDone = true;
- for (Int i = 0; i < WEAPONSLOT_COUNT; ++i)
- {
- Weapon* w = jet->getWeaponInWeaponSlot((WeaponSlotType)i);
- if (w == NULL)
- continue;
-
- if (now >= m_reloadDoneFrame)
- w->setClipPercentFull(1.0f, false);
- else
- w->setClipPercentFull((Real)(m_reloadTime - (m_reloadDoneFrame - now)) / m_reloadTime, false);
- if (w->getRemainingAmmo() != w->getClipSize())
- allDone = false;
- }
- if (allDone)
- return STATE_SUCCESS;
- return STATE_CONTINUE;
- }
- virtual void onExit(StateExitType status)
- {
- Object* jet = getMachineOwner();
- JetAIUpdate* jetAI = (JetAIUpdate*)jet->getAIUpdateInterface();
- jetAI->friend_setTakeoffInProgress(false);
- jetAI->friend_setLandingInProgress(false);
- }
- };
- EMPTY_DTOR(JetOrHeliReloadAmmoState)
- //-------------------------------------------------------------------------------------------------
- /*
- Success: we are close enough to a friendly airfield to land
- Failure: we are unable to get close enough to a friendly airfield to land
- */
- class JetOrHeliReturnForLandingState : public AIInternalMoveToState
- {
- MEMORY_POOL_GLUE_WITH_USERLOOKUP_CREATE(JetOrHeliReturnForLandingState, "JetOrHeliReturnForLandingState")
- public:
- JetOrHeliReturnForLandingState( StateMachine *machine ) : AIInternalMoveToState( machine, "JetOrHeliReturnForLandingState") { }
- virtual StateReturnType onEnter()
- {
- Object* jet = getMachineOwner();
- JetAIUpdate* jetAI = (JetAIUpdate*)jet->getAIUpdateInterface();
- ParkingPlaceBehaviorInterface* pp = getPP(jet->getProducerID());
- if (pp == NULL)
- {
- // nuke the producer id, since it's dead
- jet->setProducer(NULL);
- Object* airfield = findSuitableAirfield( jet );
- pp = airfield ? getPP(airfield->getID()) : NULL;
- if (airfield && pp)
- {
- jet->setProducer(airfield);
- }
- else
- {
- return STATE_FAILURE;
- }
- }
-
- if (jet->isKindOf(KINDOF_PRODUCED_AT_HELIPAD))
- {
- m_goalPosition = jetAI->friend_getLandingPosForHelipadStuff();
- }
- else
- {
- ParkingPlaceBehaviorInterface::PPInfo ppinfo;
- if (!pp->reserveSpace(jet->getID(), jetAI->friend_getParkingOffset(), &ppinfo))
- return STATE_FAILURE;
- m_goalPosition = jetAI->friend_needsRunway() ? ppinfo.runwayApproach : ppinfo.parkingSpace;
- }
- setAdjustsDestination(false); // precision is necessary
- return AIInternalMoveToState::onEnter();
- }
- };
- EMPTY_DTOR(JetOrHeliReturnForLandingState)
- //-------------------------------------------------------------------------------------------------
- //-------------------------------------------------------------------------------------------------
- //-------------------------------------------------------------------------------------------------
- //-------------------------------------------------------------------------------------------------
- class JetAIStateMachine : public AIStateMachine
- {
- MEMORY_POOL_GLUE_WITH_USERLOOKUP_CREATE( JetAIStateMachine, "JetAIStateMachine" );
- public:
- JetAIStateMachine( Object *owner, AsciiString name );
- };
- //-------------------------------------------------------------------------------------------------
- JetAIStateMachine::JetAIStateMachine(Object *owner, AsciiString name) : AIStateMachine(owner, name)
- {
- defineState( RETURNING_FOR_LANDING, newInstance(JetOrHeliReturnForLandingState)( this ), LANDING_AWAIT_CLEARANCE, RETURN_TO_DEAD_AIRFIELD );
- defineState( TAKING_OFF_AWAIT_CLEARANCE, newInstance(JetAwaitingRunwayState)( this, false ), TAXI_TO_TAKEOFF, AI_IDLE );
- defineState( TAXI_TO_TAKEOFF, newInstance(JetOrHeliTaxiState)( this, FROM_PARKING ), PAUSE_BEFORE_TAKEOFF, AI_IDLE );
- defineState( PAUSE_BEFORE_TAKEOFF, newInstance(JetPauseBeforeTakeoffState)( this ), TAKING_OFF, AI_IDLE );
- defineState( TAKING_OFF, newInstance(JetTakeoffOrLandingState)( this, false ), AI_IDLE, AI_IDLE );
- defineState( LANDING_AWAIT_CLEARANCE, newInstance(JetAwaitingRunwayState)( this, true ), LANDING, AI_IDLE );
- defineState( LANDING, newInstance(JetTakeoffOrLandingState)( this, true ), TAXI_FROM_LANDING, AI_IDLE );
- defineState( TAXI_FROM_LANDING, newInstance(JetOrHeliTaxiState)( this, TO_PARKING ), ORIENT_FOR_PARKING_PLACE, AI_IDLE );
- defineState( TAXI_FROM_HANGAR, newInstance(JetOrHeliTaxiState)( this, FROM_HANGAR ), ORIENT_FOR_PARKING_PLACE, AI_IDLE );
- defineState( ORIENT_FOR_PARKING_PLACE, newInstance(JetOrHeliParkOrientState)( this ), RELOAD_AMMO, AI_IDLE );
- defineState( RELOAD_AMMO, newInstance(JetOrHeliReloadAmmoState)( this ), AI_IDLE, AI_IDLE );
- defineState( RETURN_TO_DEAD_AIRFIELD, newInstance(JetOrHeliReturningToDeadAirfieldState)( this ), CIRCLING_DEAD_AIRFIELD, RETURN_TO_DEAD_AIRFIELD );
- defineState( CIRCLING_DEAD_AIRFIELD, newInstance(JetOrHeliCirclingDeadAirfieldState)( this ), AI_IDLE, AI_IDLE );
- }
- //-------------------------------------------------------------------------------------------------
- JetAIStateMachine::~JetAIStateMachine()
- {
- }
- //-------------------------------------------------------------------------------------------------
- //-------------------------------------------------------------------------------------------------
- //-------------------------------------------------------------------------------------------------
- //-------------------------------------------------------------------------------------------------
- class HeliAIStateMachine : public AIStateMachine
- {
- MEMORY_POOL_GLUE_WITH_USERLOOKUP_CREATE( HeliAIStateMachine, "HeliAIStateMachine" );
- public:
- HeliAIStateMachine( Object *owner, AsciiString name );
- };
- //-------------------------------------------------------------------------------------------------
- HeliAIStateMachine::HeliAIStateMachine(Object *owner, AsciiString name) : AIStateMachine(owner, name)
- {
- defineState( RETURNING_FOR_LANDING, newInstance(JetOrHeliReturnForLandingState)( this ), LANDING_AWAIT_CLEARANCE, RETURN_TO_DEAD_AIRFIELD );
- defineState( TAKING_OFF_AWAIT_CLEARANCE, newInstance(SuccessState)( this ), TAKING_OFF, AI_IDLE );
- defineState( TAKING_OFF, newInstance(HeliTakeoffOrLandingState)( this, false ), AI_IDLE, AI_IDLE );
- defineState( LANDING_AWAIT_CLEARANCE, newInstance(SuccessState)( this ), ORIENT_FOR_PARKING_PLACE, AI_IDLE );
- defineState( ORIENT_FOR_PARKING_PLACE, newInstance(JetOrHeliParkOrientState)( this ), LANDING, AI_IDLE );
- defineState( LANDING, newInstance(HeliTakeoffOrLandingState)( this, true ), RELOAD_AMMO, AI_IDLE );
- defineState( RELOAD_AMMO, newInstance(JetOrHeliReloadAmmoState)( this ), AI_IDLE, AI_IDLE );
- defineState( RETURN_TO_DEAD_AIRFIELD, newInstance(JetOrHeliReturningToDeadAirfieldState)( this ), CIRCLING_DEAD_AIRFIELD, RETURN_TO_DEAD_AIRFIELD );
- defineState( CIRCLING_DEAD_AIRFIELD, newInstance(JetOrHeliCirclingDeadAirfieldState)( this ), AI_IDLE, AI_IDLE );
- defineState( TAXI_FROM_HANGAR, newInstance(JetOrHeliTaxiState)( this, FROM_HANGAR ), AI_IDLE, AI_IDLE );
- }
- //-------------------------------------------------------------------------------------------------
- HeliAIStateMachine::~HeliAIStateMachine()
- {
- }
- //-------------------------------------------------------------------------------------------------
- //-------------------------------------------------------------------------------------------------
- //-------------------------------------------------------------------------------------------------
- //-------------------------------------------------------------------------------------------------
- JetAIUpdateModuleData::JetAIUpdateModuleData()
- {
- m_outOfAmmoDamagePerSecond = 0;
- m_needsRunway = true;
- m_keepsParkingSpaceWhenAirborne = true;
- m_takeoffDistForMaxLift = 0.0f;
- m_minHeight = 0.0f;
- m_parkingOffset = 0.0f;
- m_sneakyOffsetWhenAttacking = 0.0f;
- m_takeoffPause = 0;
- m_attackingLoco = LOCOMOTORSET_NORMAL;
- m_returningLoco = LOCOMOTORSET_NORMAL;
- m_attackLocoPersistTime = 0;
- m_attackersMissPersistTime = 0;
- m_lockonTime = 0;
- m_lockonCursor.clear();
- m_lockonInitialDist = 100;
- m_lockonFreq = 0.5;
- m_lockonAngleSpin = 720;
- m_returnToBaseIdleTime = 0;
- }
- //-------------------------------------------------------------------------------------------------
- /*static*/ void JetAIUpdateModuleData::buildFieldParse(MultiIniFieldParse& p)
- {
- AIUpdateModuleData::buildFieldParse(p);
- static const FieldParse dataFieldParse[] =
- {
- { "OutOfAmmoDamagePerSecond", INI::parsePercentToReal, NULL, offsetof( JetAIUpdateModuleData, m_outOfAmmoDamagePerSecond ) },
- { "NeedsRunway", INI::parseBool, NULL, offsetof( JetAIUpdateModuleData, m_needsRunway ) },
- { "KeepsParkingSpaceWhenAirborne",INI::parseBool, NULL, offsetof( JetAIUpdateModuleData, m_keepsParkingSpaceWhenAirborne ) },
- { "TakeoffDistForMaxLift", INI::parsePercentToReal, NULL, offsetof( JetAIUpdateModuleData, m_takeoffDistForMaxLift ) },
- { "TakeoffPause", INI::parseDurationUnsignedInt, NULL, offsetof( JetAIUpdateModuleData, m_takeoffPause ) },
- { "MinHeight", INI::parseReal, NULL, offsetof( JetAIUpdateModuleData, m_minHeight ) },
- { "ParkingOffset", INI::parseReal, NULL, offsetof( JetAIUpdateModuleData, m_parkingOffset ) },
- { "SneakyOffsetWhenAttacking", INI::parseReal, NULL, offsetof( JetAIUpdateModuleData, m_sneakyOffsetWhenAttacking ) },
- { "AttackLocomotorType", INI::parseIndexList, TheLocomotorSetNames, offsetof( JetAIUpdateModuleData, m_attackingLoco ) },
- { "AttackLocomotorPersistTime", INI::parseDurationUnsignedInt, NULL, offsetof( JetAIUpdateModuleData, m_attackLocoPersistTime ) },
- { "AttackersMissPersistTime", INI::parseDurationUnsignedInt, NULL, offsetof( JetAIUpdateModuleData, m_attackersMissPersistTime ) },
- { "ReturnForAmmoLocomotorType", INI::parseIndexList, TheLocomotorSetNames, offsetof( JetAIUpdateModuleData, m_returningLoco ) },
- { "LockonTime", INI::parseDurationUnsignedInt, NULL, offsetof( JetAIUpdateModuleData, m_lockonTime ) },
- { "LockonCursor", INI::parseAsciiString, NULL, offsetof( JetAIUpdateModuleData, m_lockonCursor ) },
- { "LockonInitialDist", INI::parseReal, NULL, offsetof( JetAIUpdateModuleData, m_lockonInitialDist ) },
- { "LockonFreq", INI::parseReal, NULL, offsetof( JetAIUpdateModuleData, m_lockonFreq ) },
- { "LockonAngleSpin", INI::parseAngleReal, NULL, offsetof( JetAIUpdateModuleData, m_lockonAngleSpin ) },
- { "LockonBlinky", INI::parseBool, NULL, offsetof( JetAIUpdateModuleData, m_lockonBlinky ) },
- { "ReturnToBaseIdleTime", INI::parseDurationUnsignedInt, NULL, offsetof( JetAIUpdateModuleData, m_returnToBaseIdleTime ) },
- { 0, 0, 0, 0 }
- };
- p.add(dataFieldParse);
- }
- //-------------------------------------------------------------------------------------------------
- //-------------------------------------------------------------------------------------------------
- //-------------------------------------------------------------------------------------------------
- //-------------------------------------------------------------------------------------------------
- AIStateMachine* JetAIUpdate::makeStateMachine()
- {
- if (getJetAIUpdateModuleData()->m_needsRunway)
- return newInstance(JetAIStateMachine)( getObject(), "JetAIStateMachine");
- else
- return newInstance(HeliAIStateMachine)( getObject(), "HeliAIStateMachine");
- }
- //-------------------------------------------------------------------------------------------------
- JetAIUpdate::JetAIUpdate( Thing *thing, const ModuleData* moduleData ) : AIUpdateInterface( thing, moduleData )
- {
- m_flags = 0;
- m_afterburnerSound = *(getObject()->getTemplate()->getPerUnitSound("Afterburner"));
- m_afterburnerSound.setObjectID(getObject()->getID());
- m_attackLocoExpireFrame = 0;
- m_attackersMissExpireFrame = 0;
- m_untargetableExpireFrame = 0;
- m_returnToBaseFrame = 0;
- m_lockonDrawable = NULL;
- m_landingPosForHelipadStuff.zero();
- //Added By Sadullah Nader
- //Initializations missing and needed
- m_producerLocation.zero();
- //
- m_enginesOn = TRUE;
- }
- //-------------------------------------------------------------------------------------------------
- JetAIUpdate::~JetAIUpdate()
- {
- if (m_lockonDrawable)
- {
- TheGameClient->destroyDrawable(m_lockonDrawable);
- m_lockonDrawable = NULL;
- }
- }
- //-------------------------------------------------------------------------------------------------
- Bool JetAIUpdate::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 (getFlag(HAS_PENDING_COMMAND))
- return false;
- return AIUpdateInterface::isIdle();
- }
- //-------------------------------------------------------------------------------------------------
- Bool JetAIUpdate::isReloading() const
- {
- StateID stateID = getStateMachine()->getCurrentStateID();
- if( stateID == RELOAD_AMMO )
- {
- return TRUE;
- }
- return FALSE;
- }
- //-------------------------------------------------------------------------------------------------
- Bool JetAIUpdate::isTaxiingToParking() const
- {
- StateID stateID = getStateMachine()->getCurrentStateID();
- switch( stateID )
- {
- case TAXI_FROM_HANGAR:
- case TAXI_FROM_LANDING:
- case ORIENT_FOR_PARKING_PLACE:
- case RELOAD_AMMO:
- case TAKING_OFF_AWAIT_CLEARANCE:
- case TAXI_TO_TAKEOFF:
- case PAUSE_BEFORE_TAKEOFF:
- case TAKING_OFF:
- return TRUE;
- }
- return FALSE;
- }
- //-------------------------------------------------------------------------------------------------
- void JetAIUpdate::onObjectCreated()
- {
- AIUpdateInterface::onObjectCreated();
- friend_setAllowAirLoco(false);
- chooseLocomotorSet(LOCOMOTORSET_TAXIING);
- }
- //-------------------------------------------------------------------------------------------------
- void JetAIUpdate::onDelete()
- {
- AIUpdateInterface::onDelete();
- ParkingPlaceBehaviorInterface* pp = getPP(getObject()->getProducerID());
- if (pp)
- pp->releaseSpace(getObject()->getID());
- }
- //-------------------------------------------------------------------------------------------------
- void JetAIUpdate::getProducerLocation()
- {
- if (getFlag(HAS_PRODUCER_LOCATION))
- return;
- Object* jet = getObject();
- Object* airfield = TheGameLogic->findObjectByID( jet->getProducerID() );
- if (airfield == NULL)
- m_producerLocation = *jet->getPosition();
- else
- m_producerLocation = *airfield->getPosition();
- /*
- if we aren't allowed to fly, then we should be parked (or at least taxiing),
- which implies we have a parking place reserved. If we don't, it's probably
- because we were directly spawned via script (or directly placed on the map).
- So, check to see if we have no parking place, and if not, quietly enable flight.
- */
- ParkingPlaceBehaviorInterface* pp = getPP(jet->getProducerID());
- if (!pp || !pp->hasReservedSpace(jet->getID()))
- {
- friend_setAllowAirLoco(true);
- chooseLocomotorSet(LOCOMOTORSET_NORMAL);
- }
- else
- {
- friend_setAllowAirLoco(false);
- chooseLocomotorSet(LOCOMOTORSET_TAXIING);
- }
- setFlag(HAS_PRODUCER_LOCATION, true);
- }
- //-------------------------------------------------------------------------------------------------
- UpdateSleepTime JetAIUpdate::update()
- {
- const JetAIUpdateModuleData* d = getJetAIUpdateModuleData();
- getProducerLocation();
- Object* jet = getObject();
- ParkingPlaceBehaviorInterface* pp = getPP(getObject()->getProducerID());
- // If idle & out of ammo, return
- // have to call our parent's isIdle, because we override it to never return true
- // when we have a pending command...
- UnsignedInt now = TheGameLogic->getFrame();
- // srj sez: not 100% sure on this. calling RELOAD_AMMO "idle" allows us to get healed while reloading,
- // but might have other side effects we didn't want. if this does prove to cause a bug, be sure
- // that jets (and ESPECIALLY comanches) are still getting healed at airfields.
- if (AIUpdateInterface::isIdle() || getStateMachine()->getCurrentStateID() == RELOAD_AMMO)
- {
- if (pp != NULL)
- {
- if (!getFlag(ALLOW_AIR_LOCO) &&
- !getFlag(HAS_PENDING_COMMAND) &&
- jet->isKindOf(KINDOF_PRODUCED_AT_HELIPAD) &&
- jet->getBodyModule()->getHealth() == jet->getBodyModule()->getMaxHealth())
- {
- // we're completely healed, so take off again
- pp->setHealee(jet, false);
- friend_setAllowAirLoco(true);
- getStateMachine()->clear();
- setLastCommandSource( CMD_FROM_AI );
- getStateMachine()->setState( TAKING_OFF_AWAIT_CLEARANCE );
- }
- else
- {
- pp->setHealee(jet, !getFlag(ALLOW_AIR_LOCO));
- }
- }
- // note that we might still have weapons with ammo, but still be forced to return to reload.
- if (isOutOfSpecialReloadAmmo() && getFlag(ALLOW_AIR_LOCO))
- {
- m_returnToBaseFrame = 0;
- // this is really a "just-in-case" to ensure the targeter list doesn't spin out of control (srj)
- pruneDeadTargeters();
- setFlag(USE_SPECIAL_RETURN_LOCO, true);
- setLastCommandSource( CMD_FROM_AI );
- getStateMachine()->setState(RETURNING_FOR_LANDING);
- }
- else if (getFlag(HAS_PENDING_COMMAND)
- // srj sez: if we are reloading ammo, wait will we are done before processing the pending command.
- && getStateMachine()->getCurrentStateID() != RELOAD_AMMO)
- {
- m_returnToBaseFrame = 0;
- AICommandParms parms(AICMD_MOVE_TO_POSITION, CMD_FROM_AI); // values don't matter, will be wiped by next line
- m_mostRecentCommand.reconstitute(parms);
- setFlag(HAS_PENDING_COMMAND, false);
- aiDoCommand(&parms);
- }
- else if (m_returnToBaseFrame != 0 && now >= m_returnToBaseFrame && getFlag(ALLOW_AIR_LOCO))
- {
- m_returnToBaseFrame = 0;
- DEBUG_ASSERTCRASH(isOutOfSpecialReloadAmmo() == false, ("Hmm, this seems unlikely -- isOutOfSpecialReloadAmmo()==false"));
- setFlag(USE_SPECIAL_RETURN_LOCO, false);
- setLastCommandSource( CMD_FROM_AI );
- getStateMachine()->setState(RETURNING_FOR_LANDING);
- }
- else if (m_returnToBaseFrame == 0 && d->m_returnToBaseIdleTime > 0 && getFlag(ALLOW_AIR_LOCO))
- {
- m_returnToBaseFrame = now + d->m_returnToBaseIdleTime;
- }
- }
- else
- {
- if (pp != NULL)
- {
- pp->setHealee(getObject(), false);
- }
- m_returnToBaseFrame = 0;
- if (getFlag(ALLOW_INTERRUPT_AND_RESUME_OF_CUR_STATE_FOR_RELOAD) &&
- isOutOfSpecialReloadAmmo() && getFlag(ALLOW_AIR_LOCO))
- {
- setFlag(USE_SPECIAL_RETURN_LOCO, true);
- setFlag(HAS_PENDING_COMMAND, true);
- setFlag(ALLOW_INTERRUPT_AND_RESUME_OF_CUR_STATE_FOR_RELOAD, false);
- setLastCommandSource( CMD_FROM_AI );
- getStateMachine()->setState(RETURNING_FOR_LANDING);
- }
- }
- Real minHeight = friend_getMinHeight();
- if( pp )
- {
- minHeight += pp->getLandingDeckHeightOffset();
- }
- Drawable* draw = jet->getDrawable();
- if (draw != NULL)
- {
- StateID id = getStateMachine()->getCurrentStateID();
- Bool needToCheckMinHeight = (id >= JETAISTATETYPE_FIRST && id <= JETAISTATETYPE_LAST) ||
- !jet->isAboveTerrain() ||
- !getFlag(ALLOW_AIR_LOCO);
- if( needToCheckMinHeight || jet->getStatusBits().test( OBJECT_STATUS_DECK_HEIGHT_OFFSET ) )
- {
- Real ht = jet->isAboveTerrain() ? jet->getHeightAboveTerrain() : 0;
- if (ht < minHeight)
- {
- Matrix3D tmp(1);
- tmp.Set_Z_Translation(minHeight - ht);
- draw->setInstanceMatrix(&tmp);
- }
- else
- {
- draw->setInstanceMatrix(NULL);
- }
- }
- else
- {
- draw->setInstanceMatrix(NULL);
- }
- }
- PhysicsBehavior* physics = jet->getPhysics();
- if (physics->getVelocityMagnitude() > 0 && getFlag(ALLOW_AIR_LOCO))
- jet->setModelConditionState(MODELCONDITION_JETEXHAUST);
- else
- jet->clearModelConditionState(MODELCONDITION_JETEXHAUST);
- if (jet->testStatus(OBJECT_STATUS_IS_ATTACKING))
- {
- m_attackLocoExpireFrame = now + d->m_attackLocoPersistTime;
- m_attackersMissExpireFrame = now + d->m_attackersMissPersistTime;
- }
- else
- {
- if (m_attackLocoExpireFrame != 0 && now >= m_attackLocoExpireFrame)
- {
- m_attackLocoExpireFrame = 0;
- }
- if (m_attackersMissExpireFrame != 0 && now >= m_attackersMissExpireFrame)
- {
- m_attackersMissExpireFrame = 0;
- }
- }
- if (m_untargetableExpireFrame != 0 && now >= m_untargetableExpireFrame)
- {
- m_untargetableExpireFrame = 0;
- }
- positionLockon();
-
- if (m_attackLocoExpireFrame != 0)
- {
- chooseLocomotorSet(d->m_attackingLoco);
- }
- else if (getFlag(USE_SPECIAL_RETURN_LOCO))
- {
- chooseLocomotorSet(d->m_returningLoco);
- }
-
- if( !jet->isKindOf( KINDOF_PRODUCED_AT_HELIPAD ) )
- {
- Drawable *draw = jet->getDrawable();
- if( draw )
- {
- if( getFlag(TAKEOFF_IN_PROGRESS)
- || getFlag(LANDING_IN_PROGRESS)
- || getObject()->isSignificantlyAboveTerrain()
- || isMoving()
- || isWaitingForPath() )
- {
- if( !m_enginesOn )
- {
- //We just started moving, therefore turn on the engines!
- draw->enableAmbientSound( TRUE );
- m_enginesOn = TRUE;
- }
- }
- else if( m_enginesOn )
- {
- //We're no longer moving, so turn off the engines!
- draw->enableAmbientSound( FALSE );
- m_enginesOn = FALSE;
- }
- }
- }
- /*UpdateSleepTime ret =*/ AIUpdateInterface::update();
- //return (mine < ret) ? mine : ret;
- /// @todo srj -- someday, make sleepy. for now, must not sleep.
- return UPDATE_SLEEP_NONE;
- }
- //-------------------------------------------------------------------------------------------------
- Bool JetAIUpdate::chooseLocomotorSet(LocomotorSetType wst)
- {
- const JetAIUpdateModuleData* d = getJetAIUpdateModuleData();
- if (!getFlag(ALLOW_AIR_LOCO))
- {
- wst = LOCOMOTORSET_TAXIING;
- }
- else if (m_attackLocoExpireFrame != 0)
- {
- wst = d->m_attackingLoco;
- }
- else if (getFlag(USE_SPECIAL_RETURN_LOCO))
- {
- wst = d->m_returningLoco;
- }
- return AIUpdateInterface::chooseLocomotorSet(wst);
- }
- //-------------------------------------------------------------------------------------------------
- void JetAIUpdate::setLocomotorGoalNone()
- {
- if ((getFlag(TAKEOFF_IN_PROGRESS) || getFlag(LANDING_IN_PROGRESS))
- && getFlag(ALLOW_AIR_LOCO) && !getFlag(ALLOW_CIRCLING))
- {
- Object* jet = getObject();
- Coord3D desiredPos = *jet->getPosition();
- const Coord3D* dir = jet->getUnitDirectionVector2D();
- desiredPos.x += dir->x * 1000.0f;
- desiredPos.y += dir->y * 1000.0f;
- setLocomotorGoalPositionExplicit(desiredPos);
- }
- else
- {
- AIUpdateInterface::setLocomotorGoalNone();
- }
- }
- //----------------------------------------------------------------------------------------
- Bool JetAIUpdate::getSneakyTargetingOffset(Coord3D* offset) const
- {
- if (m_attackersMissExpireFrame != 0 && TheGameLogic->getFrame() < m_attackersMissExpireFrame)
- {
- if (offset)
- {
- const JetAIUpdateModuleData* d = getJetAIUpdateModuleData();
- const Object* jet = getObject();
- const Coord3D* dir = jet->getUnitDirectionVector2D();
- offset->x = dir->x * d->m_sneakyOffsetWhenAttacking;
- offset->y = dir->y * d->m_sneakyOffsetWhenAttacking;
- offset->z = 0.0f;
- }
- return true;
- }
- else
- {
- return false;
- }
- }
- //----------------------------------------------------------------------------------------
- void JetAIUpdate::pruneDeadTargeters()
- {
- if (!m_targetedBy.empty())
- {
- for (std::list<ObjectID>::iterator it = m_targetedBy.begin(); it != m_targetedBy.end(); /* empty */ )
- {
- if (TheGameLogic->findObjectByID(*it) == NULL)
- {
- it = m_targetedBy.erase(it);
- }
- else
- {
- ++it;
- }
- }
- }
- }
- //----------------------------------------------------------------------------------------
- void JetAIUpdate::positionLockon()
- {
- if (!m_lockonDrawable)
- return;
- if (m_untargetableExpireFrame == 0)
- {
- TheGameClient->destroyDrawable(m_lockonDrawable);
- m_lockonDrawable = NULL;
- return;
- }
- const JetAIUpdateModuleData* d = getJetAIUpdateModuleData();
- UnsignedInt now = TheGameLogic->getFrame();
- UnsignedInt remaining = m_untargetableExpireFrame - now;
- UnsignedInt elapsed = d->m_lockonTime - remaining;
- Coord3D pos = *getObject()->getPosition();
- Real frac = (Real)remaining / (Real)d->m_lockonTime;
- Real finalDist = getObject()->getGeometryInfo().getBoundingCircleRadius();
- Real dist = finalDist + (d->m_lockonInitialDist - finalDist) * frac;
- Real angle = d->m_lockonAngleSpin * frac;
- pos.x += Cos(angle) * dist;
- pos.y += Sin(angle) * dist;
- // pos.z is untouched
- m_lockonDrawable->setPosition(&pos);
- Real dx = getObject()->getPosition()->x - pos.x;
- Real dy = getObject()->getPosition()->y - pos.y;
- if (dx || dy)
- m_lockonDrawable->setOrientation(atan2(dy, dx));
- // the Gaussian sum, to avoid keeping a running total:
- //
- // 1+2+3+...n = n*(n+1)/2
- //
- Real elapsedTimeSumPrev = 0.5f * (elapsed-1) * (elapsed);
- Real elapsedTimeSumCurr = elapsedTimeSumPrev + elapsed;
- Real factor = d->m_lockonFreq / d->m_lockonTime;
- Bool lastPhase = ((Int)(factor * elapsedTimeSumPrev) & 1) != 0;
- Bool thisPhase = ((Int)(factor * elapsedTimeSumCurr) & 1) != 0;
- if (lastPhase && (!thisPhase))
- {
- AudioEventRTS lockonSound = TheAudio->getMiscAudio()->m_lockonTickSound;
- lockonSound.setObjectID(getObject()->getID());
- TheAudio->addAudioEvent(&lockonSound);
- if (d->m_lockonBlinky)
- m_lockonDrawable->setDrawableHidden(false);
- }
- else
- {
- if (d->m_lockonBlinky)
- m_lockonDrawable->setDrawableHidden(true);
- }
- }
- //----------------------------------------------------------------------------------------
- void JetAIUpdate::buildLockonDrawableIfNecessary()
- {
- if (m_untargetableExpireFrame == 0)
- return;
- const JetAIUpdateModuleData* d = getJetAIUpdateModuleData();
- if (d->m_lockonCursor.isNotEmpty() && m_lockonDrawable == NULL)
- {
- const ThingTemplate* tt = TheThingFactory->findTemplate(d->m_lockonCursor);
- if (tt)
- {
- m_lockonDrawable = TheThingFactory->newDrawable(tt);
- }
- }
- positionLockon();
- }
- //----------------------------------------------------------------------------------------
- void JetAIUpdate::addTargeter(ObjectID id, Bool add)
- {
- const JetAIUpdateModuleData* d = getJetAIUpdateModuleData();
- UnsignedInt lockonTime = d->m_lockonTime;
- if (lockonTime != 0)
- {
- std::list<ObjectID>::iterator it = std::find(m_targetedBy.begin(), m_targetedBy.end(), id);
- if (add)
- {
- if (it == m_targetedBy.end())
- {
- m_targetedBy.push_back(id);
- if (m_untargetableExpireFrame == 0 && m_targetedBy.size() == 1)
- {
- m_untargetableExpireFrame = TheGameLogic->getFrame() + lockonTime;
- buildLockonDrawableIfNecessary();
- }
- }
- }
- else
- {
- if (it != m_targetedBy.end())
- {
- m_targetedBy.erase(it);
- if (m_targetedBy.empty())
- {
- m_untargetableExpireFrame = 0;
- }
- }
- }
- }
- }
- //----------------------------------------------------------------------------------------
- Bool JetAIUpdate::isTemporarilyPreventingAimSuccess() const
- {
- return m_untargetableExpireFrame != 0 && (TheGameLogic->getFrame() < m_untargetableExpireFrame);
- }
- //----------------------------------------------------------------------------------------
- Bool JetAIUpdate::isAllowedToMoveAwayFromUnit() const
- {
- // parked (or landing) units don't get to do this.
- if (!getFlag(ALLOW_AIR_LOCO) || getFlag(TAKEOFF_IN_PROGRESS) || getFlag(LANDING_IN_PROGRESS))
- return false;
- return AIUpdateInterface::isAllowedToMoveAwayFromUnit();
- }
- //-------------------------------------------------------------------------------------------------
- Bool JetAIUpdate::isDoingGroundMovement(void) const
- {
- // srj per jba: Air units should never be doing ground movement, even when taxiing...
- // (exception: see getTreatAsAircraftForLocoDistToGoal)
- return false;
- }
- //-------------------------------------------------------------------------------------------------
- Bool JetAIUpdate::getTreatAsAircraftForLocoDistToGoal() const
- {
- // exception to isDoingGroundMovement: should never treat as aircraft for dist-to-goal when taxiing.
- if (getFlag(TAXI_IN_PROGRESS))
- {
- return false;
- }
- else
- {
- return AIUpdateInterface::getTreatAsAircraftForLocoDistToGoal();
- }
- }
- //----------------------------------------------------------------------------------------
- /**
- * Follow the path defined by the given array of points
- */
- void JetAIUpdate::privateFollowPath( const std::vector<Coord3D>* path, Object *ignoreObject, CommandSourceType cmdSource, Bool exitProduction )
- {
- if (exitProduction)
- {
- getStateMachine()->clear();
- if( ignoreObject )
- ignoreObstacle( ignoreObject );
- setLastCommandSource( cmdSource );
- if (getObject()->isKindOf(KINDOF_PRODUCED_AT_HELIPAD))
- getStateMachine()->setState( TAKING_OFF_AWAIT_CLEARANCE );
- else
- getStateMachine()->setState( TAXI_FROM_HANGAR );
- }
- else
- {
- AIUpdateInterface::privateFollowPath(path, ignoreObject, cmdSource, exitProduction);
- }
- }
- //----------------------------------------------------------------------------------------
- void JetAIUpdate::privateFollowPathAppend( const Coord3D *pos, CommandSourceType cmdSource )
- {
- // nothing yet... might need to override. not sure. (srj)
- AIUpdateInterface::privateFollowPathAppend(pos, cmdSource);
- }
- //----------------------------------------------------------------------------------------
- void JetAIUpdate::doLandingCommand(Object *airfield, CommandSourceType cmdSource)
- {
- if (getObject()->isKindOf(KINDOF_PRODUCED_AT_HELIPAD))
- {
- m_landingPosForHelipadStuff = *airfield->getPosition();
- Coord3D tmp;
- FindPositionOptions options;
- options.maxRadius = airfield->getGeometryInfo().getBoundingCircleRadius() * 10.0f;
- if (ThePartitionManager->findPositionAround(&m_landingPosForHelipadStuff, &options, &tmp))
- m_landingPosForHelipadStuff = tmp;
- }
- for (BehaviorModule** i = airfield->getBehaviorModules(); *i; ++i)
- {
- ParkingPlaceBehaviorInterface* pp = (*i)->getParkingPlaceBehaviorInterface();
- if (pp == NULL)
- continue;
-
- if (getObject()->isKindOf(KINDOF_PRODUCED_AT_HELIPAD) ||
- pp->reserveSpace(getObject()->getID(), friend_getParkingOffset(), NULL))
- {
- // if we had a space at another airfield, release it
- ParkingPlaceBehaviorInterface* oldPP = getPP(getObject()->getProducerID());
- if (oldPP != NULL && oldPP != pp)
- {
- oldPP->releaseSpace(getObject()->getID());
- }
- getObject()->setProducer(airfield);
- DEBUG_ASSERTCRASH(isOutOfSpecialReloadAmmo() == false, ("Hmm, this seems unlikely -- isOutOfSpecialReloadAmmo()==false"));
- setFlag(USE_SPECIAL_RETURN_LOCO, false);
- setFlag(ALLOW_INTERRUPT_AND_RESUME_OF_CUR_STATE_FOR_RELOAD, false);
- setLastCommandSource( cmdSource );
- getStateMachine()->setState(RETURNING_FOR_LANDING);
- return;
- }
- }
- }
- //----------------------------------------------------------------------------------------
- void JetAIUpdate::notifyVictimIsDead()
- {
- if (getJetAIUpdateModuleData()->m_needsRunway)
- m_returnToBaseFrame = TheGameLogic->getFrame();
- }
- //----------------------------------------------------------------------------------------
- /**
- * Enter the given object
- */
- void JetAIUpdate::privateEnter( Object *objectToEnter, CommandSourceType cmdSource )
- {
- // we are already landing. just ignore it.
- if (getFlag(LANDING_IN_PROGRESS))
- return;
- if( !TheActionManager->canEnterObject( getObject(), objectToEnter, cmdSource, DONT_CHECK_CAPACITY ) )
- return;
- doLandingCommand(objectToEnter, cmdSource);
- }
- //----------------------------------------------------------------------------------------
- /**
- * Get repaired at the repair depot
- */
- void JetAIUpdate::privateGetRepaired( Object *repairDepot, CommandSourceType cmdSource )
- {
- // we are already landing. just ignore it.
- if (getFlag(LANDING_IN_PROGRESS))
- return;
- // sanity, if we can't get repaired from here get out of here
- if( TheActionManager->canGetRepairedAt( getObject(), repairDepot, cmdSource ) == FALSE )
- return;
- // dock with the repair depot
- doLandingCommand( repairDepot, cmdSource );
- }
- //-------------------------------------------------------------------------------------------------
- Bool JetAIUpdate::isParkedAt(const Object* obj) const
- {
- if (!getFlag(ALLOW_AIR_LOCO) &&
- !getObject()->isKindOf(KINDOF_PRODUCED_AT_HELIPAD) &&
- obj != NULL)
- {
- Object* airfield;
- ParkingPlaceBehaviorInterface* pp = getPP(getObject()->getProducerID(), &airfield);
- if (pp != NULL && airfield != NULL && airfield == obj)
- {
- return true;
- }
- }
- return false;
- }
- //-------------------------------------------------------------------------------------------------
- void JetAIUpdate::aiDoCommand(const AICommandParms* parms)
- {
- // call this from aiDoCommand as well as update, because this can
- // be called before update ever is... if the unit is placed on a map,
- // and a script tells it to do something with a condition of TRUE!
- getProducerLocation();
- if (!isAllowedToRespondToAiCommands(parms))
- return;
-
- // note that we always store this, even if nothing will be "pending".
- m_mostRecentCommand.store(*parms);
- if (getFlag(TAKEOFF_IN_PROGRESS) || getFlag(LANDING_IN_PROGRESS))
- {
- // have to wait for takeoff or landing to complete, just store the sucker
- setFlag(HAS_PENDING_COMMAND, true);
- return;
- }
- else if (parms->m_cmd == AICMD_IDLE && getStateMachine()->getCurrentStateID() == RELOAD_AMMO)
- {
- // uber-special-case... if we are told to idle, but are reloading ammo, ignore it for now,
- // since we're already doing "nothing" and responding to this will cease our reload...
- // don't just return, tho, in case we were (say) reloading during a guard stint.
- setFlag(HAS_PENDING_COMMAND, true);
- return;
- }
- else if( parms->m_cmd == AICMD_IDLE && getObject()->isAirborneTarget() && !getObject()->isKindOf( KINDOF_PRODUCED_AT_HELIPAD ) )
- {
- getStateMachine()->clear();
- setLastCommandSource( CMD_FROM_AI );
- getStateMachine()->setState( RETURNING_FOR_LANDING );
- return;
- }
- else if (!getFlag(ALLOW_AIR_LOCO))
- {
- 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
- break;
- case AICMD_ENTER:
- case AICMD_GET_REPAIRED:
- // if we're already parked at the airfield in question, just ignore.
- if (isParkedAt(parms->m_obj))
- return;
-
- // else fall thru to the default case!
- default:
- {
- // nuke any existing pending cmd
- m_mostRecentCommand.store(*parms);
- setFlag(HAS_PENDING_COMMAND, true);
- getStateMachine()->clear();
- setLastCommandSource( CMD_FROM_AI );
- getStateMachine()->setState( TAKING_OFF_AWAIT_CLEARANCE );
-
- return;
- }
- }
- }
- switch (parms->m_cmd)
- {
- case AICMD_GUARD_POSITION:
- case AICMD_GUARD_OBJECT:
- case AICMD_GUARD_AREA:
- case AICMD_HUNT:
- case AICMD_GUARD_RETALIATE:
- setFlag(ALLOW_INTERRUPT_AND_RESUME_OF_CUR_STATE_FOR_RELOAD, true);
- break;
- default:
- setFlag(ALLOW_INTERRUPT_AND_RESUME_OF_CUR_STATE_FOR_RELOAD, false);
- break;
- }
- setFlag(HAS_PENDING_COMMAND, false);
- AIUpdateInterface::aiDoCommand(parms);
- }
- //-------------------------------------------------------------------------------------------------
- void JetAIUpdate::friend_setAllowAirLoco(Bool allowAirLoco)
- {
- setFlag(ALLOW_AIR_LOCO, allowAirLoco);
- }
- //-------------------------------------------------------------------------------------------------
- void JetAIUpdate::friend_enableAfterburners(Bool v)
- {
- Object* jet = getObject();
- if (v)
- {
- jet->setModelConditionState(MODELCONDITION_JETAFTERBURNER);
- if (!m_afterburnerSound.isCurrentlyPlaying())
- {
- m_afterburnerSound.setObjectID(jet->getID());
- m_afterburnerSound.setPlayingHandle(TheAudio->addAudioEvent(&m_afterburnerSound));
- }
- }
- else
- {
- jet->clearModelConditionState(MODELCONDITION_JETAFTERBURNER);
- if (m_afterburnerSound.isCurrentlyPlaying())
- {
- TheAudio->removeAudioEvent(m_afterburnerSound.getPlayingHandle());
- }
- }
- }
- //-------------------------------------------------------------------------------------------------
- void JetAIUpdate::friend_addWaypointToGoalPath( const Coord3D &bestPos )
- {
- privateFollowPathAppend( &bestPos, CMD_FROM_AI );
- }
- //-------------------------------------------------------------------------------------------------
- AICommandType JetAIUpdate::friend_getPendingCommandType() const
- {
- if( getFlag( HAS_PENDING_COMMAND ) )
- {
- return m_mostRecentCommand.getCommandType();
- }
- return AICMD_NO_COMMAND;
- }
- //-------------------------------------------------------------------------------------------------
- void JetAIUpdate::friend_purgePendingCommand()
- {
- setFlag(HAS_PENDING_COMMAND, false);
- }
- // ------------------------------------------------------------------------------------------------
- /** CRC */
- // ------------------------------------------------------------------------------------------------
- void JetAIUpdate::crc( Xfer *xfer )
- {
- // extend base class
- AIUpdateInterface::crc(xfer);
- } // end crc
- // ------------------------------------------------------------------------------------------------
- /** Xfer method
- * Version Info:
- * 1: Initial version */
- // ------------------------------------------------------------------------------------------------
- void JetAIUpdate::xfer( Xfer *xfer )
- {
- // version
- XferVersion currentVersion = 2;
- XferVersion version = currentVersion;
- xfer->xferVersion( &version, currentVersion );
-
- // extend base class
- AIUpdateInterface::xfer(xfer);
- xfer->xferCoord3D(&m_producerLocation);
- m_mostRecentCommand.doXfer(xfer);
- xfer->xferUnsignedInt(&m_attackLocoExpireFrame);
- xfer->xferUnsignedInt(&m_attackersMissExpireFrame);
- xfer->xferUnsignedInt(&m_returnToBaseFrame);
- xfer->xferSTLObjectIDList(&m_targetedBy);
- xfer->xferUnsignedInt(&m_untargetableExpireFrame);
- // Set on create.
- //AudioEventRTS m_afterburnerSound; ///< Sound when afterburners on
- AsciiString drawName;
- if (m_lockonDrawable) {
- drawName = m_lockonDrawable->getTemplate()->getName();
- }
- xfer->xferAsciiString(&drawName);
- if (drawName.isNotEmpty() && m_lockonDrawable==NULL)
- {
- const ThingTemplate* tt = TheThingFactory->findTemplate(drawName);
- if (tt)
- {
- m_lockonDrawable = TheThingFactory->newDrawable(tt);
- }
- }
- xfer->xferInt(&m_flags);
- if( version >= 2 )
- {
- xfer->xferBool( &m_enginesOn );
- }
- else
- {
- //We don't have to be accurate -- this is a patch.
- if( getFlag(TAKEOFF_IN_PROGRESS) || getFlag(LANDING_IN_PROGRESS) || getObject()->isSignificantlyAboveTerrain() || getObject()->isKindOf( KINDOF_PRODUCED_AT_HELIPAD ) )
- {
- m_enginesOn = TRUE;
- }
- else
- {
- m_enginesOn = FALSE;
- }
- }
- } // end xfer
- // ------------------------------------------------------------------------------------------------
- /** Load post process */
- // ------------------------------------------------------------------------------------------------
- void JetAIUpdate::loadPostProcess( void )
- {
- //When drawables are created, so are their ambient sounds. After loading, only turn off the
- //ambient sound if the engine is off.
- if( !m_enginesOn )
- {
- Drawable *draw = getObject()->getDrawable();
- if( draw )
- {
- draw->stopAmbientSound();
- }
- }
- // extend base class
- AIUpdateInterface::loadPostProcess();
- } // end loadPostProcess
|