| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372 |
- /*
- ** 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. //
- // //
- ////////////////////////////////////////////////////////////////////////////////
- // 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/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
- };
- //-------------------------------------------------------------------------------------------------
- static Bool isOutOfSpecialReloadAmmo(Object* jet)
- {
- // 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++ )
- {
- 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_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_AIRFIELD), KINDOFMASK_NONE);
- PartitionFilterRejectByObjectStatus filterStatus(OBJECT_STATUS_UNDER_CONSTRUCTION, 0);
- PartitionFilterRejectByObjectStatus filterStatusTwo(OBJECT_STATUS_SOLD, 0); // 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;
- }
- // 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 (!isOutOfSpecialReloadAmmo(jet) && 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);
- 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)
- {
- 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 (intermed)
- movePath->appendNode( &intermedPt, LAYER_GROUND );
- movePath->appendNode( &ppinfo.runwayPrep, LAYER_GROUND );
- movePath->appendNode( &ppinfo.runwayStart, LAYER_GROUND );
- }
- else if (m_taxiMode == FROM_HANGAR)
- {
- 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 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
- 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.runwayApproach);
- }
- 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 )
- {
- 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( 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());
- PhysicsBehavior* physics = jet->getPhysics();
- Real ratio = physics->getVelocityMagnitude() / (m_maxSpeed * jetAI->friend_getTakeoffSpeedForMaxLift());
- 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();
- }
- 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;
- 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));
- 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();
- UnsignedInt 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_takeoffSpeedForMaxLift = 1.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 ) },
- { "TakeoffSpeedForMaxLift", INI::parsePercentToReal, NULL, offsetof( JetAIUpdateModuleData, m_takeoffSpeedForMaxLift ) },
- { "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();
- }
- //-------------------------------------------------------------------------------------------------
- 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(jet) && 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(jet) == false, ("Hmm, this seems unlikely -- isOutOfSpecialReloadAmmo(jet)==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(jet) && 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();
- 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)
- {
- 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(getObject()) == false, ("Hmm, this seems unlikely -- isOutOfSpecialReloadAmmo(jet)==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 (!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:
- 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());
- }
- }
- }
- // ------------------------------------------------------------------------------------------------
- /** 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
|