diff --git a/Core/GameEngine/Source/Common/System/GameMemoryInitPools_GeneralsMD.inl b/Core/GameEngine/Source/Common/System/GameMemoryInitPools_GeneralsMD.inl index f7e02d7c8a9..aed9ab5adea 100644 --- a/Core/GameEngine/Source/Common/System/GameMemoryInitPools_GeneralsMD.inl +++ b/Core/GameEngine/Source/Common/System/GameMemoryInitPools_GeneralsMD.inl @@ -186,6 +186,8 @@ static PoolSizeRec PoolSizes[] = { "ParticleUplinkCannonUpdate", 16, 16 }, { "SpectreGunshipUpdate", 8, 8 }, { "SpectreGunshipDeploymentUpdate", 8, 8 }, + { "KodiakUpdate", 8, 8 }, + { "KodiakDeploymentUpdate", 8, 8 }, { "BaikonurLaunchPower", 4, 4 }, { "RadiusDecalUpdate", 16, 16 }, { "RadiusDecalBehavior", 32, 32 }, diff --git a/GeneralsMD/Code/GameEngine/CMakeLists.txt b/GeneralsMD/Code/GameEngine/CMakeLists.txt index e0dde9186b8..2c662daf497 100644 --- a/GeneralsMD/Code/GameEngine/CMakeLists.txt +++ b/GeneralsMD/Code/GameEngine/CMakeLists.txt @@ -491,6 +491,8 @@ set(GAMEENGINE_SRC Include/GameLogic/Module/WeaponBonusUpgrade.h Include/GameLogic/Module/WeaponSetUpgrade.h Include/GameLogic/Module/WorkerAIUpdate.h + Include/GameLogic/Module/KodiakDeploymentUpdate.h + Include/GameLogic/Module/KodiakUpdate.h Include/GameLogic/Object.h Include/GameLogic/ObjectCreationList.h Include/GameLogic/ObjectIter.h @@ -1082,6 +1084,8 @@ set(GAMEENGINE_SRC Source/GameLogic/Object/Update/WaveGuideUpdate.cpp Source/GameLogic/Object/Update/WeaponBonusUpdate.cpp Source/GameLogic/Object/Update/ArmorDamageScalarUpdate.cpp + Source/GameLogic/Object/Update/KodiakDeploymentUpdate.cpp + Source/GameLogic/Object/Update/KodiakUpdate.cpp Source/GameLogic/Object/Upgrade/ActiveShroudUpgrade.cpp Source/GameLogic/Object/Upgrade/ArmorUpgrade.cpp Source/GameLogic/Object/Upgrade/CommandSetUpgrade.cpp diff --git a/GeneralsMD/Code/GameEngine/Include/GameLogic/Module/ContainModule.h b/GeneralsMD/Code/GameEngine/Include/GameLogic/Module/ContainModule.h index 4c2e73235c8..4a8dfca296f 100644 --- a/GeneralsMD/Code/GameEngine/Include/GameLogic/Module/ContainModule.h +++ b/GeneralsMD/Code/GameEngine/Include/GameLogic/Module/ContainModule.h @@ -213,6 +213,7 @@ class ContainModuleInterface virtual short getRiderSlot(ObjectID riderID) const = 0; // get the slot occupied by the object. virtual short getPortableSlot(ObjectID portableID) const = 0; // get the slot occupied by the object. virtual const ContainedItemsList* getAddOnList() const = 0; + virtual ContainedItemsList* getAddOnList() = 0; }; //------------------------------------------------------------------------------------------------- diff --git a/GeneralsMD/Code/GameEngine/Include/GameLogic/Module/KodiakDeploymentUpdate.h b/GeneralsMD/Code/GameEngine/Include/GameLogic/Module/KodiakDeploymentUpdate.h new file mode 100644 index 00000000000..cc608e76fd9 --- /dev/null +++ b/GeneralsMD/Code/GameEngine/Include/GameLogic/Module/KodiakDeploymentUpdate.h @@ -0,0 +1,129 @@ +// FILE: KodiakDeploymentUpdate.h ////////////////////////////////////////////////////////////////////////// +// Desc: Update module to handle deployment of the Kodiak Generals special power.from command center +/////////////////////////////////////////////////////////////////////////////////////////////////// + +#pragma once + +#ifndef __KODIAK_DEPLOYMENT_UPDATE_H_ +#define __KODIAK_DEPLOYMENT_UPDATE_H_ + +// INCLUDES /////////////////////////////////////////////////////////////////////////////////////// +#include "Common/KindOf.h" +#include "GameLogic/Module/SpecialPowerUpdateModule.h" +#include "SpecialPowerModule.h" +#include +#include "SpectreGunshipDeploymentUpdate.h" + +// FORWARD REFERENCES ///////////////////////////////////////////////////////////////////////////// +class SpecialPowerModule; +class ParticleSystem; +class FXList; +class AudioEventRTS; +enum ParticleSystemID CPP_11(: Int); +enum ScienceType CPP_11(: Int); + +//#define MAX_OUTER_NODES 16 + +//#define PUCK + + +//enum GunshipCreateLocType CPP_11(: Int) +//{ +// CREATE_GUNSHIP_AT_EDGE_NEAR_SOURCE, +// CREATE_GUNSHIP_AT_EDGE_FARTHEST_FROM_SOURCE, +// CREATE_GUNSHIP_AT_EDGE_NEAR_TARGET, +// CREATE_GUNSHIP_AT_EDGE_FARTHEST_FROM_TARGET, +//}; + + + +//------------------------------------------------------------------------------------------------- +//------------------------------------------------------------------------------------------------- +class KodiakDeploymentUpdateModuleData : public ModuleData +{ +public: + SpecialPowerTemplate *m_specialPowerTemplate; + ScienceType m_extraRequiredScience; ///< science required (if any) to actually execute this power + WeaponTemplate *m_howitzerWeaponTemplate; + AsciiString m_gunshipTemplateName; + AsciiString m_gattlingTemplateName; +// AsciiString m_howitzerTemplateName; + RadiusDecalTemplate m_attackAreaDecalTemplate; + RadiusDecalTemplate m_targetingReticleDecalTemplate; + UnsignedInt m_orbitFrames; + Real m_attackAreaRadius; + Real m_targetingReticleRadius; + Real m_gunshipOrbitRadius; + GunshipCreateLocType m_createLoc; + + + const ParticleSystemTemplate * m_gattlingStrafeFXParticleSystem; + + KodiakDeploymentUpdateModuleData(); + static void buildFieldParse(MultiIniFieldParse& p); + +private: + +}; + +//enum GunshipDeployStatus CPP_11(: Int) +//{ +// GUNSHIPDEPLOY_STATUS_INSERTING, +// GUNSHIPDEPLOY_STATUS_ORBITING, +// GUNSHIPDEPLOY_STATUS_DEPARTING, +// GUNSHIPDEPLOY_STATUS_IDLE, +//}; + + +//------------------------------------------------------------------------------------------------- +/** The default update module */ +//------------------------------------------------------------------------------------------------- +class KodiakDeploymentUpdate : public SpecialPowerUpdateModule +{ + + MEMORY_POOL_GLUE_WITH_USERLOOKUP_CREATE( KodiakDeploymentUpdate, "KodiakDeploymentUpdate" ) + MAKE_STANDARD_MODULE_MACRO_WITH_MODULE_DATA( KodiakDeploymentUpdate, KodiakDeploymentUpdateModuleData ); + +public: + + KodiakDeploymentUpdate( Thing *thing, const ModuleData* moduleData ); + // virtual destructor prototype provided by memory pool declaration + + // SpecialPowerUpdateInterface + virtual Bool initiateIntentToDoSpecialPower(const SpecialPowerTemplate *specialPowerTemplate, const Object *targetObj, const Coord3D *targetPos, const Waypoint *way, UnsignedInt commandOptions ); + virtual Bool isSpecialAbility() const { return false; } + virtual Bool isSpecialPower() const { return true; } + virtual Bool isActive() const {return FALSE;} + virtual SpecialPowerUpdateInterface* getSpecialPowerUpdateInterface() { return this; } + virtual CommandOption getCommandOption() const { return (CommandOption)0; } + virtual Bool isPowerCurrentlyInUse( const CommandButton *command = NULL ) const { return FALSE; }; + virtual ScienceType getExtraRequiredScience() const { return getKodiakDeploymentUpdateModuleData()->m_extraRequiredScience; } //Does this object have more than one special power module with the same spTemplate? + + virtual void onObjectCreated(); + virtual UpdateSleepTime update(); + + void cleanUp(); + + + + virtual Bool doesSpecialPowerHaveOverridableDestinationActive() const { return FALSE; }; + virtual Bool doesSpecialPowerHaveOverridableDestination() const { return FALSE; } //Does it have it, even if it's not active? + virtual void setSpecialPowerOverridableDestination( const Coord3D *loc ) {}; + + // Disabled conditions to process (termination conditions!) + virtual DisabledMaskType getDisabledTypesToProcess() const { return MAKE_DISABLED_MASK4( DISABLED_SUBDUED, DISABLED_UNDERPOWERED, DISABLED_EMP, DISABLED_HACKED ); } + +protected: + + + + SpecialPowerModuleInterface* m_specialPowerModule; + Coord3D m_initialTargetPosition; + ObjectID m_gunshipID; + + +}; + + +#endif // __SPECTRE_GUNSHIP_DEPLOYMENT_UPDATE_H_ + diff --git a/GeneralsMD/Code/GameEngine/Include/GameLogic/Module/KodiakUpdate.h b/GeneralsMD/Code/GameEngine/Include/GameLogic/Module/KodiakUpdate.h new file mode 100644 index 00000000000..994a33b11e8 --- /dev/null +++ b/GeneralsMD/Code/GameEngine/Include/GameLogic/Module/KodiakUpdate.h @@ -0,0 +1,144 @@ +// FILE: KodiakUpdate.h ////////////////////////////////////////////////////////////////////////// +// Desc: Update module to handle weapon firing of the Kodiak Generals special power. +/////////////////////////////////////////////////////////////////////////////////////////////////// + +#pragma once + +#ifndef __KODIAK_UPDATE_H_ +#define __KODIAK_UPDATE_H_ + +// INCLUDES /////////////////////////////////////////////////////////////////////////////////////// +#include "Common/KindOf.h" +#include "GameLogic/Module/SpecialPowerUpdateModule.h" +#include "SpecialPowerModule.h" +#include +#include "UpdateModule.h" +#include "SpectreGunshipUpdate.h" + +// FORWARD REFERENCES ///////////////////////////////////////////////////////////////////////////// +class SpecialPowerModule; +class ParticleSystem; +class FXList; +class AudioEventRTS; +enum ParticleSystemID CPP_11(: Int); + +//#define MAX_OUTER_NODES 16 +//#define TRACKERS + +//#define PUCK + +//------------------------------------------------------------------------------------------------- +//------------------------------------------------------------------------------------------------- +class KodiakUpdateModuleData : public ModuleData +{ +public: + SpecialPowerTemplate *m_specialPowerTemplate; + + RadiusDecalTemplate m_attackAreaDecalTemplate; + RadiusDecalTemplate m_targetingReticleDecalTemplate; + UnsignedInt m_orbitFrames; + UnsignedInt m_mainTargetingRate; + UnsignedInt m_sideTargetingRate; + UnsignedInt m_aaTargetingRate; + UnsignedInt m_turretRecenterFramesBeforeExit; + UnsignedInt m_initialAttackDelayFrames; + + Real m_attackAreaRadius; + Real m_sideAttackAreaRadius; + Real m_aaAttackAreaRadius; + Real m_targetingReticleRadius; + Real m_gunshipOrbitRadius; + Real m_missileLockRadius; + UnsignedInt m_numMainTurrets; + UnsignedInt m_numSideTurrets; + UnsignedInt m_numAATurrets; + + Real m_missileScatterRadius; + std::vector m_scatterTargets; ///< targets for missile attack, amount of targets should match clip size of primary weapon + + //const ParticleSystemTemplate * m_gattlingStrafeFXParticleSystem; + + KodiakUpdateModuleData(); + static void buildFieldParse(MultiIniFieldParse& p); + + + static void parseScatterTarget(INI* ini, void* instance, void* /*store*/, const void* /*userData*/); +private: + +}; + +//------------------------------------------------------------------------------------------------- +/** The default update module */ +//------------------------------------------------------------------------------------------------- +class KodiakUpdate : public SpecialPowerUpdateModule +{ + MEMORY_POOL_GLUE_WITH_USERLOOKUP_CREATE( KodiakUpdate, "KodiakUpdate" ) + MAKE_STANDARD_MODULE_MACRO_WITH_MODULE_DATA( KodiakUpdate, KodiakUpdateModuleData ); + +public: + + KodiakUpdate( Thing *thing, const ModuleData* moduleData ); + // virtual destructor prototype provided by memory pool declaration + + // SpecialPowerUpdateInterface + virtual Bool initiateIntentToDoSpecialPower(const SpecialPowerTemplate *specialPowerTemplate, const Object *targetObj, const Coord3D *targetPos, const Waypoint *way, UnsignedInt commandOptions ); + virtual Bool isSpecialAbility() const { return false; } + virtual Bool isSpecialPower() const { return true; } + virtual Bool isActive() const {return m_status < GUNSHIP_STATUS_DEPARTING;} + virtual SpecialPowerUpdateInterface* getSpecialPowerUpdateInterface() { return this; } + virtual CommandOption getCommandOption() const { return (CommandOption)0; } + virtual Bool isPowerCurrentlyInUse( const CommandButton *command = NULL ) const; + + virtual void onObjectCreated(); + virtual UpdateSleepTime update(); + + void cleanUp(); + + + + virtual Bool doesSpecialPowerHaveOverridableDestinationActive() const; + virtual Bool doesSpecialPowerHaveOverridableDestination() const { return true; } //Does it have it, even if it's not active? + virtual void setSpecialPowerOverridableDestination( const Coord3D *loc ); + + // Disabled conditions to process (termination conditions!) + virtual DisabledMaskType getDisabledTypesToProcess() const { return MAKE_DISABLED_MASK4( DISABLED_SUBDUED, DISABLED_UNDERPOWERED, DISABLED_EMP, DISABLED_HACKED ); } + static void parseScatterTarget(INI* ini, void* instance, void* /*store*/, const void* /*userData*/); +protected: + + void setLogicalStatus( GunshipStatus newStatus ) { m_status = newStatus; } + void disengageAndDepartAO( Object *gunship ); + + Bool isPointOffMap( const Coord3D& testPos ) const; + Bool isFairDistanceFromShip( Object *target ); + + SpecialPowerModuleInterface* m_specialPowerModule; + + void friend_enableAfterburners(Bool v); + + + + + Coord3D m_initialTargetPosition; + Coord3D m_overrideTargetDestination; + Coord3D m_movementPosition; + Coord3D m_positionToShootAt; + + + GunshipStatus m_status; + + UnsignedInt m_orbitEscapeFrame; + + RadiusDecal m_attackAreaDecal; + RadiusDecal m_targetingReticleDecal; + +#if defined TRACKERS + RadiusDecal m_howitzerTrackerDecal; +#endif + + AudioEventRTS m_afterburnerSound; + +}; + + +#endif // __KODIAK_UPDATE_H_ + diff --git a/GeneralsMD/Code/GameEngine/Include/GameLogic/Module/MultiAddOnContain.h b/GeneralsMD/Code/GameEngine/Include/GameLogic/Module/MultiAddOnContain.h index 7dc2e8b3027..20b18feb0a0 100644 --- a/GeneralsMD/Code/GameEngine/Include/GameLogic/Module/MultiAddOnContain.h +++ b/GeneralsMD/Code/GameEngine/Include/GameLogic/Module/MultiAddOnContain.h @@ -132,6 +132,7 @@ class MultiAddOnContain : public TransportContain virtual short getRiderSlot(ObjectID riderID) const; virtual short getPortableSlot(ObjectID riderID) const; virtual const ContainedItemsList* getAddOnList() const { return &m_addOnList; } + virtual ContainedItemsList* getAddOnList() { return &m_addOnList; } private: void parseAddOnEntry(INI* ini, void* instance, void* store, const void* /*userData*/); diff --git a/GeneralsMD/Code/GameEngine/Include/GameLogic/Module/OpenContain.h b/GeneralsMD/Code/GameEngine/Include/GameLogic/Module/OpenContain.h index c037171162d..464226d0bac 100644 --- a/GeneralsMD/Code/GameEngine/Include/GameLogic/Module/OpenContain.h +++ b/GeneralsMD/Code/GameEngine/Include/GameLogic/Module/OpenContain.h @@ -258,6 +258,7 @@ class OpenContain : public UpdateModule, virtual short getRiderSlot(ObjectID riderID) const { return -1; } virtual short getPortableSlot(ObjectID portableID) const { return -1; } virtual const ContainedItemsList* getAddOnList() const { return NULL; } + virtual ContainedItemsList* getAddOnList() { return NULL; } void pruneDeadWanters(); diff --git a/GeneralsMD/Code/GameEngine/Source/Common/Thing/ModuleFactory.cpp b/GeneralsMD/Code/GameEngine/Source/Common/Thing/ModuleFactory.cpp index 573b53c2879..85445660282 100644 --- a/GeneralsMD/Code/GameEngine/Source/Common/Thing/ModuleFactory.cpp +++ b/GeneralsMD/Code/GameEngine/Source/Common/Thing/ModuleFactory.cpp @@ -145,6 +145,8 @@ #include "GameLogic/Module/ParticleUplinkCannonUpdate.h" #include "GameLogic/Module/SpectreGunshipUpdate.h" #include "GameLogic/Module/SpectreGunshipDeploymentUpdate.h" +#include "GameLogic/Module/KodiakUpdate.h" +#include "GameLogic/Module/KodiakDeploymentUpdate.h" #include "GameLogic/Module/BaikonurLaunchPower.h" #include "GameLogic/Module/BattlePlanUpdate.h" #include "GameLogic/Module/LifetimeUpdate.h" @@ -450,6 +452,8 @@ void ModuleFactory::init( void ) addModule( ParticleUplinkCannonUpdate ); addModule( SpectreGunshipUpdate ); addModule( SpectreGunshipDeploymentUpdate ); + addModule( KodiakUpdate ); + addModule( KodiakDeploymentUpdate ); addModule( BaikonurLaunchPower ); addModule( BattlePlanUpdate ); addModule( ProjectileStreamUpdate ); diff --git a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Update/KodiakDeploymentUpdate.cpp b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Update/KodiakDeploymentUpdate.cpp new file mode 100644 index 00000000000..5376afb620d --- /dev/null +++ b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Update/KodiakDeploymentUpdate.cpp @@ -0,0 +1,302 @@ +// FILE: KodiakDeploymentUpdate.cpp ////////////////////////////////////////////////////////////////////////// +// Desc: Update module to handle deployment of the Kodiak Generals special power.from command center +/////////////////////////////////////////////////////////////////////////////////////////////////// + +#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 "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/KodiakDeploymentUpdate.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" + + + + +//------------------------------------------------------------------------------------------------- +//------------------------------------------------------------------------------------------------- +KodiakDeploymentUpdateModuleData::KodiakDeploymentUpdateModuleData() +{ + m_specialPowerTemplate = NULL; + m_extraRequiredScience = SCIENCE_INVALID; +/******BOTH*******//*BOTH*//******BOTH*******//******BOTH*******/ m_attackAreaRadius = 200.0f; + m_createLoc = CREATE_GUNSHIP_AT_EDGE_FARTHEST_FROM_TARGET; + +} + + +static const char* TheGunshipCreateLocTypeNames[] = +{ + "CREATE_AT_EDGE_NEAR_SOURCE", + "CREATE_AT_EDGE_FARTHEST_FROM_SOURCE", + "CREATE_AT_EDGE_NEAR_TARGET", + "CREATE_AT_EDGE_FARTHEST_FROM_TARGET", + NULL +}; + + + +static Real zero = 0.0f; +//------------------------------------------------------------------------------------------------- +/*static*/ void KodiakDeploymentUpdateModuleData::buildFieldParse(MultiIniFieldParse& p) +{ + ModuleData::buildFieldParse(p); + + static const FieldParse dataFieldParse[] = + { + { "GunshipTemplateName", INI::parseAsciiString, NULL, offsetof( KodiakDeploymentUpdateModuleData, m_gunshipTemplateName ) }, + { "RequiredScience", INI::parseScience, NULL, offsetof( KodiakDeploymentUpdateModuleData, m_extraRequiredScience ) }, +/******BOTH*******/ { "SpecialPowerTemplate", INI::parseSpecialPowerTemplate, NULL, offsetof( KodiakDeploymentUpdateModuleData, m_specialPowerTemplate ) }, +/*******BOTH******/ { "AttackAreaRadius", INI::parseReal, NULL, offsetof( KodiakDeploymentUpdateModuleData, m_attackAreaRadius ) }, + { "CreateLocation", INI::parseIndexList, TheGunshipCreateLocTypeNames, offsetof( KodiakDeploymentUpdateModuleData, m_createLoc ) }, + + { 0, 0, 0, 0 } + }; + p.add(dataFieldParse); +} + +//------------------------------------------------------------------------------------------------- +KodiakDeploymentUpdate::KodiakDeploymentUpdate( Thing *thing, const ModuleData* moduleData ) : SpecialPowerUpdateModule( thing, moduleData ) +{ + m_specialPowerModule = NULL; + m_gunshipID = INVALID_ID; +} + +//------------------------------------------------------------------------------------------------- +KodiakDeploymentUpdate::~KodiakDeploymentUpdate( void ) +{ +} + +//------------------------------------------------------------------------------------------------- +// Validate that we have the necessary data from the ini file. +//------------------------------------------------------------------------------------------------- +void KodiakDeploymentUpdate::onObjectCreated() +{ + const KodiakDeploymentUpdateModuleData *data = getKodiakDeploymentUpdateModuleData(); + Object *obj = getObject(); + + if( !data->m_specialPowerTemplate ) + { + DEBUG_CRASH( ("%s object's KodiakDeploymentUpdate lacks access to the SpecialPowerTemplate. Needs to be specified in ini.", obj->getTemplate()->getName().str() ) ); + return; + } + + m_specialPowerModule = obj->getSpecialPowerModule( data->m_specialPowerTemplate ); +} + +//------------------------------------------------------------------------------------------------- +Bool KodiakDeploymentUpdate::initiateIntentToDoSpecialPower(const SpecialPowerTemplate* specialPowerTemplate, const Object* targetObj, const Coord3D* targetPos, const Waypoint* way, UnsignedInt commandOptions) +{ + const KodiakDeploymentUpdateModuleData* data = getKodiakDeploymentUpdateModuleData(); + + if (m_specialPowerModule->getSpecialPowerTemplate() != specialPowerTemplate) + { + //Check to make sure our modules are connected. + return FALSE; + } + + // getObject()->getControllingPlayer()->getAcademyStats()->recordSpecialPowerUsed( specialPowerTemplate ); + + if (!BitIsSet(commandOptions, COMMAND_FIRED_BY_SCRIPT)) + { + /******CHANGE*******/ m_initialTargetPosition.set(targetPos); + } + else + { + UnsignedInt now = TheGameLogic->getFrame(); + m_specialPowerModule->setReadyFrame(now); + /******CHANGE*******/ m_initialTargetPosition.set(targetPos); + // setLogicalStatus( GUNSHIPDEPLOY_STATUS_INSERTING ); + } + + Object* newGunship = TheGameLogic->findObjectByID(m_gunshipID); + const ThingTemplate* gunshipTemplate = TheThingFactory->findTemplate(data->m_gunshipTemplateName); + if (newGunship != NULL) + { + // disengageAndDepartAO( newGunship ); + m_gunshipID = INVALID_ID; + newGunship = NULL; + } + + + // Lets make a gunship, since we have none. + { + newGunship = TheThingFactory->newObject(gunshipTemplate, getObject()->getTeam()); + } + + DEBUG_ASSERTCRASH(newGunship, ("KodiakUpdate failed to find or create a gunship object")); + if (newGunship) + { + //PRODUCER + newGunship->setProducer(getObject()); + + //POSITION + Coord3D creationCoord; + switch (data->m_createLoc) + { + case CREATE_GUNSHIP_AT_EDGE_NEAR_SOURCE: + creationCoord = TheTerrainLogic->findClosestEdgePoint(getObject()->getPosition()); + break; + case CREATE_GUNSHIP_AT_EDGE_FARTHEST_FROM_SOURCE: + creationCoord = TheTerrainLogic->findFarthestEdgePoint(getObject()->getPosition()); + break; + case CREATE_GUNSHIP_AT_EDGE_NEAR_TARGET: + creationCoord = TheTerrainLogic->findClosestEdgePoint(targetPos); + break; + case CREATE_GUNSHIP_AT_EDGE_FARTHEST_FROM_TARGET: + default: + creationCoord = TheTerrainLogic->findFarthestEdgePoint(targetPos); + break; + } + + // HERE WE NEED TO CREATE THE POINT FURTHER OFF THE MAP SO WE CANT SEE THE LAME HOVER AND ACCELLERATE BEHAVIOR + Coord3D deltaToCreationPoint = m_initialTargetPosition; + deltaToCreationPoint.sub(&creationCoord); + Real distanceFromTarget = deltaToCreationPoint.length(); + deltaToCreationPoint.normalize(); + deltaToCreationPoint.x *= (distanceFromTarget + data->m_gunshipOrbitRadius); + deltaToCreationPoint.y *= (distanceFromTarget + data->m_gunshipOrbitRadius); + creationCoord.x = m_initialTargetPosition.x - deltaToCreationPoint.x; + creationCoord.y = m_initialTargetPosition.y - deltaToCreationPoint.y; + + Real preferredElevation = newGunship->getAI()->getCurLocomotor()->getPreferredHeight(); + creationCoord.z = preferredElevation; + newGunship->setPosition(&creationCoord); + + //ORIENTATION + Real orient = atan2(m_initialTargetPosition.y - creationCoord.y, m_initialTargetPosition.x - creationCoord.x); + newGunship->setOrientation(orient); + + // ID + m_gunshipID = newGunship->getID(); + + // FIRE THE SPECIAL POWER OF THE GUNSHIP + SpecialPowerModuleInterface* shipSPMInterface = newGunship->getSpecialPowerModule(specialPowerTemplate); + if (shipSPMInterface) + { + SpecialPowerModule* spModule = (SpecialPowerModule*)shipSPMInterface; + spModule->markSpecialPowerTriggered(&m_initialTargetPosition); + spModule->doSpecialPowerAtLocation(&m_initialTargetPosition, INVALID_ANGLE, commandOptions); + + } + + // MAKE THE GUNSHIP SELECTED (Update: Now only for the local player) + + // TheGameLogic->selectObject(newGunship, TRUE, getObject()->getControllingPlayer()->getPlayerMask(), TRUE); + TheGameLogic->selectObject(newGunship, TRUE, getObject()->getControllingPlayer()->getPlayerMask(), getObject()->isLocallyControlled()); + + + } + + + SpecialPowerModuleInterface* spmInterface = getObject()->getSpecialPowerModule(specialPowerTemplate); + if (spmInterface) + { + SpecialPowerModule* spModule = (SpecialPowerModule*)spmInterface; + spModule->markSpecialPowerTriggered(&m_initialTargetPosition); + } + + return TRUE; +} + + + + +//------------------------------------------------------------------------------------------------- +/** The update callback. */ +//------------------------------------------------------------------------------------------------- +UpdateSleepTime KodiakDeploymentUpdate::update() +{ +// const SpectreGunshipDeploymentUpdateModuleData *data = getSpectreGunshipDeploymentUpdateModuleData(); + + + Object *me = getObject(); + // Abort conditions. + if( me->testStatus(OBJECT_STATUS_SOLD) + || me->testStatus(OBJECT_STATUS_UNDER_CONSTRUCTION) + || me->isEffectivelyDead() ) + { + + return UPDATE_SLEEP_FOREVER; + } + + return UPDATE_SLEEP_NONE; + +} + + + + + + + + + + +// ------------------------------------------------------------------------------------------------ +/** CRC */ +// ------------------------------------------------------------------------------------------------ +void KodiakDeploymentUpdate::crc( Xfer *xfer ) +{ + + // extend base class + UpdateModule::crc( xfer ); + +} // end crc + +// ------------------------------------------------------------------------------------------------ +/** Xfer method + * Version Info: + * 1: Initial version */ +// ------------------------------------------------------------------------------------------------ +void KodiakDeploymentUpdate::xfer( Xfer *xfer ) +{ +// const SpectreGunshipDeploymentUpdateModuleData *data = getSpectreGunshipDeploymentUpdateModuleData(); + + // version + XferVersion currentVersion = 1; + XferVersion version = currentVersion; + xfer->xferVersion( &version, currentVersion ); + + // extend base class + UpdateModule::xfer( xfer ); + xfer->xferObjectID( &m_gunshipID ); + +} // end xfer + +// ------------------------------------------------------------------------------------------------ +/** Load post process */ +// ------------------------------------------------------------------------------------------------ +void KodiakDeploymentUpdate::loadPostProcess( void ) +{ + // extend base class + UpdateModule::loadPostProcess(); + +} // end loadPostProcess diff --git a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Update/KodiakUpdate.cpp b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Update/KodiakUpdate.cpp new file mode 100644 index 00000000000..aa8fadd5f08 --- /dev/null +++ b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Update/KodiakUpdate.cpp @@ -0,0 +1,955 @@ +// FILE: KodiakUpdate.cpp ////////////////////////////////////////////////////////////////////////// +// Desc: Update module to handle weapon firing of the Kodiak 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/KodiakUpdate.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" + + +#define ONE (1.0f) +#define ZERO (0.0f) +#define LOTS_OF_SHOTS (9999) + + +//------------------------------------------------------------------------------------------------- +//------------------------------------------------------------------------------------------------- +KodiakUpdateModuleData::KodiakUpdateModuleData() +{ + 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_missileLockRadius = 50.0f; + m_mainTargetingRate = 100; + + m_sideTargetingRate = 100; + m_aaTargetingRate = 100; + + m_attackAreaRadius = 250.0f; + m_sideAttackAreaRadius = 250.0f; + m_aaAttackAreaRadius = 250.0f; + m_missileScatterRadius = 250.0f; + + m_numMainTurrets = 1; + m_numSideTurrets = 0; + m_numAATurrets = 0; + m_turretRecenterFramesBeforeExit = 0; + m_initialAttackDelayFrames = 0; +} + +static Real zero = 0.0f; +//------------------------------------------------------------------------------------------------- +/*static*/ void KodiakUpdateModuleData::buildFieldParse(MultiIniFieldParse& p) +{ + ModuleData::buildFieldParse(p); + + static const FieldParse dataFieldParse[] = + { + { "SpecialPowerTemplate", INI::parseSpecialPowerTemplate, NULL, offsetof( KodiakUpdateModuleData, m_specialPowerTemplate ) }, + { "MainTargetingRate", INI::parseDurationUnsignedInt, NULL, offsetof( KodiakUpdateModuleData, m_mainTargetingRate ) }, + { "SideTargetingRate", INI::parseDurationUnsignedInt, NULL, offsetof( KodiakUpdateModuleData, m_sideTargetingRate ) }, + { "AATargetingRate", INI::parseDurationUnsignedInt, NULL, offsetof( KodiakUpdateModuleData, m_aaTargetingRate ) }, + { "OrbitTime", INI::parseDurationUnsignedInt, NULL, offsetof( KodiakUpdateModuleData, m_orbitFrames ) }, + { "AttackAreaRadius", INI::parseReal, NULL, offsetof( KodiakUpdateModuleData, m_attackAreaRadius ) }, + { "SideAttackAreaRadius", INI::parseReal, NULL, offsetof(KodiakUpdateModuleData, m_sideAttackAreaRadius) }, + { "AAAttackAreaRadius", INI::parseReal, NULL, offsetof(KodiakUpdateModuleData, m_aaAttackAreaRadius) }, + { "TargetingReticleRadius", INI::parseReal, NULL, offsetof( KodiakUpdateModuleData, m_targetingReticleRadius ) }, + { "GunshipOrbitRadius", INI::parseReal, NULL, offsetof( KodiakUpdateModuleData, m_gunshipOrbitRadius ) }, + { "MissileLockRadius", INI::parseReal, NULL, offsetof( KodiakUpdateModuleData, m_missileLockRadius) }, + { "AttackAreaDecal", RadiusDecalTemplate::parseRadiusDecalTemplate, NULL, offsetof( KodiakUpdateModuleData, m_attackAreaDecalTemplate ) }, + { "TargetingReticleDecal", RadiusDecalTemplate::parseRadiusDecalTemplate, NULL, offsetof( KodiakUpdateModuleData, m_targetingReticleDecalTemplate ) }, + { "ScatterTarget", KodiakUpdateModuleData::parseScatterTarget, NULL, 0 }, + { "MainTurrets", INI::parseUnsignedInt, NULL, offsetof(KodiakUpdateModuleData, m_numMainTurrets) }, + { "SideTurrets", INI::parseUnsignedInt, NULL, offsetof(KodiakUpdateModuleData, m_numSideTurrets) }, + { "AATurrets", INI::parseUnsignedInt, NULL, offsetof(KodiakUpdateModuleData, m_numAATurrets) }, + { "MissileScatterRadius", INI::parseReal, NULL, offsetof(KodiakUpdateModuleData, m_missileScatterRadius) }, + { "TurretRecenterTimeBeforeExit", INI::parseDurationUnsignedInt, NULL, offsetof(KodiakUpdateModuleData, m_turretRecenterFramesBeforeExit) }, + { "InitialAttackDelay", INI::parseDurationUnsignedInt, NULL, offsetof(KodiakUpdateModuleData, m_initialAttackDelayFrames) }, + + + { 0, 0, 0, 0 } + }; + p.add(dataFieldParse); +} + +/*static*/ void KodiakUpdateModuleData::parseScatterTarget(INI* ini, void* instance, void* /*store*/, const void* /*userData*/) +{ + // Accept multiple listings of Coord2D's. + KodiakUpdateModuleData* self = (KodiakUpdateModuleData*)instance; + + Coord2D target; + target.x = 0; + target.y = 0; + INI::parseCoord2D(ini, NULL, &target, NULL); + + self->m_scatterTargets.push_back(target); +} + +//------------------------------------------------------------------------------------------------- +KodiakUpdate::KodiakUpdate( Thing *thing, const ModuleData* moduleData ) : SpecialPowerUpdateModule( thing, moduleData ) +{ + m_specialPowerModule = NULL; + m_status = GUNSHIP_STATUS_IDLE; + m_initialTargetPosition.zero(); + m_overrideTargetDestination.zero(); + m_positionToShootAt.zero(); + m_attackAreaDecal.clear(); + m_targetingReticleDecal.clear(); + m_orbitEscapeFrame = 0; + m_afterburnerSound = *(getObject()->getTemplate()->getPerUnitSound("Afterburner")); + +#if defined TRACKERS +m_howitzerTrackerDecal.clear(); +#endif + +} + +//------------------------------------------------------------------------------------------------- +KodiakUpdate::~KodiakUpdate( 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 KodiakUpdate::onObjectCreated() +{ + const KodiakUpdateModuleData *data = getKodiakUpdateModuleData(); + Object *obj = getObject(); + + if( !data->m_specialPowerTemplate ) + { + DEBUG_CRASH( ("%s object's KodiakUpdate lacks access to the SpecialPowerTemplate. Needs to be specified in ini.", obj->getTemplate()->getName().str() ) ); + return; + } + + m_specialPowerModule = obj->getSpecialPowerModule( data->m_specialPowerTemplate ); + m_movementPosition = Coord3D(0, 0, 0); +} + +//------------------------------------------------------------------------------------------------- +Bool KodiakUpdate::initiateIntentToDoSpecialPower(const SpecialPowerTemplate *specialPowerTemplate, const Object *targetObj, const Coord3D *targetPos, const Waypoint *way, UnsignedInt commandOptions ) +{ + const KodiakUpdateModuleData *data = getKodiakUpdateModuleData(); + + if( m_specialPowerModule->getSpecialPowerTemplate() != specialPowerTemplate ) + { + //Check to make sure our modules are connected. + return FALSE; + } + + if( !BitIsSet( commandOptions, COMMAND_FIRED_BY_SCRIPT ) ) + { + m_initialTargetPosition.set( targetPos ); + m_overrideTargetDestination.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 + + } + + + 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 KodiakUpdate::isPowerCurrentlyInUse( const CommandButton *command ) const +{ + return false; +} + +//------------------------------------------------------------------------------------------------- +void KodiakUpdate::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 KodiakUpdate::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(RTS_DEBUG) + virtual const char* debugGetName() { return "PartitionFilterLiveMapEnemies"; } +#endif +}; + +class PartitionFilterLiveMapAirEnemies : public PartitionFilter +{ +private: + const Object* m_obj; +public: + PartitionFilterLiveMapAirEnemies(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; + + if (!objOther->isAirborneTarget()) { + return false; + } + return true; + } + +#if defined(RTS_DEBUG) + virtual const char* debugGetName() { return "PartitionFilterLiveMapAirEnemies"; } +#endif +}; +//----------------------------------------------------------------------------- + + + + + +//------------------------------------------------------------------------------------------------- +/** The update callback. */ +//------------------------------------------------------------------------------------------------- +UpdateSleepTime KodiakUpdate::update() +{ + const KodiakUpdateModuleData *data = getKodiakUpdateModuleData(); + + int total_turrets = data->m_numMainTurrets + data->m_numSideTurrets + data->m_numAATurrets; + + 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(); + + // get the turrets + std::vector turrets; + turrets.reserve(total_turrets); + + ContainModuleInterface* cmi = getObject()->getContain(); + if (cmi != nullptr) { + ContainedItemsList* addOns = cmi->getAddOnList(); + for (ContainedItemsList::iterator it = addOns->begin(); it != addOns->end(); it++) { + turrets.push_back(*it); + } + } + + + + if ( m_status == GUNSHIP_STATUS_INSERTING || m_status == GUNSHIP_STATUS_ORBITING ) + { + Coord3D pos = *gunship->getPosition(); + pos.sub( &m_initialTargetPosition ); + pos.z = zero; + Real distanceToTarget = pos.length(); + + Real orbitalRadius = data->m_gunshipOrbitRadius; + + Coord3D zero_pos; + zero_pos.zero(); + if (m_movementPosition.equals(zero_pos)) { + // set the direction + Coord3D direction = m_initialTargetPosition; + direction.sub(gunship->getPosition()); + direction.z = zero; + direction.normalize(); + + m_movementPosition = direction; + m_movementPosition.scale(200.0f); + } + + Coord3D target_move_location = *gunship->getPosition(); + target_move_location.add(&m_movementPosition); + + if ( shipAI) + { + shipAI->aiMoveToPosition( &target_move_location, 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; + + 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 && TheGameLogic->getFrame() >= (m_orbitEscapeFrame - data->m_orbitFrames + data->m_initialAttackDelayFrames) ) // delay until attack + { + Object *validTargetObject = NULL; + + + if ( TheGameLogic->getFrame() >= m_orbitEscapeFrame ) + { + cleanUp(); + setLogicalStatus( GUNSHIP_STATUS_DEPARTING ); + + // CEASE FIRE, RETURN TO BASE + disengageAndDepartAO( gunship ); + + + }//endif escapeframe + else if (TheGameLogic->getFrame() >= m_orbitEscapeFrame - data->m_turretRecenterFramesBeforeExit) { + for (auto& turret : turrets) { + AIUpdateInterface* ai = turret->getAI(); + if (ai != nullptr) { + //ai->setTurretEnabled(WhichTurretType::TURRET_MAIN, false); + ai->aiAttackPosition(&m_initialTargetPosition, 0, CMD_FROM_AI); + ai->recenterTurret(WhichTurretType::TURRET_MAIN); + } + } + } + else + { + + // ONLY EVERY FEW FRAMES DO WE RE_EVALUATE THE TARGET OBJECT + if ( TheGameLogic->getFrame() %data->m_mainTargetingRate < 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; + } + } + } + } + + // Order Main turrets to attack location + for (UnsignedInt i = 0; i < data->m_numMainTurrets; i++) { + AIUpdateInterface* ai = turrets.at(i)->getAI(); + if (ai != nullptr) { + ai->aiAttackPosition(&m_positionToShootAt, LOTS_OF_SHOTS, CMD_FROM_AI); + } + } + + }//endif frame modulator + + /* Auto Acquire Targets with side Turrets */ + if (data->m_numSideTurrets > 0 && TheGameLogic->getFrame() % data->m_sideTargetingRate < ONE) + { + 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; + + std::vector targets; + targets.reserve(data->m_numSideTurrets); + + ObjectIterator* iter = ThePartitionManager->iterateObjectsInRange(gunship->getPosition(), + data->m_sideAttackAreaRadius, + FROM_BOUNDINGSPHERE_2D, + filters, + ITER_SORTED_CHEAP_TO_EXPENSIVE); + MemoryPoolObjectHolder holder(iter); + for (Object* theEnemy = iter->first(); theEnemy && (targets.size() < data->m_numSideTurrets); theEnemy = iter->next()) + { + targets.push_back(theEnemy); + } + if (!targets.empty()) { + for (UnsignedInt i = 0U; i < data->m_numSideTurrets; i++) { + AIUpdateInterface* ai = turrets.at(i + data->m_numMainTurrets)->getAI(); + if (ai != nullptr) { + Object* target = nullptr; + if (targets.size() > i) { + target = targets.at(i); + } + else { + target = targets.at(targets.size() - 1); + } + ai->aiAttackObject(target, LOTS_OF_SHOTS, CMD_FROM_AI); + } + } + } + } + + //AA TURRET TARGETING + if (data->m_numAATurrets > 0 && TheGameLogic->getFrame() % data->m_aaTargetingRate < ONE) + { + PartitionFilterLiveMapAirEnemies filterObvious(gunship); + PartitionFilterStealthedAndUndetected filterStealth(gunship, false); + PartitionFilterPossibleToAttack filterAttack(ATTACK_NEW_TARGET, turrets.at(data->m_numMainTurrets+data->m_numSideTurrets), 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; + + std::vector targets; + targets.reserve(data->m_numAATurrets); + + ObjectIterator* iter = ThePartitionManager->iterateObjectsInRange(gunship->getPosition(), + data->m_aaAttackAreaRadius, + FROM_BOUNDINGSPHERE_2D, + filters, + ITER_SORTED_CHEAP_TO_EXPENSIVE); + MemoryPoolObjectHolder holder(iter); + for (Object* theEnemy = iter->first(); theEnemy && (targets.size() < data->m_numAATurrets); theEnemy = iter->next()) + { + if (theEnemy) + { + targets.push_back(theEnemy); + } + } + if (!targets.empty()) { + for (UnsignedInt i = 0U; i < data->m_numAATurrets; i++) { + AIUpdateInterface* ai = turrets.at(i + data->m_numMainTurrets + data->m_numSideTurrets)->getAI(); + if (ai != nullptr) { + Object* target = nullptr; + if (targets.size() > i) { + target = targets.at(i); + } + else { + target = targets.at(targets.size() - 1); + } + ai->aiAttackObject(target, LOTS_OF_SHOTS, CMD_FROM_AI); + } + } + } + } + + //Missile Barrage + Weapon* weap = getObject()->getWeaponInWeaponSlot(WeaponSlotType::PRIMARY_WEAPON); + if (weap != nullptr) { + if (weap->getPossibleNextShotFrame() <= TheGameLogic->getFrame()) { + + int shot_index = weap->getClipSize() - weap->getRemainingAmmo(); + if (!data->m_scatterTargets.empty()) { + size_t target_idx = shot_index % data->m_scatterTargets.size(); + Coord2D scatterOffset = data->m_scatterTargets.at(target_idx); + Coord3D targetPos = m_initialTargetPosition; + //Calculate Target from Scatter + + scatterOffset.x *= data->m_missileScatterRadius; + scatterOffset.y *= data->m_missileScatterRadius; + + const Coord3D srcPos = *getObject()->getPosition(); + Real angle = 0.0f; // getObject()->getOrientation(); + angle += atan2(targetPos.y - srcPos.y, targetPos.x - srcPos.x); + + Real cosA = Cos(angle); + Real sinA = Sin(angle); + Real scatterOffsetRotX = scatterOffset.x * cosA - scatterOffset.y * sinA; + Real scatterOffsetRotY = scatterOffset.x * sinA + scatterOffset.y * cosA; + scatterOffset.x = scatterOffsetRotX; + scatterOffset.y = scatterOffsetRotY; + + targetPos.x += scatterOffset.x; + targetPos.y += scatterOffset.y; + targetPos.z = TheTerrainLogic->getGroundHeight(targetPos.x, targetPos.y); + + + // Check for a valid target near impact + 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; + + Object* targetLock = nullptr; + // THIS WILL FIND A VALID TARGET WITHIN THE TARGETING RETICLE + ObjectIterator* iter = ThePartitionManager->iterateObjectsInRange(&targetPos, + data->m_missileLockRadius, + FROM_BOUNDINGSPHERE_2D, + filters, + ITER_SORTED_EXPENSIVE_TO_CHEAP); + MemoryPoolObjectHolder holder(iter); + for (Object* theEnemy = iter->first(); theEnemy && targetLock == nullptr; theEnemy = iter->next()) + { + if (theEnemy) + { + targetLock = theEnemy; + } + } + + if (targetLock != nullptr) { + weap->fireWeapon(getObject(), targetLock); + } + else { + weap->fireWeapon(getObject(), &targetPos); + } + } + else { + weap->fireWeapon(getObject(), getObject()->getPosition()); + } + } + + } + + }// 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 KodiakUpdate::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 KodiakUpdate::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() > getKodiakUpdateModuleData()->m_gunshipOrbitRadius); + +} + + + + + + + +//------------------------------------------------------------------------------------------------- +void KodiakUpdate::cleanUp() +{ + m_attackAreaDecal.clear(); + m_targetingReticleDecal.clear(); + +#if defined TRACKERS + m_howitzerTrackerDecal.clear(); +#endif +} + + + + + +// -------------------------------------------------------------------------------------------------- +void KodiakUpdate::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 ); + + + + } + + 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 KodiakUpdate::doesSpecialPowerHaveOverridableDestinationActive() const +{ + return m_status < GUNSHIP_STATUS_DEPARTING; +} + + +// ------------------------------------------------------------------------------------------------ +/** CRC */ +// ------------------------------------------------------------------------------------------------ +void KodiakUpdate::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 KodiakUpdate::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_movementPosition ); + // 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_positionToShootAt ); + } + +} // end xfer + +// ------------------------------------------------------------------------------------------------ +/** Load post process */ +// ------------------------------------------------------------------------------------------------ +void KodiakUpdate::loadPostProcess( void ) +{ + + // extend base class + UpdateModule::loadPostProcess(); + +} // end loadPostProcess diff --git a/GeneralsMD/Code/Main/WinMain.cpp b/GeneralsMD/Code/Main/WinMain.cpp index a38545549ac..13ecdf162e8 100644 --- a/GeneralsMD/Code/Main/WinMain.cpp +++ b/GeneralsMD/Code/Main/WinMain.cpp @@ -778,7 +778,7 @@ void WaitForDebugger() // Once the debugger is attached, IsDebuggerPresent() returns true, // the loop exits, and we break into the debugger. - __debugbreak(); // MSVC Compiler Intrinsic to cause a breakpoint. + //__debugbreak(); // MSVC Compiler Intrinsic to cause a breakpoint. // This is often more robust than DebugBreak() as it doesn't // rely on a specific SDK header for DebugBreak() itself. #endif // _DEBUG @@ -804,7 +804,7 @@ Int APIENTRY WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, { Int exitcode = 1; //#ifdef _DEBUG -// WaitForDebugger(); //in debug build, wait for debugger attachment + // WaitForDebugger(); //in debug build, wait for debugger attachment //#endif #ifdef RTS_PROFILE