| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474 |
- /*
- ** 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: ParticleUplinkCannonUpdate.cpp //////////////////////////////////////////////////////////////////////////
- // Author: Kris Morness, September 2002
- // Desc: Update module to handle building states and weapon firing of the particle uplink cannon.
- ///////////////////////////////////////////////////////////////////////////////////////////////////
- #include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
- #define DEFINE_DEATH_NAMES
- // INCLUDES ///////////////////////////////////////////////////////////////////////////////////////
- #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 "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\ParticleUplinkCannonUpdate.h"
- #include "GameLogic\Module\PhysicsUpdate.h"
- #include "GameLogic\Module\LaserUpdate.h"
- #include "GameLogic\Module\ActiveBody.h"
- #ifdef _INTERNAL
- // for occasional debugging...
- //#pragma optimize("", off)
- //#pragma MESSAGE("************************************** WARNING, optimization disabled for debugging purposes")
- #endif
- //-------------------------------------------------------------------------------------------------
- //-------------------------------------------------------------------------------------------------
- ParticleUplinkCannonUpdateModuleData::ParticleUplinkCannonUpdateModuleData()
- {
- m_specialPowerTemplate = NULL;
- m_beginChargeFrames = 0;
- m_raiseAntennaFrames = 0;
- m_readyDelayFrames = 0;
- m_outerEffectNumBones = 0;
- m_beamTravelFrames = 0;
- m_widthGrowFrames = 0;
- m_totalFiringFrames = 0;
- m_totalScorchMarks = 0;
- m_scorchMarkScalar = 1.0f;
- m_damageRadiusScalar = 1.0f;
- m_groundHitFX = NULL;
- m_beamLaunchFX = NULL;
- m_framesBetweenLaunchFXRefresh = 30;
- m_totalDamagePulses = 0;
- m_damagePerSecond = 0.0f;
- m_damageType = DAMAGE_LASER;
- m_deathType = DEATH_LASERED;
- m_revealRange = 0.0f;
- m_manualDrivingSpeed = 0.0f;
- m_manualFastDrivingSpeed = 0.0f;
- m_doubleClickToFastDriveDelay = 500;
- //Added by Sadullah Nader
- //Initializations inserted
- m_swathOfDeathAmplitude = 0.0f;
- m_swathOfDeathDistance = 0.0f;
- //
- }
- //-------------------------------------------------------------------------------------------------
- /*static*/ void ParticleUplinkCannonUpdateModuleData::buildFieldParse(MultiIniFieldParse& p)
- {
- ModuleData::buildFieldParse(p);
- static const FieldParse dataFieldParse[] =
- {
- { "SpecialPowerTemplate", INI::parseSpecialPowerTemplate, NULL, offsetof( ParticleUplinkCannonUpdateModuleData, m_specialPowerTemplate ) },
- { "BeginChargeTime", INI::parseDurationUnsignedInt, NULL, offsetof( ParticleUplinkCannonUpdateModuleData, m_beginChargeFrames ) },
- { "RaiseAntennaTime", INI::parseDurationUnsignedInt, NULL, offsetof( ParticleUplinkCannonUpdateModuleData, m_raiseAntennaFrames ) },
- { "ReadyDelayTime", INI::parseDurationUnsignedInt, NULL, offsetof( ParticleUplinkCannonUpdateModuleData, m_readyDelayFrames ) },
- { "WidthGrowTime", INI::parseDurationUnsignedInt, NULL, offsetof( ParticleUplinkCannonUpdateModuleData, m_widthGrowFrames ) },
- { "BeamTravelTime", INI::parseDurationUnsignedInt, NULL, offsetof( ParticleUplinkCannonUpdateModuleData, m_beamTravelFrames ) },
- { "TotalFiringTime", INI::parseDurationUnsignedInt, NULL, offsetof( ParticleUplinkCannonUpdateModuleData, m_totalFiringFrames ) },
- { "RevealRange", INI::parseReal, NULL, offsetof( ParticleUplinkCannonUpdateModuleData, m_revealRange ) },
-
- { "OuterEffectBoneName", INI::parseAsciiString, NULL, offsetof( ParticleUplinkCannonUpdateModuleData, m_outerEffectBaseBoneName ) },
- { "OuterEffectNumBones", INI::parseUnsignedInt, NULL, offsetof( ParticleUplinkCannonUpdateModuleData, m_outerEffectNumBones ) },
- { "OuterNodesLightFlareParticleSystem", INI::parseAsciiString, NULL, offsetof( ParticleUplinkCannonUpdateModuleData, m_outerNodesLightFlareParticleSystemName ) },
- { "OuterNodesMediumFlareParticleSystem", INI::parseAsciiString, NULL, offsetof( ParticleUplinkCannonUpdateModuleData, m_outerNodesMediumFlareParticleSystemName ) },
- { "OuterNodesIntenseFlareParticleSystem", INI::parseAsciiString, NULL, offsetof( ParticleUplinkCannonUpdateModuleData, m_outerNodesIntenseFlareParticleSystemName ) },
- { "ConnectorBoneName", INI::parseAsciiString, NULL, offsetof( ParticleUplinkCannonUpdateModuleData, m_connectorBoneName ) },
- { "ConnectorMediumLaserName", INI::parseAsciiString, NULL, offsetof( ParticleUplinkCannonUpdateModuleData, m_connectorMediumLaserNameName ) },
- { "ConnectorIntenseLaserName", INI::parseAsciiString, NULL, offsetof( ParticleUplinkCannonUpdateModuleData, m_connectorIntenseLaserNameName ) },
- { "ConnectorMediumFlare", INI::parseAsciiString, NULL, offsetof( ParticleUplinkCannonUpdateModuleData, m_connectorMediumFlareParticleSystemName ) },
- { "ConnectorIntenseFlare", INI::parseAsciiString, NULL, offsetof( ParticleUplinkCannonUpdateModuleData, m_connectorIntenseFlareParticleSystemName ) },
- { "FireBoneName", INI::parseAsciiString, NULL, offsetof( ParticleUplinkCannonUpdateModuleData, m_fireBoneName ) },
- { "LaserBaseLightFlareParticleSystemName", INI::parseAsciiString, NULL, offsetof( ParticleUplinkCannonUpdateModuleData, m_laserBaseLightFlareParticleSystemName ) },
- { "LaserBaseMediumFlareParticleSystemName", INI::parseAsciiString, NULL, offsetof( ParticleUplinkCannonUpdateModuleData, m_laserBaseMediumFlareParticleSystemName ) },
- { "LaserBaseIntenseFlareParticleSystemName", INI::parseAsciiString, NULL, offsetof( ParticleUplinkCannonUpdateModuleData, m_laserBaseIntenseFlareParticleSystemName ) },
- { "ParticleBeamLaserName", INI::parseAsciiString, NULL, offsetof( ParticleUplinkCannonUpdateModuleData, m_particleBeamLaserName ) },
- { "SwathOfDeathDistance", INI::parseReal, NULL, offsetof( ParticleUplinkCannonUpdateModuleData, m_swathOfDeathDistance ) },
- { "SwathOfDeathAmplitude", INI::parseReal, NULL, offsetof( ParticleUplinkCannonUpdateModuleData, m_swathOfDeathAmplitude ) },
- { "TotalScorchMarks", INI::parseUnsignedInt, NULL, offsetof( ParticleUplinkCannonUpdateModuleData, m_totalScorchMarks ) },
- { "ScorchMarkScalar", INI::parseReal, NULL, offsetof( ParticleUplinkCannonUpdateModuleData, m_scorchMarkScalar ) },
- { "BeamLaunchFX", INI::parseFXList, NULL, offsetof( ParticleUplinkCannonUpdateModuleData, m_beamLaunchFX ) },
- { "DelayBetweenLaunchFX", INI::parseDurationUnsignedInt, NULL, offsetof( ParticleUplinkCannonUpdateModuleData, m_framesBetweenLaunchFXRefresh ) },
- { "GroundHitFX", INI::parseFXList, NULL, offsetof( ParticleUplinkCannonUpdateModuleData, m_groundHitFX ) },
- { "DamagePerSecond", INI::parseReal, NULL, offsetof( ParticleUplinkCannonUpdateModuleData, m_damagePerSecond ) },
- { "TotalDamagePulses", INI::parseUnsignedInt, NULL, offsetof( ParticleUplinkCannonUpdateModuleData, m_totalDamagePulses ) },
- { "DamageType", DamageTypeFlags::parseSingleBitFromINI, NULL, offsetof( ParticleUplinkCannonUpdateModuleData, m_damageType ) },
- { "DeathType", INI::parseIndexList, TheDeathNames, offsetof( ParticleUplinkCannonUpdateModuleData, m_deathType ) },
- { "DamageRadiusScalar", INI::parseReal, NULL, offsetof( ParticleUplinkCannonUpdateModuleData, m_damageRadiusScalar ) },
- { "PoweringUpSoundLoop", INI::parseAsciiString, NULL, offsetof( ParticleUplinkCannonUpdateModuleData, m_powerupSoundName ) },
- { "UnpackToIdleSoundLoop", INI::parseAsciiString, NULL, offsetof( ParticleUplinkCannonUpdateModuleData, m_unpackToReadySoundName ) },
- { "FiringToPackSoundLoop", INI::parseAsciiString, NULL, offsetof( ParticleUplinkCannonUpdateModuleData, m_firingToIdleSoundName ) },
- { "GroundAnnihilationSoundLoop", INI::parseAsciiString, NULL, offsetof( ParticleUplinkCannonUpdateModuleData, m_annihilationSoundName ) },
- { "DamagePulseRemnantObjectName", INI::parseAsciiString, NULL, offsetof( ParticleUplinkCannonUpdateModuleData, m_damagePulseRemnantObjectName ) },
- { "ManualDrivingSpeed", INI::parseReal, NULL, offsetof( ParticleUplinkCannonUpdateModuleData, m_manualDrivingSpeed ) },
- { "ManualFastDrivingSpeed", INI::parseReal, NULL, offsetof( ParticleUplinkCannonUpdateModuleData, m_manualFastDrivingSpeed ) },
- { "DoubleClickToFastDriveDelay", INI::parseDurationUnsignedInt, NULL, offsetof( ParticleUplinkCannonUpdateModuleData, m_doubleClickToFastDriveDelay ) },
- { 0, 0, 0, 0 }
- };
- p.add(dataFieldParse);
- }
- //-------------------------------------------------------------------------------------------------
- ParticleUplinkCannonUpdate::ParticleUplinkCannonUpdate( Thing *thing, const ModuleData* moduleData ) : SpecialPowerUpdateModule( thing, moduleData )
- {
- m_status = STATUS_IDLE;
- m_laserStatus = LASERSTATUS_NONE;
- m_frames = 0;
- m_specialPowerModule = NULL;
- m_groundToOrbitBeamID = INVALID_DRAWABLE_ID;
- m_orbitToTargetBeamID = INVALID_DRAWABLE_ID;
- m_connectorSystemID = INVALID_PARTICLE_SYSTEM_ID;
- m_laserBaseSystemID = INVALID_PARTICLE_SYSTEM_ID;
- m_connectorNodePosition.zero();
- m_laserOriginPosition.zero();
- m_upBonesCached = FALSE;
- m_defaultInfoCached = FALSE;
- m_invalidSettings = FALSE;
- m_manualTargetMode = FALSE;
- m_scriptedWaypointMode = FALSE;
- m_nextDestWaypointID = 0;
- m_initialTargetPosition.zero();
- m_currentTargetPosition.zero();
- m_overrideTargetDestination.zero();
- m_scorchMarksMade= 0;
- m_nextScorchMarkFrame = 0;
- m_nextLaunchFXFrame = 0;
- m_damagePulsesMade = 0;
- m_nextDamagePulseFrame = 0;
- m_startAttackFrame = 0;
- m_startDecayFrame = 0;
- m_lastDrivingClickFrame = 0;
- m_2ndLastDrivingClickFrame = 0;
- m_clientShroudedLastFrame = FALSE;
- for( Int i = 0; i < MAX_OUTER_NODES; i++ )
- {
- m_outerSystemIDs[ i ] = INVALID_PARTICLE_SYSTEM_ID;
- m_laserBeamIDs[ i ] = INVALID_DRAWABLE_ID;
- //Initializing (even though they will get cached properly).
- m_outerNodePositions[ i ].zero();
- m_outerNodeOrientations[ i ].Make_Identity();
- }
- }
- //-------------------------------------------------------------------------------------------------
- //-------------------------------------------------------------------------------------------------
- void ParticleUplinkCannonUpdate::killEverything()
- {
- removeAllEffects();
- //This laser is independent from the other effects and needs to be specially handled.
- if( m_orbitToTargetBeamID )
- {
- Drawable *beam = TheGameClient->findDrawableByID( m_orbitToTargetBeamID );
- if( beam )
- {
- TheGameClient->destroyDrawable( beam );
- }
- m_orbitToTargetBeamID = INVALID_DRAWABLE_ID;
- }
- TheAudio->removeAudioEvent( m_powerupSound.getPlayingHandle() );
- TheAudio->removeAudioEvent( m_unpackToReadySound.getPlayingHandle() );
- TheAudio->removeAudioEvent( m_firingToIdleSound.getPlayingHandle() );
- TheAudio->removeAudioEvent( m_annihilationSound.getPlayingHandle() );
- }
- //-------------------------------------------------------------------------------------------------
- //-------------------------------------------------------------------------------------------------
- ParticleUplinkCannonUpdate::~ParticleUplinkCannonUpdate( void )
- {
- killEverything();
- }
- //-------------------------------------------------------------------------------------------------
- // Validate that we have the necessary data from the ini file.
- //-------------------------------------------------------------------------------------------------
- void ParticleUplinkCannonUpdate::onObjectCreated()
- {
- const ParticleUplinkCannonUpdateModuleData *data = getParticleUplinkCannonUpdateModuleData();
- Object *obj = getObject();
- if( !data->m_specialPowerTemplate )
- {
- DEBUG_CRASH( ("%s object's ParticleUplinkCannonUpdate lacks access to the SpecialPowerTemplate. Needs to be specified in ini.", obj->getTemplate()->getName().str() ) );
- m_invalidSettings = TRUE;
- return;
- }
- m_specialPowerModule = obj->getSpecialPowerModule( data->m_specialPowerTemplate );
- m_connectorNodePosition.set( obj->getPosition() );
- m_laserOriginPosition.set( obj->getPosition() );
- //Create instances of the sounds required.
- m_powerupSound.setEventName( data->m_powerupSoundName );
- m_unpackToReadySound.setEventName( data->m_unpackToReadySoundName );
- m_firingToIdleSound.setEventName( data->m_firingToIdleSoundName );
- m_annihilationSound.setEventName( data->m_annihilationSoundName );
- TheAudio->getInfoForAudioEvent( &m_powerupSound );
- TheAudio->getInfoForAudioEvent( &m_unpackToReadySound );
- TheAudio->getInfoForAudioEvent( &m_firingToIdleSound );
- TheAudio->getInfoForAudioEvent( &m_annihilationSound );
- }
- //-------------------------------------------------------------------------------------------------
- Bool ParticleUplinkCannonUpdate::initiateIntentToDoSpecialPower(const SpecialPowerTemplate *specialPowerTemplate, const Object *targetObj, const Coord3D *targetPos, const Waypoint *way, UnsignedInt commandOptions )
- {
- const ParticleUplinkCannonUpdateModuleData *data = getParticleUplinkCannonUpdateModuleData();
- if( m_specialPowerModule->getSpecialPowerTemplate() != specialPowerTemplate )
- {
- //Check to make sure our modules are connected.
- return FALSE;
- }
- // getObject()->getControllingPlayer()->getAcademyStats()->recordSpecialPowerUsed( specialPowerTemplate );
- if( !BitTest( commandOptions, COMMAND_FIRED_BY_SCRIPT ) )
- {
- //All human players have manual control and must "drive" the beam around!
- m_startAttackFrame = TheGameLogic->getFrame();
- m_laserStatus = LASERSTATUS_NONE;
- m_manualTargetMode = TRUE;
- m_initialTargetPosition.set( targetPos );
- m_overrideTargetDestination.set( targetPos );
- m_currentTargetPosition.set( targetPos );
- }
- else if( way )
- {
- //Script fired with a specific waypoint path, so set things up to follow the waypoint!
- UnsignedInt now = TheGameLogic->getFrame();
-
- Coord3D pos;
- if( way )
- {
- pos.set( way->getLocation() );
- }
- else if( targetObj )
- {
- pos.set( targetObj->getPosition() );
- }
- m_startAttackFrame = max( now, (UnsignedInt)1 );
- m_scriptedWaypointMode = TRUE;
- m_laserStatus = LASERSTATUS_NONE;
- setLogicalStatus( STATUS_READY_TO_FIRE );
- m_specialPowerModule->setReadyFrame( now );
- m_initialTargetPosition.set( &pos );
- m_currentTargetPosition.set( &pos );
- m_nextDestWaypointID = way->getID();
- Int linkCount = way->getNumLinks();
- Int which = GameLogicRandomValue( 0, linkCount-1 );
- Waypoint *next = way->getLink( which );
- if( next )
- {
- m_nextDestWaypointID = next->getID();
- m_overrideTargetDestination.set( next->getLocation() );
- }
- }
- else
- {
- //All computer controlled players have automatic control -- the "S" curve.
- UnsignedInt now = TheGameLogic->getFrame();
-
- Coord3D pos;
- if( targetPos )
- {
- pos.set( targetPos );
- }
- else if( targetObj )
- {
- pos.set( targetObj->getPosition() );
- }
- m_initialTargetPosition.set( &pos );
- m_startAttackFrame = max( now, (UnsignedInt)1 );
- m_laserStatus = LASERSTATUS_NONE;
- setLogicalStatus( STATUS_READY_TO_FIRE );
- m_specialPowerModule->setReadyFrame( now );
- }
- m_startDecayFrame = m_startAttackFrame + data->m_totalFiringFrames;
- SpecialPowerModuleInterface *spmInterface = getObject()->getSpecialPowerModule( specialPowerTemplate );
- if( spmInterface )
- {
- SpecialPowerModule *spModule = (SpecialPowerModule*)spmInterface;
- spModule->markSpecialPowerTriggered( &m_initialTargetPosition );
- }
- return TRUE;
- }
- //-------------------------------------------------------------------------------------------------
- Bool ParticleUplinkCannonUpdate::isPowerCurrentlyInUse( const CommandButton *command ) const
- {
- if( m_startAttackFrame != 0 && m_startAttackFrame <= TheGameLogic->getFrame() )
- {
- return TRUE;
- }
- return FALSE;
- }
- //-------------------------------------------------------------------------------------------------
- void ParticleUplinkCannonUpdate::setSpecialPowerOverridableDestination( const Coord3D *loc )
- {
- if( !getObject()->isDisabled() )
- {
- m_overrideTargetDestination = *loc;
- m_manualTargetMode = TRUE;
- m_2ndLastDrivingClickFrame = m_lastDrivingClickFrame;
- m_lastDrivingClickFrame = TheGameLogic->getFrame();
- }
- }
- //-------------------------------------------------------------------------------------------------
- /** The update callback. */
- //-------------------------------------------------------------------------------------------------
- UpdateSleepTime ParticleUplinkCannonUpdate::update()
- {
- if( m_invalidSettings )
- {
- // can't return UPDATE_SLEEP_FOREVER unless we are sleepy...
- return UPDATE_SLEEP_NONE;
- ///return UPDATE_SLEEP_FOREVER;
- }
- const ParticleUplinkCannonUpdateModuleData *data = getParticleUplinkCannonUpdateModuleData();
- Object *me = getObject();
- // must test SOLD *before* the others.
- if( me->testStatus(OBJECT_STATUS_SOLD))
- {
- if (m_status != STATUS_IDLE)
- {
- setLogicalStatus( STATUS_IDLE );
- killEverything();
- }
- return UPDATE_SLEEP_NONE;
- }
- if( me->testStatus(OBJECT_STATUS_UNDER_CONSTRUCTION) )
- {
- return UPDATE_SLEEP_NONE;
- }
- if( me->isEffectivelyDead() )
- {
- return UPDATE_SLEEP_NONE;
- }
- //Check to see what our status is -- there are a couple:
- //1) Idle while waiting to get near the time when we should be preparing to be ready.
- //2) If superweapon delay is nuked (cheat) then we need to make the building ready now.
- UnsignedInt now = TheGameLogic->getFrame();
- UnsignedInt readyToFireFrame = m_specialPowerModule->isReady() ? now : m_specialPowerModule->getReadyFrame();
- UnsignedInt almostReadyFrame = readyToFireFrame - data->m_readyDelayFrames;
- UnsignedInt raiseAntennaFrame = almostReadyFrame - data->m_raiseAntennaFrames;
- UnsignedInt beginChargeFrame = raiseAntennaFrame - data->m_beginChargeFrames;
- if( m_startAttackFrame != 0 && m_startAttackFrame <= now )
- {
- if( m_startDecayFrame > now )
- {
- if( me->isDisabledByType( DISABLED_UNDERPOWERED ) ||
- me->isDisabledByType( DISABLED_EMP ) ||
- me->isDisabledByType( DISABLED_SUBDUED ) ||
- me->isDisabledByType( DISABLED_HACKED ) )
- {
- //We must end the special power early! ABORT! ABORT!
- m_startDecayFrame = now;
- }
- }
- UnsignedInt endDecayFrame = m_startDecayFrame + data->m_widthGrowFrames;
- UnsignedInt orbitalBirthFrame = m_startAttackFrame + data->m_beamTravelFrames;
- UnsignedInt orbitalDecayStart = m_startDecayFrame + data->m_beamTravelFrames;
- UnsignedInt orbitalDeathFrame = orbitalDecayStart + data->m_widthGrowFrames;
- switch( m_laserStatus )
- {
- case LASERSTATUS_NONE:
- //Means we are eligible to fire!
- if( orbitalBirthFrame <= now )
- {
- createOrbitToTargetLaser( data->m_widthGrowFrames );
- m_laserStatus = LASERSTATUS_BORN;
- m_scorchMarksMade = 0;
- m_nextScorchMarkFrame = now;
- m_damagePulsesMade = 0;
- m_nextDamagePulseFrame = now;
- }
- break;
- case LASERSTATUS_BORN:
- {
- Drawable *beam = TheGameClient->findDrawableByID( m_orbitToTargetBeamID );
- if( beam )
- {
- //m_annihilationSound.setPosition( beam->getPosition() );
- if( orbitalDecayStart <= now )
- {
- static NameKeyType nameKeyClientUpdate = NAMEKEY( "LaserUpdate" );
- LaserUpdate *update = (LaserUpdate*)beam->findClientUpdateModule( nameKeyClientUpdate );
- if( update )
- {
- update->setDecayFrames( data->m_widthGrowFrames );
- }
- m_laserStatus = LASERSTATUS_DECAYING;
- }
- }
- break;
- }
- case LASERSTATUS_DECAYING:
- {
- Drawable *beam = TheGameClient->findDrawableByID( m_orbitToTargetBeamID );
- if( beam )
- {
- //m_annihilationSound.setPosition( beam->getPosition() );
- TheAudio->removeAudioEvent( m_annihilationSound.getPlayingHandle() );
- if( orbitalDeathFrame <= now )
- {
- TheGameClient->destroyDrawable( beam );
- m_orbitToTargetBeamID = INVALID_DRAWABLE_ID;
- m_laserStatus = LASERSTATUS_DEAD;
- m_startAttackFrame = 0;
- setLogicalStatus( STATUS_IDLE );
- }
- }
- break;
- }
- case LASERSTATUS_DEAD:
- //Nothing... this is just a wait state.
- break;
- }
- Drawable *beam = TheGameClient->findDrawableByID( m_orbitToTargetBeamID );
- if( beam && orbitalBirthFrame <= now && now <= orbitalDeathFrame )
- {
-
- if( !m_manualTargetMode && !m_scriptedWaypointMode )
- {
- //Calculate the position of the beam because it swaths -- a nice S curve centering at the target location!
-
- //First determine the factor of time completed (ranges between 0.0 and 1.0)
- Real factor = (Real)(now - orbitalBirthFrame) / (Real)(orbitalDeathFrame - orbitalBirthFrame);
- //We're generating a swath that travels the points between sin( -1PI ) and sin( 1PI )
- Real radians = (factor * TWO_PI) - PI;
- Real cxDistance = (factor * data->m_swathOfDeathDistance ) - (data->m_swathOfDeathDistance * 0.5f); //cx is cartesian x
- //Now calculate the amplitude value.
- Real height = sin( radians );
- Real cxHeight = height * data->m_swathOfDeathAmplitude;
- Coord3D buildingToInitialTargetVector;
- buildingToInitialTargetVector.set( &m_initialTargetPosition );
- buildingToInitialTargetVector.sub( me->getPosition() );
- Real targetDistance = buildingToInitialTargetVector.length();
- //Calculate the point position assuming the target position is on the x axis relative to the building.
- m_currentTargetPosition.x = cxDistance + targetDistance;
- m_currentTargetPosition.y = cxHeight;
- m_currentTargetPosition.z = 0.0f;
- targetDistance = m_currentTargetPosition.length();
- //Now that we have our cartesian offset relative to the target coordinate, we need to rotate that offset
- //so it's aligned to along the building -> target vector.
- Vector2 buildingToTargetVector( m_initialTargetPosition.x - me->getPosition()->x, m_initialTargetPosition.y - me->getPosition()->y );
- buildingToTargetVector.Normalize();
- Vector2 cartesianTargetVector( m_currentTargetPosition.x, m_currentTargetPosition.y );
- cartesianTargetVector.Normalize();
- Real dotProduct = Vector2::Dot_Product( buildingToTargetVector, cartesianTargetVector );
- dotProduct = __min( 0.99999f, __max( -0.99999f, dotProduct ) ); //Account for numerical errors. Also, acos(-1.00000) is coming out QNAN on the superweapon general map. Heh.
- Real angle = (Real)ACos( dotProduct );
- if( buildingToTargetVector.Y >= 0 )
- {
- angle = -angle;
- }
- Matrix3D mtx( Vector3( 1.0f, 0.0f, 0.0f ) );
- mtx.Scale( targetDistance );
- mtx.Rotate_Z( -angle );
- Vector3 v = mtx.Get_X_Vector();
- m_currentTargetPosition.x = me->getPosition()->x + v.X;
- m_currentTargetPosition.y = me->getPosition()->y + v.Y;
- }
- else
- {
- Real speed = data->m_manualDrivingSpeed;
- if( m_scriptedWaypointMode || m_lastDrivingClickFrame - m_2ndLastDrivingClickFrame < data->m_doubleClickToFastDriveDelay )
- {
- //Because we double clicked, use the faster driving speed.
- speed = data->m_manualFastDrivingSpeed;
- }
- //Convert speed to speed per frame.
- speed /= LOGICFRAMES_PER_SECOND;
- //Calculate the distance from our current position to our target dest.
- Coord3D vector = m_overrideTargetDestination;
- vector.sub( &m_currentTargetPosition );
- Real distance = vector.length();
- if( distance < speed )
- {
- //Don't allow the speed to overshoot the target point if close.
- speed = distance;
- //If we're in scripted waypoint mode, go to the next waypoint!
- if( m_scriptedWaypointMode )
- {
- Waypoint *way = TheTerrainLogic->getWaypointByID( m_nextDestWaypointID );
- if( way )
- {
- //Advance to the next waypoint.
- Int linkCount = way->getNumLinks();
- Int which = GameLogicRandomValue( 0, linkCount-1 );
- way = way->getLink( which );
- m_nextDestWaypointID = way->getID();
- m_overrideTargetDestination.set( way->getLocation() );
- }
- }
- }
- //Unitize the vector then apply the distance we will move.
- vector.normalize();
- vector.scale( speed );
- //Move the current target position in the desired direction and speed.
- m_currentTargetPosition.x += vector.x;
- m_currentTargetPosition.y += vector.y;
- }
- //Regardless of which method we used to set the target position, make sure the z position is at the terrain.
- m_currentTargetPosition.z = TheTerrainLogic->getGroundHeight( m_currentTargetPosition.x, m_currentTargetPosition.y );
- Coord3D orbitPosition;
- orbitPosition.set( &m_currentTargetPosition );
- orbitPosition.z += 500.0f;
- Real scorchRadius = 0.0f;
- Real damageRadius = 0.0f;
- //Reset the laser position
- static NameKeyType nameKeyClientUpdate = NAMEKEY( "LaserUpdate" );
- LaserUpdate *update = (LaserUpdate*)beam->findClientUpdateModule( nameKeyClientUpdate );
- if( update )
- {
- update->initLaser( NULL, NULL, &orbitPosition, &m_currentTargetPosition, "" );
- scorchRadius = update->getCurrentLaserRadius() * data->m_scorchMarkScalar;
- damageRadius = update->getCurrentLaserRadius() * data->m_damageRadiusScalar;
- }
- //Create scorch marks periodically
- if( m_nextScorchMarkFrame <= now )
- {
- m_scorchMarksMade++;
- //Create the scorch mark now!
- Scorches scorchID = (Scorches)GameClientRandomValue( SCORCH_1, SCORCH_4 ); //Yes, this is just client fluff!
- TheGameClient->addScorch( &m_currentTargetPosition, scorchRadius, scorchID );
- //Calculate next scorch mark frame.
- Real nextFactor = (Real)m_scorchMarksMade / (Real)data->m_totalScorchMarks;
- m_nextScorchMarkFrame = orbitalBirthFrame + nextFactor * (orbitalDeathFrame - orbitalBirthFrame);
- //Generate iteration of fxlist for beam hitting ground.
- if( data->m_groundHitFX )
- {
- FXList::doFXPos( data->m_groundHitFX, &m_currentTargetPosition, NULL );
- }
- //Also reveal vision because the owning player has full rights to watch the carnage he created!
- ThePartitionManager->doShroudReveal( m_currentTargetPosition.x, m_currentTargetPosition.y, data->m_revealRange, me->getControllingPlayer()->getPlayerMask() );
- ThePartitionManager->undoShroudReveal( m_currentTargetPosition.x, m_currentTargetPosition.y, data->m_revealRange, me->getControllingPlayer()->getPlayerMask() );
- }
- //Handle damage pulses
- if( m_nextDamagePulseFrame <= now )
- {
- m_damagePulsesMade++;
- DamageInfo damageInfo;
- Real totalFiringSeconds = data->m_totalFiringFrames / LOGICFRAMES_PER_SECOND;
- Real damagePerPulse = (Real)(totalFiringSeconds * data->m_damagePerSecond) / (Real)data->m_totalDamagePulses;
- damageInfo.in.m_amount = damagePerPulse;
- damageInfo.in.m_sourceID = me->getID();
- damageInfo.in.m_damageType = data->m_damageType;
- damageInfo.in.m_deathType = data->m_deathType;
- PartitionFilterAlive filterAlive;
- PartitionFilter *filters[] = { &filterAlive, NULL };
- ObjectIterator *iter = ThePartitionManager->iterateObjectsInRange( &m_currentTargetPosition, damageRadius, FROM_CENTER_2D, filters );
- MemoryPoolObjectHolder hold( iter );
- for( Object *obj = iter->first(); obj; obj = iter->next() )
- {
- BodyModuleInterface *body = obj->getBodyModule();
- if( body )
- {
- body->attemptDamage( &damageInfo );
- }
- }
- if( data->m_damagePulseRemnantObjectName.isNotEmpty() )
- {
- //Create a remnant damaging object that will fade over time to represent the burning trail.
- const ThingTemplate *thing = TheThingFactory->findTemplate( data->m_damagePulseRemnantObjectName );
- if( thing )
- {
- //Fire and forget
- Object *remnant = TheThingFactory->newObject( thing, me->getTeam() );
- if( remnant )
- {
- remnant->setPosition( &m_currentTargetPosition );
- }
- }
- }
- //Calculate next damage pulse frame.
- Real nextFactor = (Real)m_damagePulsesMade / (Real)data->m_totalDamagePulses;
- m_nextDamagePulseFrame = orbitalBirthFrame + nextFactor * (orbitalDeathFrame - orbitalBirthFrame);
- }
- }
-
- if( endDecayFrame <= now )
- {
- setLogicalStatus( STATUS_PACKING );
- }
- else if( m_startDecayFrame <= now )
- {
- setLogicalStatus( STATUS_POSTFIRE );
- }
- else
- {
- setLogicalStatus( STATUS_FIRING );
- }
- }
- else if( readyToFireFrame <= now )
- {
- setLogicalStatus( STATUS_READY_TO_FIRE );
- }
- else if( almostReadyFrame <= now )
- {
- setLogicalStatus( STATUS_ALMOST_READY );
- }
- else if( raiseAntennaFrame <= now )
- {
- setLogicalStatus( STATUS_PREPARING );
- }
- else if( beginChargeFrame <= now )
- {
- setLogicalStatus( STATUS_CHARGING );
- }
- else if( m_status == STATUS_ALMOST_READY )
- {
- }
- else if( m_status == STATUS_READY_TO_FIRE )
- {
- //The particle cannon timer has been reset (sabotaged?)
- //If so, when ready, pack it up again.
- setLogicalStatus( STATUS_PACKING );
- }
- if( m_status == STATUS_FIRING )
- {
- if( m_nextLaunchFXFrame <= now )
- {
- //Generate iteration of fxlist for beam launching
- if( data->m_beamLaunchFX )
- {
- FXList::doFXPos( data->m_beamLaunchFX, &m_laserOriginPosition, NULL );
- }
- m_nextLaunchFXFrame = now + data->m_framesBetweenLaunchFXRefresh;
- }
- }
- //Handle shrouded status changes for the client player.
- Player *localPlayer = ThePlayerList->getLocalPlayer();
- if( localPlayer )
- {
- Bool shrouded = me->getShroudedStatus( localPlayer->getPlayerIndex() ) != OBJECTSHROUD_CLEAR;
- if( shrouded )
- {
- //We can't see it so any client effects that have been added logically needs to be removed!
- removeAllEffects();
- }
- else
- {
- //We can see it -- we only want to do anything if we just started seeing it, which means
- //we want to add client effects again.
- Bool revealThisFrame = m_clientShroudedLastFrame != shrouded;
- if( revealThisFrame )
- {
- //Only if we reveal this frame, will we add client effects. The logic can take it from
- //here on... unless of course we lose sight again.
- setClientStatus( m_status, revealThisFrame );
- }
- }
- m_clientShroudedLastFrame = shrouded;
- }
- return UPDATE_SLEEP_NONE;
- }
- //-------------------------------------------------------------------------------------------------
- void ParticleUplinkCannonUpdate::createOuterNodeParticleSystems( IntensityTypes intensity )
- {
- const ParticleUplinkCannonUpdateModuleData *data = getParticleUplinkCannonUpdateModuleData();
- AsciiString str;
- switch( intensity )
- {
- case IT_LIGHT:
- str = data->m_outerNodesLightFlareParticleSystemName;
- break;
- case IT_MEDIUM:
- str = data->m_outerNodesMediumFlareParticleSystemName;
- break;
- case IT_INTENSE:
- str = data->m_outerNodesIntenseFlareParticleSystemName;
- break;
- case IT_FINISH:
- break;
- }
- if( str.isNotEmpty() )
- {
- const ParticleSystemTemplate *tmp = TheParticleSystemManager->findTemplate( str );
- if( tmp )
- {
- ParticleSystem *system;
- for( int i = 0; i < data->m_outerEffectNumBones; i++ )
- {
- system = TheParticleSystemManager->createParticleSystem( tmp );
- if( system )
- {
- m_outerSystemIDs[ i ] = system->getSystemID();
- system->setPosition( &m_outerNodePositions[ i ] );
- system->setLocalTransform( &m_outerNodeOrientations[ i ] );
- }
- }
- }
- }
- }
- //-------------------------------------------------------------------------------------------------
- void ParticleUplinkCannonUpdate::createConnectorLasers( IntensityTypes intensity )
- {
- //Cache bone positions for the laser when it is ready to fire
- if( !m_upBonesCached )
- {
- calculateUpBonePositions();
- m_upBonesCached = TRUE;
- }
- const ParticleUplinkCannonUpdateModuleData *data = getParticleUplinkCannonUpdateModuleData();
- AsciiString str;
- switch( intensity )
- {
- case IT_LIGHT:
- break;
- case IT_MEDIUM:
- str = data->m_connectorMediumLaserNameName;;
- break;
- case IT_INTENSE:
- str = data->m_connectorIntenseLaserNameName;
- break;
- case IT_FINISH:
- break;
- }
- if( str.isNotEmpty() )
- {
- const ThingTemplate *thingTemplate = TheThingFactory->findTemplate( str );
- if( thingTemplate )
- {
- for( int i = 0; i < data->m_outerEffectNumBones; i++ )
- {
- Drawable *beam = TheThingFactory->newDrawable( thingTemplate );
- if( beam )
- {
- m_laserBeamIDs[ i ] = beam->getID();
- static NameKeyType nameKeyClientUpdate = NAMEKEY( "LaserUpdate" );
- LaserUpdate *update = (LaserUpdate*)beam->findClientUpdateModule( nameKeyClientUpdate );
- if( update )
- {
- update->initLaser( NULL, NULL, &m_outerNodePositions[ i ], &m_connectorNodePosition, "" );
- }
- }
- }
- }
- }
- }
- //-------------------------------------------------------------------------------------------------
- void ParticleUplinkCannonUpdate::createConnectorFlare( IntensityTypes intensity )
- {
- const ParticleUplinkCannonUpdateModuleData *data = getParticleUplinkCannonUpdateModuleData();
- AsciiString str;
- switch( intensity )
- {
- case IT_LIGHT:
- break;
- case IT_MEDIUM:
- str = data->m_connectorMediumFlareParticleSystemName;
- break;
- case IT_INTENSE:
- str = data->m_connectorIntenseFlareParticleSystemName;
- break;
- case IT_FINISH:
- break;
- }
- if( str.isNotEmpty() )
- {
- const ParticleSystemTemplate *tmp = TheParticleSystemManager->findTemplate( str );
- ParticleSystem *system;
- if( tmp )
- {
- system = TheParticleSystemManager->createParticleSystem( tmp );
- if( system )
- {
- m_connectorSystemID = system->getSystemID();
- system->setPosition( &m_connectorNodePosition );
- }
- }
- }
- }
- //-------------------------------------------------------------------------------------------------
- void ParticleUplinkCannonUpdate::createLaserBaseFlare( IntensityTypes intensity )
- {
- const ParticleUplinkCannonUpdateModuleData *data = getParticleUplinkCannonUpdateModuleData();
- AsciiString str;
- switch( intensity )
- {
- case IT_LIGHT:
- str = data->m_laserBaseLightFlareParticleSystemName;
- break;
- case IT_MEDIUM:
- str = data->m_laserBaseMediumFlareParticleSystemName;
- break;
- case IT_INTENSE:
- str = data->m_laserBaseIntenseFlareParticleSystemName;
- break;
- case IT_FINISH:
- break;
- }
- if( str.isNotEmpty() )
- {
- const ParticleSystemTemplate *tmp = TheParticleSystemManager->findTemplate( str );
- if( tmp )
- {
- ParticleSystem *system = TheParticleSystemManager->createParticleSystem( tmp );
- if( system )
- {
- m_laserBaseSystemID = system->getSystemID();
- system->setPosition( &m_laserOriginPosition );
- }
- }
- }
- }
- //-------------------------------------------------------------------------------------------------
- void ParticleUplinkCannonUpdate::createGroundToOrbitLaser( UnsignedInt growthFrames )
- {
- const ParticleUplinkCannonUpdateModuleData *data = getParticleUplinkCannonUpdateModuleData();
- Drawable *beam = TheGameClient->findDrawableByID( m_groundToOrbitBeamID );
- if( beam )
- {
- TheGameClient->destroyDrawable( beam );
- m_orbitToTargetBeamID = INVALID_DRAWABLE_ID;
- }
- if( data->m_particleBeamLaserName.isNotEmpty() )
- {
- const ThingTemplate *thingTemplate = TheThingFactory->findTemplate( data->m_particleBeamLaserName );
- if( thingTemplate )
- {
- Drawable *beam = TheThingFactory->newDrawable( thingTemplate );
- if( beam )
- {
- m_groundToOrbitBeamID = beam->getID();
- static NameKeyType nameKeyClientUpdate = NAMEKEY( "LaserUpdate" );
- LaserUpdate *update = (LaserUpdate*)beam->findClientUpdateModule( nameKeyClientUpdate );
- if( update )
- {
- Coord3D orbitPosition;
- orbitPosition.set( &m_laserOriginPosition );
- orbitPosition.z += 500.0f;
- update->initLaser( NULL, NULL, &m_laserOriginPosition, &orbitPosition, "", growthFrames );
- }
- }
- }
- }
- }
- //-------------------------------------------------------------------------------------------------
- void ParticleUplinkCannonUpdate::createOrbitToTargetLaser( UnsignedInt growthFrames )
- {
- const ParticleUplinkCannonUpdateModuleData *data = getParticleUplinkCannonUpdateModuleData();
- Drawable *beam = TheGameClient->findDrawableByID( m_orbitToTargetBeamID );
- if( beam )
- {
- TheAudio->removeAudioEvent( m_annihilationSound.getPlayingHandle() );
- TheGameClient->destroyDrawable( beam );
- m_orbitToTargetBeamID = INVALID_DRAWABLE_ID;
- }
- if( data->m_particleBeamLaserName.isNotEmpty() )
- {
- const ThingTemplate *thingTemplate = TheThingFactory->findTemplate( data->m_particleBeamLaserName );
- if( thingTemplate )
- {
- Drawable *beam = TheThingFactory->newDrawable( thingTemplate );
- if( beam )
- {
- m_orbitToTargetBeamID = beam->getID();
- static NameKeyType nameKeyClientUpdate = NAMEKEY( "LaserUpdate" );
- LaserUpdate *update = (LaserUpdate*)beam->findClientUpdateModule( nameKeyClientUpdate );
- if( update )
- {
- Coord3D orbitPosition;
- orbitPosition.set( &m_initialTargetPosition );
- orbitPosition.z += 500.0f;
- update->initLaser( NULL, NULL, &orbitPosition, &m_initialTargetPosition, "", growthFrames );
- }
- }
- }
- if( m_annihilationSound.getEventName().isNotEmpty() )
- {
- m_annihilationSound.setDrawableID( m_orbitToTargetBeamID );
- //m_annihilationSound.setPosition( &m_initialTargetPosition );
- m_annihilationSound.setPlayingHandle( TheAudio->addAudioEvent( &m_annihilationSound ) );
- }
- }
- }
- //-------------------------------------------------------------------------------------------------
- void ParticleUplinkCannonUpdate::createGroundHitParticleSystem( IntensityTypes intensity )
- {
- }
- //-------------------------------------------------------------------------------------------------
- void ParticleUplinkCannonUpdate::removeAllEffects()
- {
- const ParticleUplinkCannonUpdateModuleData *data = getParticleUplinkCannonUpdateModuleData();
- for( int i = 0; i < data->m_outerEffectNumBones; i++ )
- {
- if( m_outerSystemIDs && m_outerSystemIDs[ i ] )
- {
- TheParticleSystemManager->destroyParticleSystemByID( m_outerSystemIDs[ i ] );
- m_outerSystemIDs[ i ] = INVALID_PARTICLE_SYSTEM_ID;
- }
- Drawable *beam = TheGameClient->findDrawableByID( m_laserBeamIDs[ i ] );
- if( beam )
- {
- TheGameClient->destroyDrawable( beam );
- m_laserBeamIDs[ i ] = INVALID_DRAWABLE_ID;
- }
- }
- if( m_connectorSystemID )
- {
- TheParticleSystemManager->destroyParticleSystemByID( m_connectorSystemID );
- m_connectorSystemID = INVALID_PARTICLE_SYSTEM_ID;
- }
- if( m_laserBaseSystemID )
- {
- TheParticleSystemManager->destroyParticleSystemByID( m_laserBaseSystemID );
- m_laserBaseSystemID = INVALID_PARTICLE_SYSTEM_ID;
- }
- Drawable *beam = TheGameClient->findDrawableByID( m_groundToOrbitBeamID );
- if( beam )
- {
- TheGameClient->destroyDrawable( beam );
- m_groundToOrbitBeamID = INVALID_DRAWABLE_ID;
- }
- //Remove all sound hooks
- }
- //-------------------------------------------------------------------------------------------------
- Bool ParticleUplinkCannonUpdate::calculateDefaultInformation()
- {
- const ParticleUplinkCannonUpdateModuleData *data = getParticleUplinkCannonUpdateModuleData();
- Object *obj = getObject();
- //Get the local bone positions for each of the outer nodes.
- Coord3D bonePositions[ MAX_OUTER_NODES ];
- Matrix3D boneMatrices[ MAX_OUTER_NODES ];
- int numBones = obj->getMultiLogicalBonePosition( data->m_outerEffectBaseBoneName.str(), data->m_outerEffectNumBones, bonePositions, boneMatrices, FALSE );
- if( numBones != data->m_outerEffectNumBones )
- {
- DEBUG_CRASH( ("Particle cannon requires %d outer node bones, but can only find %d bones.", data->m_outerEffectNumBones, numBones ) );
- m_invalidSettings = TRUE;
- return FALSE;
- }
- for( int i = 0; i < data->m_outerEffectNumBones; i++ )
- {
- m_laserBeamIDs[ i ] = INVALID_DRAWABLE_ID;
- m_outerSystemIDs[ i ] = INVALID_PARTICLE_SYSTEM_ID;
- //Convert the local bone position into world space.
- Matrix3D nodeMatrix;
- getObject()->convertBonePosToWorldPos( &bonePositions[i], &boneMatrices[i], &m_outerNodePositions[ i ], &m_outerNodeOrientations[ i ] );
- }
- return TRUE;
- }
- //-------------------------------------------------------------------------------------------------
- Bool ParticleUplinkCannonUpdate::calculateUpBonePositions()
- {
- const ParticleUplinkCannonUpdateModuleData *data = getParticleUplinkCannonUpdateModuleData();
- Object *obj = getObject();
- //Due to instant special weapons (or scripting)... it's possible to be in many states that are ready to fire (or firing)
- Drawable *draw = obj->getDrawable();
- Matrix3D mtx;
- Coord3D pos;
- if( draw )
- {
- if( data->m_connectorBoneName.isNotEmpty() && draw->getCurrentClientBonePositions( data->m_connectorBoneName.str(), 0, &pos, &mtx, 1 ) )
- {
- obj->convertBonePosToWorldPos( &pos, &mtx, &m_connectorNodePosition, &mtx );
- }
- if( data->m_connectorBoneName.isNotEmpty() && draw->getCurrentClientBonePositions( data->m_fireBoneName.str(), 0, &pos, &mtx, 1 ) )
- {
- obj->convertBonePosToWorldPos( &pos, &mtx, &m_laserOriginPosition, &mtx );
- }
- }
- return TRUE;
- }
- //-------------------------------------------------------------------------------------------------
- void ParticleUplinkCannonUpdate::setLogicalStatus( PUCStatus newStatus )
- {
- Object *obj = getObject();
- if( m_status == newStatus )
- {
- return;
- }
- //Handle entering new status
- switch( newStatus )
- {
- case STATUS_IDLE:
- {
- //Set unpacked animation
- obj->clearModelConditionFlags( MAKE_MODELCONDITION_MASK3( MODELCONDITION_PACKING, MODELCONDITION_UNPACKING, MODELCONDITION_DEPLOYED ) );
- TheAudio->removeAudioEvent( m_powerupSound.getPlayingHandle() );
- TheAudio->removeAudioEvent( m_unpackToReadySound.getPlayingHandle() );
- TheAudio->removeAudioEvent( m_firingToIdleSound.getPlayingHandle() );
- TheAudio->removeAudioEvent( m_annihilationSound.getPlayingHandle() );
- break;
- }
- case STATUS_CHARGING:
- {
- m_laserStatus = LASERSTATUS_NONE;
- m_powerupSound.setObjectID( obj->getID() );
- m_powerupSound.setPlayingHandle( TheAudio->addAudioEvent( &m_powerupSound ) );
- TheAudio->removeAudioEvent( m_unpackToReadySound.getPlayingHandle() );
- TheAudio->removeAudioEvent( m_firingToIdleSound.getPlayingHandle() );
- TheAudio->removeAudioEvent( m_annihilationSound.getPlayingHandle() );
- break;
- }
- case STATUS_PREPARING:
- {
- //Set unpacking animation
- obj->clearAndSetModelConditionFlags( MAKE_MODELCONDITION_MASK2( MODELCONDITION_PACKING, MODELCONDITION_DEPLOYED ),
- MAKE_MODELCONDITION_MASK( MODELCONDITION_UNPACKING ) );
- if( m_unpackToReadySound.getEventName().isNotEmpty() )
- {
- m_unpackToReadySound.setObjectID( obj->getID() );
- m_unpackToReadySound.setPlayingHandle( TheAudio->addAudioEvent( &m_unpackToReadySound ) );
- }
- TheAudio->removeAudioEvent( m_firingToIdleSound.getPlayingHandle() );
- TheAudio->removeAudioEvent( m_annihilationSound.getPlayingHandle() );
-
- m_laserStatus = LASERSTATUS_NONE;
- break;
- }
- case STATUS_ALMOST_READY:
- {
- //Set deployed animation
- obj->clearAndSetModelConditionFlags( MAKE_MODELCONDITION_MASK2( MODELCONDITION_PACKING, MODELCONDITION_UNPACKING ),
- MAKE_MODELCONDITION_MASK( MODELCONDITION_DEPLOYED ) );
- m_laserStatus = LASERSTATUS_NONE;
- break;
- }
- case STATUS_READY_TO_FIRE:
- {
- obj->clearAndSetModelConditionFlags( MAKE_MODELCONDITION_MASK2( MODELCONDITION_PACKING, MODELCONDITION_UNPACKING ),
- MAKE_MODELCONDITION_MASK( MODELCONDITION_DEPLOYED ) );
- TheAudio->removeAudioEvent( m_powerupSound.getPlayingHandle() );
- TheAudio->removeAudioEvent( m_firingToIdleSound.getPlayingHandle() );
- TheAudio->removeAudioEvent( m_annihilationSound.getPlayingHandle() );
- m_laserStatus = LASERSTATUS_NONE;
- break;
- }
- case STATUS_PREFIRE:
- {
- break;
- }
- case STATUS_FIRING:
- {
- obj->clearAndSetModelConditionFlags( MAKE_MODELCONDITION_MASK2( MODELCONDITION_PACKING, MODELCONDITION_UNPACKING ),
- MAKE_MODELCONDITION_MASK( MODELCONDITION_DEPLOYED ) );
- if( m_firingToIdleSound.getEventName().isNotEmpty() )
- {
- m_firingToIdleSound.setObjectID( obj->getID() );
- m_firingToIdleSound.setPlayingHandle( TheAudio->addAudioEvent( &m_firingToIdleSound ) );
- }
- TheAudio->removeAudioEvent( m_powerupSound.getPlayingHandle() );
- TheAudio->removeAudioEvent( m_unpackToReadySound.getPlayingHandle() );
- TheAudio->removeAudioEvent( m_annihilationSound.getPlayingHandle() );
- m_nextLaunchFXFrame = 0;
- break;
- }
- case STATUS_POSTFIRE:
- {
- break;
- }
- case STATUS_PACKING:
- {
- //Set packing animation
- obj->clearAndSetModelConditionFlags( MAKE_MODELCONDITION_MASK2( MODELCONDITION_UNPACKING, MODELCONDITION_DEPLOYED ),
- MAKE_MODELCONDITION_MASK( MODELCONDITION_PACKING ) );
- break;
- }
- }
- //Set new status.
- m_status = newStatus;
- setClientStatus( m_status, FALSE );
- }
- //-------------------------------------------------------------------------------------------------
- void ParticleUplinkCannonUpdate::setClientStatus( PUCStatus newStatus, Bool revealThisFrame )
- {
- const ParticleUplinkCannonUpdateModuleData *data = getParticleUplinkCannonUpdateModuleData();
- //Caches bone positions for positioning the client beams.
- if( !m_defaultInfoCached )
- {
- if( !calculateDefaultInformation() )
- {
- m_invalidSettings = TRUE;
- return;
- }
- m_defaultInfoCached = TRUE;
- }
- //This isn't the most efficient way to do this. Various effects can live in more than
- //one status state, but keeping track of them properly is error-prone and hard to read.
- //Therefore, the new philosophy is to delete EVERYTHING between status changes and
- //create the ones we want!
- removeAllEffects();
- //Handle entering new status
- switch( newStatus )
- {
- case STATUS_IDLE:
- {
- break;
- }
- case STATUS_CHARGING:
- {
- createOuterNodeParticleSystems( IT_LIGHT );
- break;
- }
- case STATUS_PREPARING:
- {
- createOuterNodeParticleSystems( IT_MEDIUM );
- break;
- }
- case STATUS_ALMOST_READY:
- {
- createOuterNodeParticleSystems( IT_MEDIUM );
- createConnectorLasers( IT_MEDIUM );
- createConnectorFlare( IT_MEDIUM );
- break;
- }
- case STATUS_READY_TO_FIRE:
- {
- createOuterNodeParticleSystems( IT_MEDIUM );
- createConnectorLasers( IT_MEDIUM );
- createConnectorFlare( IT_MEDIUM );
- createLaserBaseFlare( IT_LIGHT );
- break;
- }
- case STATUS_PREFIRE:
- {
- break;
- }
- case STATUS_FIRING:
- {
- if( revealThisFrame )
- {
- createGroundToOrbitLaser( 0 );
- }
- else
- {
- createGroundToOrbitLaser( data->m_widthGrowFrames );
- }
- createOuterNodeParticleSystems( IT_INTENSE );
- createConnectorLasers( IT_INTENSE );
- createConnectorFlare( IT_INTENSE );
- createLaserBaseFlare( IT_INTENSE );
- break;
- }
- case STATUS_POSTFIRE:
- {
- createOuterNodeParticleSystems( IT_MEDIUM );
- createConnectorLasers( IT_MEDIUM );
- createConnectorFlare( IT_MEDIUM );
- createLaserBaseFlare( IT_MEDIUM );
- createGroundToOrbitLaser( 0 );
- Drawable *beam = TheGameClient->findDrawableByID( m_groundToOrbitBeamID );
- if( beam )
- {
- static NameKeyType nameKeyClientUpdate = NAMEKEY( "LaserUpdate" );
- LaserUpdate *update = (LaserUpdate*)beam->findClientUpdateModule( nameKeyClientUpdate );
- if( update )
- {
- if( revealThisFrame )
- {
- update->setDecayFrames( 0 );
- }
- else
- {
- update->setDecayFrames( data->m_widthGrowFrames );
- }
- }
- }
- break;
- }
- case STATUS_PACKING:
- {
- break;
- }
- }
- }
- //-------------------------------------------------------------------------------------------------
- Bool ParticleUplinkCannonUpdate::doesSpecialPowerHaveOverridableDestinationActive() const
- {
- return m_status == STATUS_PREFIRE || m_status == STATUS_FIRING || m_status == STATUS_POSTFIRE;
- }
- // ------------------------------------------------------------------------------------------------
- /** CRC */
- // ------------------------------------------------------------------------------------------------
- void ParticleUplinkCannonUpdate::crc( Xfer *xfer )
- {
- // extend base class
- UpdateModule::crc( xfer );
- } // end crc
- // ------------------------------------------------------------------------------------------------
- /** Xfer method
- * Version Info:
- * 1: Initial version */
- // ------------------------------------------------------------------------------------------------
- void ParticleUplinkCannonUpdate::xfer( Xfer *xfer )
- {
- const ParticleUplinkCannonUpdateModuleData *data = getParticleUplinkCannonUpdateModuleData();
- // version
- XferVersion currentVersion = 3;
- XferVersion version = currentVersion;
- xfer->xferVersion( &version, currentVersion );
- // extend base class
- UpdateModule::xfer( xfer );
- // status
- xfer->xferUser( &m_status, sizeof( PUCStatus ) );
- // laser status
- xfer->xferUser( &m_laserStatus, sizeof( LaserStatus ) );
- // frames
- xfer->xferUnsignedInt( &m_frames );
- // we do not need to tie up the special power module pointer, it is done on object creation
- // SpecialPowerModuleInterface *m_specialPowerModule;
- // outer system ids
- xfer->xferUser( m_outerSystemIDs, sizeof( ParticleSystemID ) * MAX_OUTER_NODES );
- // laser beam ids
- xfer->xferUser( m_laserBeamIDs, sizeof( DrawableID ) * MAX_OUTER_NODES );
- // ground to orbit beam id
- xfer->xferDrawableID( &m_groundToOrbitBeamID );
- // orbit to target beam
- xfer->xferDrawableID( &m_orbitToTargetBeamID );
- // connector system ID
- xfer->xferUser( &m_connectorSystemID, sizeof( ParticleSystemID ) );
- // laser base system id
- xfer->xferUser( &m_laserBaseSystemID, sizeof( ParticleSystemID ) );
- // outer node positions
- xfer->xferUser( m_outerNodePositions, sizeof( Coord3D ) * MAX_OUTER_NODES );
- // outer node orientations
- xfer->xferUser( m_outerNodeOrientations, sizeof( Matrix3D ) * MAX_OUTER_NODES );
- // connector node position
- xfer->xferCoord3D( &m_connectorNodePosition );
- // laser origin position
- xfer->xferCoord3D( &m_laserOriginPosition );
- // The manual override target destination.
- xfer->xferCoord3D( &m_overrideTargetDestination );
- // up bones cached
- xfer->xferBool( &m_upBonesCached );
- // default info cached
- xfer->xferBool( &m_defaultInfoCached );
- // invalid settings
- xfer->xferBool( &m_invalidSettings );
- // initial target position
- xfer->xferCoord3D( &m_initialTargetPosition );
- // current target position
- xfer->xferCoord3D( &m_currentTargetPosition );
- // scorch marks make
- xfer->xferUnsignedInt( &m_scorchMarksMade );
- // next scorch mark frame
- xfer->xferUnsignedInt( &m_nextScorchMarkFrame );
-
- // next launch FX frame
- xfer->xferUnsignedInt( &m_nextLaunchFXFrame );
- // damage pluses made
- xfer->xferUnsignedInt( &m_damagePulsesMade );
- // next damage pulse frame
- xfer->xferUnsignedInt( &m_nextDamagePulseFrame );
- // start attack frame
- xfer->xferUnsignedInt( &m_startAttackFrame );
- // start attack frame
- if( xfer->getXferMode() == XFER_SAVE || version >= 2 )
- {
- xfer->xferUnsignedInt( &m_startDecayFrame );
- }
- else
- {
- m_startDecayFrame = m_startAttackFrame + data->m_totalFiringFrames;
- }
- // the time of last manual target click
- xfer->xferUnsignedInt( & m_lastDrivingClickFrame );
- // the time of the 2nd last manual target click
- xfer->xferUnsignedInt( &m_2ndLastDrivingClickFrame );
- if( version >= 3 )
- {
- xfer->xferBool( &m_manualTargetMode );
- xfer->xferBool( &m_scriptedWaypointMode );
- xfer->xferUnsignedInt( &m_nextDestWaypointID );
- }
- } // end xfer
- // ------------------------------------------------------------------------------------------------
- /** Load post process */
- // ------------------------------------------------------------------------------------------------
- void ParticleUplinkCannonUpdate::loadPostProcess( void )
- {
- // extend base class
- UpdateModule::loadPostProcess();
- } // end loadPostProcess
|