| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918 |
- /*
- ** 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. //
- // //
- ////////////////////////////////////////////////////////////////////////////////
- // FILE: SpectreGunshipUpdate.cpp //////////////////////////////////////////////////////////////////////////
- // Author: Mark Lorenzen, April 2003
- // Desc: Update module to handle weapon firing of the SpectreGunship Generals special power.
- ///////////////////////////////////////////////////////////////////////////////////////////////////
- #include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
- #define DEFINE_DEATH_NAMES
- // INCLUDES ///////////////////////////////////////////////////////////////////////////////////////
- #include "Common\GameAudio.h"
- #include "Common\ThingTemplate.h"
- #include "Common\ThingFactory.h"
- #include "Common\Player.h"
- #include "Common\PlayerList.h"
- #include "Common\Xfer.h"
- #include "Common\ClientUpdateModule.h"
- #include "GameClient\ControlBar.h"
- #include "GameClient\GameClient.h"
- #include "GameClient\Drawable.h"
- #include "GameClient\ParticleSys.h"
- #include "GameClient\FXList.h"
- #include "GameClient/ParticleSys.h"
- #include "GameLogic\Locomotor.h"
- #include "GameLogic\GameLogic.h"
- #include "GameLogic\PartitionManager.h"
- #include "GameLogic\Object.h"
- #include "GameLogic\ObjectIter.h"
- #include "GameLogic\Weaponset.h"
- #include "GameLogic\Weapon.h"
- #include "GameLogic\TerrainLogic.h"
- #include "GameLogic\Module\SpecialPowerModule.h"
- #include "GameLogic\Module\SpectreGunshipUpdate.h"
- #include "GameLogic\Module\PhysicsUpdate.h"
- #include "GameLogic\Module\LaserUpdate.h"
- #include "GameLogic\Module\ActiveBody.h"
- #include "GameLogic\Module\AIUpdate.h"
- #include "GameLogic\Module\ContainModule.h"
- #ifdef _INTERNAL
- // for occasional debugging...
- //#pragma optimize("", off)
- //#pragma MESSAGE("************************************** WARNING, optimization disabled for debugging purposes")
- #endif
- #define ORBIT_INSERTION_SLOPE_MAX (0.8f)
- #define ORBIT_INSERTION_SLOPE_MIN (0.5f)
- #define ONE (1.0f)
- #define ZERO (0.0f)
- #define LOTS_OF_SHOTS (9999)
- //-------------------------------------------------------------------------------------------------
- //-------------------------------------------------------------------------------------------------
- SpectreGunshipUpdateModuleData::SpectreGunshipUpdateModuleData()
- {
- m_specialPowerTemplate = NULL;
- /******BOTH*******//*BOTH*//******BOTH*******//******BOTH*******/ m_attackAreaRadius = 200.0f;
- /*************/ m_gattlingStrafeFXParticleSystem = NULL;
- /*************/ m_howitzerWeaponTemplate = NULL;
- /*************/ m_orbitFrames = 0;
- /*************/ m_targetingReticleRadius = 25.0f;
- /*************/ m_gunshipOrbitRadius = 250.0f;
- m_strafingIncrement = 20.0f;
- m_orbitInsertionSlope = 0.7f;
- m_howitzerFiringRate = 10;
- m_howitzerFollowLag = 0;
- m_randomOffsetForHowitzer = 20.0f;
- }
- static Real zero = 0.0f;
- //-------------------------------------------------------------------------------------------------
- /*static*/ void SpectreGunshipUpdateModuleData::buildFieldParse(MultiIniFieldParse& p)
- {
- ModuleData::buildFieldParse(p);
- static const FieldParse dataFieldParse[] =
- {
- { "SpecialPowerTemplate", INI::parseSpecialPowerTemplate, NULL, offsetof( SpectreGunshipUpdateModuleData, m_specialPowerTemplate ) },
- { "GattlingTemplateName", INI::parseAsciiString, NULL, offsetof( SpectreGunshipUpdateModuleData, m_gattlingTemplateName ) },
- { "HowitzerFiringRate", INI::parseDurationUnsignedInt, NULL, offsetof( SpectreGunshipUpdateModuleData, m_howitzerFiringRate ) },
- { "OrbitTime", INI::parseDurationUnsignedInt, NULL, offsetof( SpectreGunshipUpdateModuleData, m_orbitFrames ) },
- { "HowitzerFollowLag", INI::parseDurationUnsignedInt, NULL, offsetof( SpectreGunshipUpdateModuleData, m_howitzerFollowLag ) },
- { "AttackAreaRadius", INI::parseReal, NULL, offsetof( SpectreGunshipUpdateModuleData, m_attackAreaRadius ) },
- { "StrafingIncrement", INI::parseReal, NULL, offsetof( SpectreGunshipUpdateModuleData, m_strafingIncrement ) },
- { "OrbitInsertionSlope", INI::parseReal, NULL, offsetof( SpectreGunshipUpdateModuleData, m_orbitInsertionSlope ) },
- { "RandomOffsetForHowitzer", INI::parseReal, NULL, offsetof( SpectreGunshipUpdateModuleData, m_randomOffsetForHowitzer ) },
- { "TargetingReticleRadius", INI::parseReal, NULL, offsetof( SpectreGunshipUpdateModuleData, m_targetingReticleRadius ) },
- { "GunshipOrbitRadius", INI::parseReal, NULL, offsetof( SpectreGunshipUpdateModuleData, m_gunshipOrbitRadius ) },
- { "HowitzerWeaponTemplate", INI::parseWeaponTemplate, NULL, offsetof( SpectreGunshipUpdateModuleData, m_howitzerWeaponTemplate ) },
- { "GattlingStrafeFXParticleSystem", INI::parseParticleSystemTemplate, NULL, offsetof( SpectreGunshipUpdateModuleData, m_gattlingStrafeFXParticleSystem ) },
- { "AttackAreaDecal", RadiusDecalTemplate::parseRadiusDecalTemplate, NULL, offsetof( SpectreGunshipUpdateModuleData, m_attackAreaDecalTemplate ) },
- { "TargetingReticleDecal", RadiusDecalTemplate::parseRadiusDecalTemplate, NULL, offsetof( SpectreGunshipUpdateModuleData, m_targetingReticleDecalTemplate ) },
-
- { 0, 0, 0, 0 }
- };
- p.add(dataFieldParse);
- }
- //-------------------------------------------------------------------------------------------------
- SpectreGunshipUpdate::SpectreGunshipUpdate( Thing *thing, const ModuleData* moduleData ) : SpecialPowerUpdateModule( thing, moduleData )
- {
- m_specialPowerModule = NULL;
- m_gattlingID = INVALID_ID;
- m_status = GUNSHIP_STATUS_IDLE;
- m_initialTargetPosition.zero();
- m_overrideTargetDestination.zero();
- m_gattlingTargetPosition.zero();
- m_positionToShootAt.zero();
- m_attackAreaDecal.clear();
- m_targetingReticleDecal.clear();
- m_orbitEscapeFrame = 0;
- m_afterburnerSound = *(getObject()->getTemplate()->getPerUnitSound("Afterburner"));
- m_howitzerFireSound = *(getObject()->getTemplate()->getPerUnitSound("HowitzerFire"));
- m_okToFireHowitzerCounter = 0;
- #if defined TRACKERS
- m_howitzerTrackerDecal.clear();
- #endif
- }
- //-------------------------------------------------------------------------------------------------
- SpectreGunshipUpdate::~SpectreGunshipUpdate( void )
- {
- m_attackAreaDecal.clear();
- m_targetingReticleDecal.clear();
- #if defined TRACKERS
- m_howitzerTrackerDecal.clear();
- #endif
- }
- //-------------------------------------------------------------------------------------------------
- // Validate that we have the necessary data from the ini file.
- //-------------------------------------------------------------------------------------------------
- void SpectreGunshipUpdate::onObjectCreated()
- {
- const SpectreGunshipUpdateModuleData *data = getSpectreGunshipUpdateModuleData();
- Object *obj = getObject();
- if( !data->m_specialPowerTemplate )
- {
- DEBUG_CRASH( ("%s object's SpectreGunshipUpdate lacks access to the SpecialPowerTemplate. Needs to be specified in ini.", obj->getTemplate()->getName().str() ) );
- return;
- }
- m_specialPowerModule = obj->getSpecialPowerModule( data->m_specialPowerTemplate );
- m_satellitePosition.set( obj->getPosition() );
- }
- //-------------------------------------------------------------------------------------------------
- Bool SpectreGunshipUpdate::initiateIntentToDoSpecialPower(const SpecialPowerTemplate *specialPowerTemplate, const Object *targetObj, const Coord3D *targetPos, const Waypoint *way, UnsignedInt commandOptions )
- {
- const SpectreGunshipUpdateModuleData *data = getSpectreGunshipUpdateModuleData();
- if( m_specialPowerModule->getSpecialPowerTemplate() != specialPowerTemplate )
- {
- //Check to make sure our modules are connected.
- return FALSE;
- }
- if( !BitTest( commandOptions, COMMAND_FIRED_BY_SCRIPT ) )
- {
- m_initialTargetPosition.set( targetPos );
- m_overrideTargetDestination.set( targetPos );
- m_gattlingTargetPosition.set( targetPos );
- }
- else
- {
- UnsignedInt now = TheGameLogic->getFrame();
- m_specialPowerModule->setReadyFrame( now );
- m_initialTargetPosition.set( targetPos );
- setLogicalStatus( GUNSHIP_STATUS_INSERTING );
- }
- Object * gunShip = getObject();
- if ( gunShip )
- {
- // MAKE TWEAKS TO AI SO SHIP MOVES PRETTY
- AIUpdateInterface *shipAI = gunShip->getAIUpdateInterface();
- if ( shipAI)
- {
- shipAI->chooseLocomotorSet( LOCOMOTORSET_PANIC );
- shipAI->getCurLocomotor()->setAllowInvalidPosition(TRUE);
- shipAI->getCurLocomotor()->setUltraAccurate(TRUE); // set ultra-accurate just so AI won't try to adjust our dest
- }
- Drawable *draw = gunShip->getDrawable();
- if ( draw )
- draw->clearAndSetModelConditionState( MODELCONDITION_DOOR_1_OPENING, MODELCONDITION_DOOR_1_CLOSING );
- friend_enableAfterburners(TRUE);
- setLogicalStatus( GUNSHIP_STATUS_INSERTING ); // The gunship is en route to the tharget area, from map-edge
-
- // TELL THE GUNNERS ABOARD THE GUNSHIP TO HOLD THEIR FIRE UNTIL ORBIT INSERTION
- ContainModuleInterface *shipContain = gunShip->getContain();
- if ( shipContain )
- {
- Object *newGattling = TheGameLogic->findObjectByID( m_gattlingID );
- const ThingTemplate *gattlingTemplate = TheThingFactory->findTemplate( data->m_gattlingTemplateName );
- if( newGattling != NULL )
- {
- m_gattlingID = INVALID_ID;
- newGattling = NULL;
- }
- if ( gattlingTemplate )
- {
- newGattling = TheThingFactory->newObject( gattlingTemplate, getObject()->getTeam() );
- DEBUG_ASSERTCRASH( gunShip, ("SpecterGunshipUpdate failed to find or create a GATTLING object"));
- shipContain->addToContain( newGattling );
- m_gattlingID = newGattling->getID();
- }
- Object *gattling = TheGameLogic->findObjectByID( m_gattlingID );
- if ( gattling )
- gattling->setDisabled ( DISABLED_PARALYZED );
- }
- }
- data->m_attackAreaDecalTemplate.createRadiusDecal( *getObject()->getPosition(), data->m_attackAreaRadius, getObject()->getControllingPlayer(), m_attackAreaDecal);
- data->m_targetingReticleDecalTemplate.createRadiusDecal( *getObject()->getPosition(), data->m_targetingReticleRadius, getObject()->getControllingPlayer(), m_targetingReticleDecal);
- #if defined TRACKERS
- data->m_targetingReticleDecalTemplate.createRadiusDecal( *getObject()->getPosition(), data->m_targetingReticleRadius, getObject()->getControllingPlayer(), m_howitzerTrackerDecal);
- #endif
- SpecialPowerModuleInterface *spmInterface = getObject()->getSpecialPowerModule( specialPowerTemplate );
- if( spmInterface )
- {
- SpecialPowerModule *spModule = (SpecialPowerModule*)spmInterface;
- spModule->markSpecialPowerTriggered( &m_initialTargetPosition );
- }
-
- return TRUE;
- }
- //-------------------------------------------------------------------------------------------------
- Bool SpectreGunshipUpdate::isPowerCurrentlyInUse( const CommandButton *command ) const
- {
- return false;
- }
- //-------------------------------------------------------------------------------------------------
- void SpectreGunshipUpdate::setSpecialPowerOverridableDestination( const Coord3D *loc )
- {
- Object *me = getObject();
- if( !me->isDisabled() )
- {
- m_overrideTargetDestination = *loc;
- if( me->getControllingPlayer() && me->getControllingPlayer()->isLocalPlayer() )
- {
- AudioEventRTS soundToPlay = *me->getTemplate()->getVoiceAttack();
- soundToPlay.setObjectID( me->getID() );
- TheAudio->addAudioEvent(&soundToPlay);
- }
- }
- }
- //-------------------------------------------------------------------------------------------------
- Bool SpectreGunshipUpdate::isPointOffMap( const Coord3D& testPos ) const
- {
- Region3D mapRegion;
- TheTerrainLogic->getExtentIncludingBorder( &mapRegion );
- if (!mapRegion.isInRegionNoZ( &testPos ))
- return true;
- return false;
- }
- // PARTITION FILTERS!
- //-----------------------------------------------------------------------------
- class PartitionFilterLiveMapEnemies : public PartitionFilter
- {
- private:
- const Object *m_obj;
- public:
- PartitionFilterLiveMapEnemies(const Object *obj) : m_obj(obj) { }
- virtual Bool allow(Object *objOther)
- {
- // this is way fast (bit test) so do it first.
- if (objOther->isEffectivelyDead())
- return false;
- // this is also way fast (bit test) so do it next.
- if (objOther->isOffMap() != m_obj->isOffMap())
- return false;
- Relationship r = m_obj->getRelationship(objOther);
- if (r != ENEMIES)
- return false;
- return true;
- }
- #if defined(_DEBUG) || defined(_INTERNAL)
- virtual const char* debugGetName() { return "PartitionFilterLiveMapEnemies"; }
- #endif
- };
- //-----------------------------------------------------------------------------
- //-------------------------------------------------------------------------------------------------
- /** The update callback. */
- //-------------------------------------------------------------------------------------------------
- UpdateSleepTime SpectreGunshipUpdate::update()
- {
- const SpectreGunshipUpdateModuleData *data = getSpectreGunshipUpdateModuleData();
- Object *gunship = getObject();
- if ( gunship )
- {
- if ( gunship->isEffectivelyDead() )
- return UPDATE_SLEEP_FOREVER;
- m_attackAreaDecal.update();
- m_targetingReticleDecal.update();
- #if defined TRACKERS
- m_howitzerTrackerDecal.update();
- #endif
- AIUpdateInterface *shipAI = gunship->getAIUpdateInterface();
- AIUpdateInterface *gattlingAI = NULL;
- Object *gattling = TheGameLogic->findObjectByID( m_gattlingID );
- if ( gattling )
- {
- gattlingAI = gattling->getAIUpdateInterface();
- }
- if ( m_status == GUNSHIP_STATUS_INSERTING || m_status == GUNSHIP_STATUS_ORBITING )
- {
- // init'l apogee
- // target *<-------->*
- // posi ^\ /
- // | \ /
- // perigee | \ /
- // | */ declination
- // | / \
- // v/ * m_satelliteposition
- // *
- // |
- // |
- // |
- // |
- // |
- // * gunship->getPosition()
- //perigee is the point in the orbital arc nearest the satellite being captured
- Coord3D perigee = *gunship->getPosition();
- perigee.sub( &m_initialTargetPosition );
- perigee.z = zero;
- Real distanceToTarget = perigee.length();
- perigee.normalize();
- //apogee is the anteclockwise point fathest from the perigee line
- Coord3D apogee;
- apogee.z = zero;
- apogee.x = -perigee.y;
- apogee.y = perigee.x;
- // declination intersects line [p][a], given an attack slope of n1/n2
- Coord3D declination;
- Real n1 = min( ORBIT_INSERTION_SLOPE_MAX, max(ORBIT_INSERTION_SLOPE_MIN, data->m_orbitInsertionSlope) );
- Real n2 = ONE - n1;
- declination.z = zero;
- declination.x = ( perigee.x * n1 ) + ( apogee.x * n2 );
- declination.y = ( perigee.y * n1 ) + ( apogee.y * n2 );
-
- //scale out to the orbital radius
- Real orbitalRadius = data->m_gunshipOrbitRadius;
- declination.x *= orbitalRadius;
- declination.y *= orbitalRadius;
- m_satellitePosition.x = m_initialTargetPosition.x + declination.x;
- m_satellitePosition.y = m_initialTargetPosition.y + declination.y;
-
- if ( shipAI)
- {
- shipAI->aiMoveToPosition( &m_satellitePosition, CMD_FROM_AI );
- }
- Real constraintRadius = data->m_attackAreaRadius - data->m_targetingReticleRadius;
- //Constrain Target Override to the targeting radius
- Coord3D overrideTargetDelta = m_initialTargetPosition;
- overrideTargetDelta.sub( &m_overrideTargetDestination );
- if ( overrideTargetDelta.length() > constraintRadius )
- {
- overrideTargetDelta.normalize();
- overrideTargetDelta.x *= constraintRadius;
- overrideTargetDelta.y *= constraintRadius;
- m_overrideTargetDestination.x = m_initialTargetPosition.x - overrideTargetDelta.x;
- m_overrideTargetDestination.y = m_initialTargetPosition.y - overrideTargetDelta.y;
- }
- m_attackAreaDecal.setPosition( m_initialTargetPosition );
- m_targetingReticleDecal.setPosition( m_overrideTargetDestination );
- #if defined TRACKERS
- m_howitzerTrackerDecal.setPosition( m_gattlingTargetPosition );
- #endif
- if ( (m_status == GUNSHIP_STATUS_INSERTING) && (distanceToTarget < orbitalRadius ) )// close enough to shoot
- {
- setLogicalStatus( GUNSHIP_STATUS_ORBITING );
- m_orbitEscapeFrame = TheGameLogic->getFrame() + data->m_orbitFrames;
- Object *gattling = TheGameLogic->findObjectByID( m_gattlingID );
- if ( gattling )
- gattling->clearDisabled ( DISABLED_PARALYZED );
- AIUpdateInterface *shipAI = gunship->getAIUpdateInterface();
- if ( shipAI)
- {
- shipAI->chooseLocomotorSet( LOCOMOTORSET_NORMAL );
- shipAI->getCurLocomotor()->setAllowInvalidPosition(TRUE);
- shipAI->getCurLocomotor()->setUltraAccurate(TRUE); // set ultra-accurate just so AI won't try to adjust our dest
- }
- Drawable *draw = gunship->getDrawable();
- if ( draw )
- draw->clearAndSetModelConditionState( MODELCONDITION_DOOR_1_CLOSING, MODELCONDITION_DOOR_1_OPENING );
- friend_enableAfterburners(FALSE);
- }
- } // endif status == ORBITING || INSERTING
- if ( m_status == GUNSHIP_STATUS_ORBITING )
- {
- Object *validTargetObject = NULL;
- if ( TheGameLogic->getFrame() >= m_orbitEscapeFrame )
- {
- cleanUp();
- setLogicalStatus( GUNSHIP_STATUS_DEPARTING );
- // CEASE FIRE, RETURN TO BASE
- disengageAndDepartAO( gunship );
- }//endif escapeframe
- else
- {
- // ONLY EVERY FEW FRAMES DO WE RE_EVALUATE THE TARGET OBJECT
- if ( TheGameLogic->getFrame() %data->m_howitzerFiringRate < ONE )
- {
- m_positionToShootAt = m_overrideTargetDestination; // unless we get a hit, below
- PartitionFilterLiveMapEnemies filterObvious( gunship );
- PartitionFilterStealthedAndUndetected filterStealth( gunship, false );
- PartitionFilterPossibleToAttack filterAttack(ATTACK_NEW_TARGET, gunship, CMD_FROM_AI);
- PartitionFilterFreeOfFog filterFogged( gunship->getControllingPlayer()->getPlayerIndex() );
- PartitionFilter *filters[6];
- Int numFilters = 0;
- filters[numFilters++] = &filterObvious;
- filters[numFilters++] = &filterStealth;
- filters[numFilters++] = &filterAttack;
- filters[numFilters++] = &filterFogged;
- filters[numFilters] = NULL;
- // THIS WILL FIND A VALID TARGET WITHIN THE TARGETING RETICLE
- ObjectIterator *iter = ThePartitionManager->iterateObjectsInRange(&m_overrideTargetDestination,
- data->m_targetingReticleRadius,
- FROM_BOUNDINGSPHERE_2D,
- filters,
- ITER_SORTED_NEAR_TO_FAR);
- MemoryPoolObjectHolder holder(iter);
- for (Object *theEnemy = iter->first(); theEnemy; theEnemy = iter->next())
- {
- if ( theEnemy && isFairDistanceFromShip( theEnemy ) )
- {
- validTargetObject = theEnemy;
- break;
- }
- }
-
- // WE WANT THE WIDE_RANGE AUTOACQUIRE POWER DISABLED FOR HUMAN PLAYERS
- // SO THAT THE SPECTREGUNSHIP REQUIRES BABYSITTING AT ALL TIMES
- if (gunship->getControllingPlayer()->getPlayerType() != PLAYER_HUMAN )
- {
- if ( ! validTargetObject )
- {
- // set a flag to start the targeting decal fading, since there is nothing to kill there
- // THIS WILL FIND A VALID TARGET ANYWHERE INSIDE THE TARGETING AREA (THE BIG CIRCLE)
- ObjectIterator *iter = ThePartitionManager->iterateObjectsInRange(&m_initialTargetPosition,
- data->m_attackAreaRadius,
- FROM_BOUNDINGSPHERE_2D,
- filters,
- ITER_SORTED_NEAR_TO_FAR);
- MemoryPoolObjectHolder holder(iter);
- for (Object *theEnemy = iter->first(); theEnemy; theEnemy = iter->next())
- {
- if ( theEnemy && isFairDistanceFromShip( theEnemy ) )
- {
- // WE GOT A HIT!!!! SHOOT HIM!
- validTargetObject = theEnemy;
- m_positionToShootAt = *validTargetObject->getPosition();
- break;
- }
- }
- }
- }
-
- //lets keep a constant barrage of gattling bullets on the current aim location
- if( gattlingAI )
- {
- if ( validTargetObject )// either in the reticle or the targeting area
- gattlingAI->aiAttackObject( validTargetObject, LOTS_OF_SHOTS, CMD_FROM_AI );
- else
- gattlingAI->aiAttackPosition( &m_gattlingTargetPosition, LOTS_OF_SHOTS, CMD_FROM_AI );
- }
- // IF THE GATTLING GUN HAS BEEN PLINKING THE TARGET LONG ENOUGH, LETS FOLLOW WITH A VOLLEY OF HOWITZER FIRE
- if ( m_okToFireHowitzerCounter > data->m_howitzerFollowLag )
- {
- WeaponTemplate *wt = data->m_howitzerWeaponTemplate;
- if( wt )
- {
- Coord3D attackPositionWithRandomOffset;
- Real offs = data->m_randomOffsetForHowitzer;
- attackPositionWithRandomOffset.x = m_gattlingTargetPosition.x + GameLogicRandomValue( -offs, offs );
- attackPositionWithRandomOffset.y = m_gattlingTargetPosition.y + GameLogicRandomValue( -offs, offs );
- attackPositionWithRandomOffset.z = m_gattlingTargetPosition.z;
- TheWeaponStore->createAndFireTempWeapon( wt, gunship, &attackPositionWithRandomOffset );
- m_howitzerFireSound.setObjectID(gunship->getID());
- TheAudio->addAudioEvent( &m_howitzerFireSound );
- }
- }
- }//endif frame modulator
- // GATTLING TARGETING LOGIC------------------------------------------
- const ParticleSystemTemplate *tmp = data->m_gattlingStrafeFXParticleSystem;
- if (tmp && gattling && gattling->testStatus( OBJECT_STATUS_IS_FIRING_WEAPON) )
- {
- // I am going to wind my gattling gun toward the next good spot,
- //whether an object's position or a cursor position
- Coord3D delta = m_positionToShootAt;
- delta.sub( &m_gattlingTargetPosition );
- Real dist = delta.length();
- if ( dist < data->m_strafingIncrement )
- {
- m_gattlingTargetPosition = m_positionToShootAt;
- ++m_okToFireHowitzerCounter;
- }
- else
- {
- m_okToFireHowitzerCounter = ZERO;
- delta.normalize();
- delta.scale( data->m_strafingIncrement );
- m_gattlingTargetPosition.add( &delta );
- }
-
- const Player *localPlayer = ThePlayerList->getLocalPlayer();
- //Make sure the gunship is visible to the player before drawing effects.
- if ( gunship->getShroudedStatus( localPlayer->getPlayerIndex() ) <= OBJECTSHROUD_PARTIAL_CLEAR )
- {
- // This makes the client smoke effects of the gattling cannon strafing the ground toward the attack position
- ParticleSystem *sys = TheParticleSystemManager->createParticleSystem(tmp);
- if (sys)
- {
- Coord3D impactPosition;
- impactPosition.x = m_gattlingTargetPosition.x + GameClientRandomValueReal( -5.0f, 5.0f );
- impactPosition.y = m_gattlingTargetPosition.y + GameClientRandomValueReal( -5.0f, 5.0f );
- impactPosition.z = TheTerrainLogic->getGroundHeight( impactPosition.x, impactPosition.y );
- sys->setPosition( &impactPosition );
- }
- }
- }
- }// end else
-
- }//not orbiting
- else if ( m_status == GUNSHIP_STATUS_DEPARTING )
- {
- if ( isPointOffMap( *gunship->getPosition() ) )
- {
-
- TheGameLogic->destroyObject( gunship );
- setLogicalStatus( GUNSHIP_STATUS_IDLE );
- cleanUp();
- // HERE WE NEED TO CLEAN UP THE TERRAIN DECALS, AND ANYTHING ELSE THAT WAS CREATED IN INIT-INTENT[]
- }
- }
- } // endif gunship
- else if ( m_status != GUNSHIP_STATUS_IDLE )
- {
- //OH MY GOODNESS, THE GUNSHIP MUST HAVE GOTTEN SHOT DOWN!
- setLogicalStatus( GUNSHIP_STATUS_IDLE );
- cleanUp();
- }
- return UPDATE_SLEEP_NONE;
- }
- //-------------------------------------------------------------------------------------------------
- void SpectreGunshipUpdate::friend_enableAfterburners(Bool v)
- {
- Object* gunship = getObject();
- if (v)
- {
- gunship->setModelConditionState(MODELCONDITION_JETAFTERBURNER);
- if (!m_afterburnerSound.isCurrentlyPlaying())
- {
- m_afterburnerSound.setObjectID(gunship->getID());
- m_afterburnerSound.setPlayingHandle(TheAudio->addAudioEvent(&m_afterburnerSound));
- }
- }
- else
- {
- gunship->clearModelConditionState(MODELCONDITION_JETAFTERBURNER);
- if (m_afterburnerSound.isCurrentlyPlaying())
- {
- TheAudio->removeAudioEvent(m_afterburnerSound.getPlayingHandle());
- }
- }
- }
- Bool SpectreGunshipUpdate::isFairDistanceFromShip( Object *target )
- {
- Object *gunship = getObject();
- if ( !gunship )
- return FALSE;
- if ( ! target )
- return FALSE;
- const Coord3D *targetPosition = target->getPosition();
- const Coord3D *gunshipPosition = gunship->getPosition();
- Coord3D shipToTargetDelta;
- shipToTargetDelta.x = gunshipPosition->x - targetPosition->x;
- shipToTargetDelta.y = gunshipPosition->y - targetPosition->y;
- shipToTargetDelta.z = 0.0f;
- return (shipToTargetDelta.length() > getSpectreGunshipUpdateModuleData()->m_gunshipOrbitRadius * 0.75f );
- }
- //-------------------------------------------------------------------------------------------------
- void SpectreGunshipUpdate::cleanUp()
- {
- m_attackAreaDecal.clear();
- m_targetingReticleDecal.clear();
- Object *gattling = TheGameLogic->findObjectByID( m_gattlingID );
- if ( gattling )
- TheGameLogic->destroyObject( gattling );
- #if defined TRACKERS
- m_howitzerTrackerDecal.clear();
- #endif
- }
- // --------------------------------------------------------------------------------------------------
- void SpectreGunshipUpdate::disengageAndDepartAO( Object *gunship )
- {
- if ( gunship == NULL )
- return;
- AIUpdateInterface *shipAI = gunship->getAIUpdateInterface();
- if ( shipAI)
- {
- Coord3D exitPoint;// head off the map in the direction you are facing
- gunship->getUnitDirectionVector3D( exitPoint );
- Real mapSize = 99999.0f;
- exitPoint.x *= mapSize;
- exitPoint.y *= mapSize;
- exitPoint.add( gunship->getPosition() );
- shipAI->aiMoveToPosition( &exitPoint, CMD_FROM_AI );
-
- }
- Object *gattling = TheGameLogic->findObjectByID( m_gattlingID );
- if ( gattling )
- gattling->setDisabled ( DISABLED_PARALYZED );
- if ( shipAI)
- {
- shipAI->chooseLocomotorSet( LOCOMOTORSET_PANIC );
- shipAI->getCurLocomotor()->setAllowInvalidPosition(TRUE);
- shipAI->getCurLocomotor()->setUltraAccurate(TRUE); // set ultra-accurate just so AI won't try to adjust our dest
- }
- Drawable *draw = gunship->getDrawable();
- if ( draw )
- draw->clearAndSetModelConditionState( MODELCONDITION_DOOR_1_OPENING, MODELCONDITION_DOOR_1_CLOSING );
- friend_enableAfterburners(TRUE);
- cleanUp();
- return;
- }
- //-------------------------------------------------------------------------------------------------
- Bool SpectreGunshipUpdate::doesSpecialPowerHaveOverridableDestinationActive() const
- {
- return m_status < GUNSHIP_STATUS_DEPARTING;
- }
- // ------------------------------------------------------------------------------------------------
- /** CRC */
- // ------------------------------------------------------------------------------------------------
- void SpectreGunshipUpdate::crc( Xfer *xfer )
- {
- // extend base class
- UpdateModule::crc( xfer );
- } // end crc
- // ------------------------------------------------------------------------------------------------
- /** Xfer method
- * Version Info:
- * 1: Initial version
- * 2: Can't flat-save decals, that's a hella crash (they aren't saved (no memory) and they have a pointer in them). And half the class wasn't saved.
- */
- // ------------------------------------------------------------------------------------------------
- void SpectreGunshipUpdate::xfer( Xfer *xfer )
- {
- // const SpectreGunshipUpdateModuleData *data = getSpectreGunshipUpdateModuleData();
- // version
- XferVersion currentVersion = 2;
- XferVersion version = currentVersion;
- xfer->xferVersion( &version, currentVersion );
- // extend base class
- UpdateModule::xfer( xfer );
-
-
- // The initial target destination.
- xfer->xferCoord3D( &m_initialTargetPosition );
- // The manual override target destination.
- xfer->xferCoord3D( &m_overrideTargetDestination );
- // The current move-to point of the gunship.
- xfer->xferCoord3D( &m_satellitePosition );
- // status
- xfer->xferUser( &m_status, sizeof( GunshipStatus ) );
- xfer->xferUnsignedInt( &m_orbitEscapeFrame );
- if( version < 2 )
- {
- xfer->xferUser( &m_attackAreaDecal, sizeof( RadiusDecal ) );
- xfer->xferUser( &m_targetingReticleDecal, sizeof( RadiusDecal ) );
-
- #if defined TRACKERS
- xfer->xferUser( &m_howitzerTrackerDecal, sizeof( RadiusDecal ) );
- #endif
- }
- if( version >= 2 )
- {
- xfer->xferCoord3D( &m_gattlingTargetPosition );
- xfer->xferCoord3D( &m_positionToShootAt );
- xfer->xferUnsignedInt( &m_okToFireHowitzerCounter );
- xfer->xferObjectID( &m_gattlingID );
- }
- } // end xfer
- // ------------------------------------------------------------------------------------------------
- /** Load post process */
- // ------------------------------------------------------------------------------------------------
- void SpectreGunshipUpdate::loadPostProcess( void )
- {
- // extend base class
- UpdateModule::loadPostProcess();
- } // end loadPostProcess
|