From 0343ed512802904800c779b8eb168ed1f187cfdd Mon Sep 17 00:00:00 2001 From: pWn3d Date: Sat, 6 Sep 2025 19:31:44 +0200 Subject: [PATCH 1/7] First implementation, copy of spectre update --- .../System/GameMemoryInitPools_GeneralsMD.inl | 2 + GeneralsMD/Code/GameEngine/CMakeLists.txt | 4 + .../GameLogic/Module/KodiakDeploymentUpdate.h | 129 +++ .../Include/GameLogic/Module/KodiakUpdate.h | 155 +++ .../Source/Common/Thing/ModuleFactory.cpp | 4 + .../Object/Update/KodiakDeploymentUpdate.cpp | 302 ++++++ .../GameLogic/Object/Update/KodiakUpdate.cpp | 888 ++++++++++++++++++ GeneralsMD/Code/Main/WinMain.cpp | 8 +- 8 files changed, 1488 insertions(+), 4 deletions(-) create mode 100644 GeneralsMD/Code/GameEngine/Include/GameLogic/Module/KodiakDeploymentUpdate.h create mode 100644 GeneralsMD/Code/GameEngine/Include/GameLogic/Module/KodiakUpdate.h create mode 100644 GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Update/KodiakDeploymentUpdate.cpp create mode 100644 GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Update/KodiakUpdate.cpp diff --git a/Core/GameEngine/Source/Common/System/GameMemoryInitPools_GeneralsMD.inl b/Core/GameEngine/Source/Common/System/GameMemoryInitPools_GeneralsMD.inl index 49c4f6fe416..e3ca7946e90 100644 --- a/Core/GameEngine/Source/Common/System/GameMemoryInitPools_GeneralsMD.inl +++ b/Core/GameEngine/Source/Common/System/GameMemoryInitPools_GeneralsMD.inl @@ -185,6 +185,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 6ef78dbac79..799b229e623 100644 --- a/GeneralsMD/Code/GameEngine/CMakeLists.txt +++ b/GeneralsMD/Code/GameEngine/CMakeLists.txt @@ -490,6 +490,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 @@ -1080,6 +1082,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/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..6bc51ee510b --- /dev/null +++ b/GeneralsMD/Code/GameEngine/Include/GameLogic/Module/KodiakUpdate.h @@ -0,0 +1,155 @@ +// 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; + WeaponTemplate *m_howitzerWeaponTemplate; +// AsciiString m_gunshipTemplateName; + AsciiString m_gattlingTemplateName; +// AsciiString m_howitzerTemplateName; + RadiusDecalTemplate m_attackAreaDecalTemplate; + RadiusDecalTemplate m_targetingReticleDecalTemplate; + UnsignedInt m_orbitFrames; + UnsignedInt m_howitzerFiringRate; + UnsignedInt m_howitzerFollowLag; + Real m_attackAreaRadius; + Real m_targetingReticleRadius; + Real m_gunshipOrbitRadius; + Real m_strafingIncrement; + Real m_orbitInsertionSlope; + Real m_randomOffsetForHowitzer; + + const ParticleSystemTemplate * m_gattlingStrafeFXParticleSystem; + + KodiakUpdateModuleData(); + static void buildFieldParse(MultiIniFieldParse& p); + +private: + +}; + +//enum GunshipStatus CPP_11(: Int) +//{ +// GUNSHIP_STATUS_INSERTING, +// GUNSHIP_STATUS_ORBITING, +// GUNSHIP_STATUS_DEPARTING, +// GUNSHIP_STATUS_IDLE, +//}; + + +//------------------------------------------------------------------------------------------------- +/** 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 ); } + +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_satellitePosition; + Coord3D m_gattlingTargetPosition; + Coord3D m_positionToShootAt; + + + GunshipStatus m_status; + + UnsignedInt m_okToFireHowitzerCounter; + UnsignedInt m_orbitEscapeFrame; + + +// ObjectID m_howitzerID; + ObjectID m_gattlingID; + + + RadiusDecal m_attackAreaDecal; + RadiusDecal m_targetingReticleDecal; + + + + +#if defined TRACKERS + RadiusDecal m_howitzerTrackerDecal; +#endif + + AudioEventRTS m_afterburnerSound; + AudioEventRTS m_howitzerFireSound; + +}; + + +#endif // __SPECTRE_GUNSHIP_UPDATE_H_ + diff --git a/GeneralsMD/Code/GameEngine/Source/Common/Thing/ModuleFactory.cpp b/GeneralsMD/Code/GameEngine/Source/Common/Thing/ModuleFactory.cpp index 3db7e5df038..8c09c861991 100644 --- a/GeneralsMD/Code/GameEngine/Source/Common/Thing/ModuleFactory.cpp +++ b/GeneralsMD/Code/GameEngine/Source/Common/Thing/ModuleFactory.cpp @@ -144,6 +144,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" @@ -448,6 +450,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..d1ce6272eae --- /dev/null +++ b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Update/KodiakUpdate.cpp @@ -0,0 +1,888 @@ +// 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 ORBIT_INSERTION_SLOPE_MAX (0.8f) +#define ORBIT_INSERTION_SLOPE_MIN (0.5f) +#define ONE (1.0f) +#define ZERO (0.0f) +#define LOTS_OF_SHOTS (9999) + + + + +//------------------------------------------------------------------------------------------------- +//------------------------------------------------------------------------------------------------- +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_strafingIncrement = 20.0f; + m_orbitInsertionSlope = 0.7f; + m_howitzerFiringRate = 10; + m_howitzerFollowLag = 0; + m_randomOffsetForHowitzer = 20.0f; +} + +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 ) }, + { "GattlingTemplateName", INI::parseAsciiString, NULL, offsetof( KodiakUpdateModuleData, m_gattlingTemplateName ) }, + { "HowitzerFiringRate", INI::parseDurationUnsignedInt, NULL, offsetof( KodiakUpdateModuleData, m_howitzerFiringRate ) }, + { "OrbitTime", INI::parseDurationUnsignedInt, NULL, offsetof( KodiakUpdateModuleData, m_orbitFrames ) }, + { "HowitzerFollowLag", INI::parseDurationUnsignedInt, NULL, offsetof( KodiakUpdateModuleData, m_howitzerFollowLag ) }, + { "AttackAreaRadius", INI::parseReal, NULL, offsetof( KodiakUpdateModuleData, m_attackAreaRadius ) }, + { "StrafingIncrement", INI::parseReal, NULL, offsetof( KodiakUpdateModuleData, m_strafingIncrement ) }, + { "OrbitInsertionSlope", INI::parseReal, NULL, offsetof( KodiakUpdateModuleData, m_orbitInsertionSlope ) }, + { "RandomOffsetForHowitzer", INI::parseReal, NULL, offsetof( KodiakUpdateModuleData, m_randomOffsetForHowitzer ) }, + { "TargetingReticleRadius", INI::parseReal, NULL, offsetof( KodiakUpdateModuleData, m_targetingReticleRadius ) }, + { "GunshipOrbitRadius", INI::parseReal, NULL, offsetof( KodiakUpdateModuleData, m_gunshipOrbitRadius ) }, + { "HowitzerWeaponTemplate", INI::parseWeaponTemplate, NULL, offsetof( KodiakUpdateModuleData, m_howitzerWeaponTemplate ) }, + { "GattlingStrafeFXParticleSystem", INI::parseParticleSystemTemplate, NULL, offsetof( KodiakUpdateModuleData, m_gattlingStrafeFXParticleSystem ) }, + { "AttackAreaDecal", RadiusDecalTemplate::parseRadiusDecalTemplate, NULL, offsetof( KodiakUpdateModuleData, m_attackAreaDecalTemplate ) }, + { "TargetingReticleDecal", RadiusDecalTemplate::parseRadiusDecalTemplate, NULL, offsetof( KodiakUpdateModuleData, m_targetingReticleDecalTemplate ) }, + + + + + { 0, 0, 0, 0 } + }; + p.add(dataFieldParse); +} + +//------------------------------------------------------------------------------------------------- +KodiakUpdate::KodiakUpdate( Thing *thing, const ModuleData* moduleData ) : SpecialPowerUpdateModule( thing, moduleData ) +{ + m_specialPowerModule = NULL; + m_gattlingID = INVALID_ID; + m_status = GUNSHIP_STATUS_IDLE; + m_initialTargetPosition.zero(); + m_overrideTargetDestination.zero(); + m_gattlingTargetPosition.zero(); + m_positionToShootAt.zero(); + m_attackAreaDecal.clear(); + m_targetingReticleDecal.clear(); + m_orbitEscapeFrame = 0; + m_afterburnerSound = *(getObject()->getTemplate()->getPerUnitSound("Afterburner")); + m_howitzerFireSound = *(getObject()->getTemplate()->getPerUnitSound("HowitzerFire")); + + m_okToFireHowitzerCounter = 0; + +#if defined TRACKERS +m_howitzerTrackerDecal.clear(); +#endif + +} + +//------------------------------------------------------------------------------------------------- +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_satellitePosition.set( obj->getPosition() ); +} + +//------------------------------------------------------------------------------------------------- +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 ); + m_gattlingTargetPosition.set( targetPos ); + } + else + { + UnsignedInt now = TheGameLogic->getFrame(); + m_specialPowerModule->setReadyFrame( now ); + m_initialTargetPosition.set( targetPos ); + setLogicalStatus( GUNSHIP_STATUS_INSERTING ); + } + + + Object * gunShip = getObject(); + + + if ( gunShip ) + { + // MAKE TWEAKS TO AI SO SHIP MOVES PRETTY + AIUpdateInterface *shipAI = gunShip->getAIUpdateInterface(); + if ( shipAI) + { + shipAI->chooseLocomotorSet( LOCOMOTORSET_PANIC ); + shipAI->getCurLocomotor()->setAllowInvalidPosition(TRUE); + shipAI->getCurLocomotor()->setUltraAccurate(TRUE); // set ultra-accurate just so AI won't try to adjust our dest + } + + Drawable *draw = gunShip->getDrawable(); + if ( draw ) + draw->clearAndSetModelConditionState( MODELCONDITION_DOOR_1_OPENING, MODELCONDITION_DOOR_1_CLOSING ); + friend_enableAfterburners(TRUE); + + setLogicalStatus( GUNSHIP_STATUS_INSERTING ); // The gunship is en route to the tharget area, from map-edge + + + // TELL THE GUNNERS ABOARD THE GUNSHIP TO HOLD THEIR FIRE UNTIL ORBIT INSERTION + ContainModuleInterface *shipContain = gunShip->getContain(); + if ( shipContain ) + { + + Object *newGattling = TheGameLogic->findObjectByID( m_gattlingID ); + const ThingTemplate *gattlingTemplate = TheThingFactory->findTemplate( data->m_gattlingTemplateName ); + if( newGattling != NULL ) + { + m_gattlingID = INVALID_ID; + newGattling = NULL; + } + if ( gattlingTemplate ) + { + newGattling = TheThingFactory->newObject( gattlingTemplate, getObject()->getTeam() ); + DEBUG_ASSERTCRASH( gunShip, ("SpecterGunshipUpdate failed to find or create a GATTLING object")); + shipContain->addToContain( newGattling ); + m_gattlingID = newGattling->getID(); + } + + + Object *gattling = TheGameLogic->findObjectByID( m_gattlingID ); + if ( gattling ) + gattling->setDisabled ( DISABLED_PARALYZED ); + + } + + } + + + data->m_attackAreaDecalTemplate.createRadiusDecal( *getObject()->getPosition(), data->m_attackAreaRadius, getObject()->getControllingPlayer(), m_attackAreaDecal); + data->m_targetingReticleDecalTemplate.createRadiusDecal( *getObject()->getPosition(), data->m_targetingReticleRadius, getObject()->getControllingPlayer(), m_targetingReticleDecal); + + +#if defined TRACKERS + data->m_targetingReticleDecalTemplate.createRadiusDecal( *getObject()->getPosition(), data->m_targetingReticleRadius, getObject()->getControllingPlayer(), m_howitzerTrackerDecal); +#endif + + + SpecialPowerModuleInterface *spmInterface = getObject()->getSpecialPowerModule( specialPowerTemplate ); + if( spmInterface ) + { + SpecialPowerModule *spModule = (SpecialPowerModule*)spmInterface; + spModule->markSpecialPowerTriggered( &m_initialTargetPosition ); + } + + return TRUE; +} + +//------------------------------------------------------------------------------------------------- +Bool 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 +}; +//----------------------------------------------------------------------------- + + + + + +//------------------------------------------------------------------------------------------------- +/** The update callback. */ +//------------------------------------------------------------------------------------------------- +UpdateSleepTime KodiakUpdate::update() +{ + const KodiakUpdateModuleData *data = getKodiakUpdateModuleData(); + + Object *gunship = getObject(); + if ( gunship ) + { + + + if ( gunship->isEffectivelyDead() ) + return UPDATE_SLEEP_FOREVER; + + + + + m_attackAreaDecal.update(); + m_targetingReticleDecal.update(); +#if defined TRACKERS + m_howitzerTrackerDecal.update(); +#endif + + AIUpdateInterface *shipAI = gunship->getAIUpdateInterface(); + AIUpdateInterface *gattlingAI = NULL; + + Object *gattling = TheGameLogic->findObjectByID( m_gattlingID ); + if ( gattling ) + { + gattlingAI = gattling->getAIUpdateInterface(); + } + + + + if ( m_status == GUNSHIP_STATUS_INSERTING || m_status == GUNSHIP_STATUS_ORBITING ) + { + + + // init'l apogee + // target *<-------->* + // posi ^\ / + // | \ / + // perigee | \ / + // | */ declination + // | / \ + // v/ * m_satelliteposition + // * + // | + // | + // | + // | + // | + // * gunship->getPosition() + + //perigee is the point in the orbital arc nearest the satellite being captured + Coord3D perigee = *gunship->getPosition(); + perigee.sub( &m_initialTargetPosition ); + perigee.z = zero; + Real distanceToTarget = perigee.length(); + perigee.normalize(); + + //apogee is the anteclockwise point fathest from the perigee line + Coord3D apogee; + apogee.z = zero; + apogee.x = -perigee.y; + apogee.y = perigee.x; + + // declination intersects line [p][a], given an attack slope of n1/n2 + Coord3D declination; + Real n1 = min( ORBIT_INSERTION_SLOPE_MAX, max(ORBIT_INSERTION_SLOPE_MIN, data->m_orbitInsertionSlope) ); + Real n2 = ONE - n1; + declination.z = zero; + declination.x = ( perigee.x * n1 ) + ( apogee.x * n2 ); + declination.y = ( perigee.y * n1 ) + ( apogee.y * n2 ); + + //scale out to the orbital radius + Real orbitalRadius = data->m_gunshipOrbitRadius; + declination.x *= orbitalRadius; + declination.y *= orbitalRadius; + + m_satellitePosition.x = m_initialTargetPosition.x + declination.x; + m_satellitePosition.y = m_initialTargetPosition.y + declination.y; + + if ( shipAI) + { + shipAI->aiMoveToPosition( &m_satellitePosition, CMD_FROM_AI ); + } + + Real constraintRadius = data->m_attackAreaRadius - data->m_targetingReticleRadius; + + //Constrain Target Override to the targeting radius + Coord3D overrideTargetDelta = m_initialTargetPosition; + overrideTargetDelta.sub( &m_overrideTargetDestination ); + if ( overrideTargetDelta.length() > constraintRadius ) + { + overrideTargetDelta.normalize(); + overrideTargetDelta.x *= constraintRadius; + overrideTargetDelta.y *= constraintRadius; + + m_overrideTargetDestination.x = m_initialTargetPosition.x - overrideTargetDelta.x; + m_overrideTargetDestination.y = m_initialTargetPosition.y - overrideTargetDelta.y; + + } + + m_attackAreaDecal.setPosition( m_initialTargetPosition ); + m_targetingReticleDecal.setPosition( m_overrideTargetDestination ); + + +#if defined TRACKERS + m_howitzerTrackerDecal.setPosition( m_gattlingTargetPosition ); +#endif + + + if ( (m_status == GUNSHIP_STATUS_INSERTING) && (distanceToTarget < orbitalRadius ) )// close enough to shoot + { + setLogicalStatus( GUNSHIP_STATUS_ORBITING ); + m_orbitEscapeFrame = TheGameLogic->getFrame() + data->m_orbitFrames; + + Object *gattling = TheGameLogic->findObjectByID( m_gattlingID ); + if ( gattling ) + gattling->clearDisabled ( DISABLED_PARALYZED ); + + AIUpdateInterface *shipAI = gunship->getAIUpdateInterface(); + if ( shipAI) + { + shipAI->chooseLocomotorSet( LOCOMOTORSET_NORMAL ); + shipAI->getCurLocomotor()->setAllowInvalidPosition(TRUE); + shipAI->getCurLocomotor()->setUltraAccurate(TRUE); // set ultra-accurate just so AI won't try to adjust our dest + } + + Drawable *draw = gunship->getDrawable(); + if ( draw ) + draw->clearAndSetModelConditionState( MODELCONDITION_DOOR_1_CLOSING, MODELCONDITION_DOOR_1_OPENING ); + friend_enableAfterburners(FALSE); + + } + + } // endif status == ORBITING || INSERTING + + + if ( m_status == GUNSHIP_STATUS_ORBITING ) + { + Object *validTargetObject = NULL; + + + if ( TheGameLogic->getFrame() >= m_orbitEscapeFrame ) + { + cleanUp(); + setLogicalStatus( GUNSHIP_STATUS_DEPARTING ); + + // CEASE FIRE, RETURN TO BASE + disengageAndDepartAO( gunship ); + + + }//endif escapeframe + else + { + + // ONLY EVERY FEW FRAMES DO WE RE_EVALUATE THE TARGET OBJECT + if ( TheGameLogic->getFrame() %data->m_howitzerFiringRate < ONE ) + { + + m_positionToShootAt = m_overrideTargetDestination; // unless we get a hit, below + + PartitionFilterLiveMapEnemies filterObvious( gunship ); + PartitionFilterStealthedAndUndetected filterStealth( gunship, false ); + PartitionFilterPossibleToAttack filterAttack(ATTACK_NEW_TARGET, gunship, CMD_FROM_AI); + PartitionFilterFreeOfFog filterFogged( gunship->getControllingPlayer()->getPlayerIndex() ); + PartitionFilter *filters[6]; + Int numFilters = 0; + filters[numFilters++] = &filterObvious; + filters[numFilters++] = &filterStealth; + filters[numFilters++] = &filterAttack; + filters[numFilters++] = &filterFogged; + filters[numFilters] = NULL; + + + + // THIS WILL FIND A VALID TARGET WITHIN THE TARGETING RETICLE + ObjectIterator *iter = ThePartitionManager->iterateObjectsInRange(&m_overrideTargetDestination, + data->m_targetingReticleRadius, + FROM_BOUNDINGSPHERE_2D, + filters, + ITER_SORTED_NEAR_TO_FAR); + MemoryPoolObjectHolder holder(iter); + for (Object *theEnemy = iter->first(); theEnemy; theEnemy = iter->next()) + { + if ( theEnemy && isFairDistanceFromShip( theEnemy ) ) + { + validTargetObject = theEnemy; + break; + } + } + + + + // WE WANT THE WIDE_RANGE AUTOACQUIRE POWER DISABLED FOR HUMAN PLAYERS + // SO THAT THE SPECTREGUNSHIP REQUIRES BABYSITTING AT ALL TIMES + if (gunship->getControllingPlayer()->getPlayerType() != PLAYER_HUMAN ) + { + if ( ! validTargetObject ) + { + // set a flag to start the targeting decal fading, since there is nothing to kill there + // THIS WILL FIND A VALID TARGET ANYWHERE INSIDE THE TARGETING AREA (THE BIG CIRCLE) + ObjectIterator *iter = ThePartitionManager->iterateObjectsInRange(&m_initialTargetPosition, + data->m_attackAreaRadius, + FROM_BOUNDINGSPHERE_2D, + filters, + ITER_SORTED_NEAR_TO_FAR); + MemoryPoolObjectHolder holder(iter); + for (Object *theEnemy = iter->first(); theEnemy; theEnemy = iter->next()) + { + if ( theEnemy && isFairDistanceFromShip( theEnemy ) ) + { + // WE GOT A HIT!!!! SHOOT HIM! + validTargetObject = theEnemy; + m_positionToShootAt = *validTargetObject->getPosition(); + + break; + } + } + } + } + + + + //lets keep a constant barrage of gattling bullets on the current aim location + if( gattlingAI ) + { + if ( validTargetObject )// either in the reticle or the targeting area + gattlingAI->aiAttackObject( validTargetObject, LOTS_OF_SHOTS, CMD_FROM_AI ); + else + gattlingAI->aiAttackPosition( &m_gattlingTargetPosition, LOTS_OF_SHOTS, CMD_FROM_AI ); + } + + + + + // IF THE GATTLING GUN HAS BEEN PLINKING THE TARGET LONG ENOUGH, LETS FOLLOW WITH A VOLLEY OF HOWITZER FIRE + if ( m_okToFireHowitzerCounter > data->m_howitzerFollowLag ) + { + WeaponTemplate *wt = data->m_howitzerWeaponTemplate; + if( wt ) + { + Coord3D attackPositionWithRandomOffset; + Real offs = data->m_randomOffsetForHowitzer; + attackPositionWithRandomOffset.x = m_gattlingTargetPosition.x + GameLogicRandomValue( -offs, offs ); + attackPositionWithRandomOffset.y = m_gattlingTargetPosition.y + GameLogicRandomValue( -offs, offs ); + attackPositionWithRandomOffset.z = m_gattlingTargetPosition.z; + TheWeaponStore->createAndFireTempWeapon( wt, gunship, &attackPositionWithRandomOffset ); + + m_howitzerFireSound.setObjectID(gunship->getID()); + TheAudio->addAudioEvent( &m_howitzerFireSound ); + + } + } + + + }//endif frame modulator + + + + + + // GATTLING TARGETING LOGIC------------------------------------------ + const ParticleSystemTemplate *tmp = data->m_gattlingStrafeFXParticleSystem; + if (tmp && gattling && gattling->testStatus( OBJECT_STATUS_IS_FIRING_WEAPON) ) + { + + + + // I am going to wind my gattling gun toward the next good spot, + //whether an object's position or a cursor position + + + Coord3D delta = m_positionToShootAt; + delta.sub( &m_gattlingTargetPosition ); + Real dist = delta.length(); + if ( dist < data->m_strafingIncrement ) + { + m_gattlingTargetPosition = m_positionToShootAt; + ++m_okToFireHowitzerCounter; + } + else + { + m_okToFireHowitzerCounter = ZERO; + delta.normalize(); + delta.scale( data->m_strafingIncrement ); + m_gattlingTargetPosition.add( &delta ); + } + + + const Player *localPlayer = ThePlayerList->getLocalPlayer(); + + //Make sure the gunship is visible to the player before drawing effects. + if ( gunship->getShroudedStatus( localPlayer->getPlayerIndex() ) <= OBJECTSHROUD_PARTIAL_CLEAR ) + { + + // This makes the client smoke effects of the gattling cannon strafing the ground toward the attack position + ParticleSystem *sys = TheParticleSystemManager->createParticleSystem(tmp); + if (sys) + { + Coord3D impactPosition; + impactPosition.x = m_gattlingTargetPosition.x + GameClientRandomValueReal( -5.0f, 5.0f ); + impactPosition.y = m_gattlingTargetPosition.y + GameClientRandomValueReal( -5.0f, 5.0f ); + impactPosition.z = TheTerrainLogic->getGroundHeight( impactPosition.x, impactPosition.y ); + sys->setPosition( &impactPosition ); + + } + } + + } + }// end else + + + }//not orbiting + else if ( m_status == GUNSHIP_STATUS_DEPARTING ) + { + if ( isPointOffMap( *gunship->getPosition() ) ) + { + + TheGameLogic->destroyObject( gunship ); + setLogicalStatus( GUNSHIP_STATUS_IDLE ); + + cleanUp(); + + // HERE WE NEED TO CLEAN UP THE TERRAIN DECALS, AND ANYTHING ELSE THAT WAS CREATED IN INIT-INTENT[] + + } + } + + } // endif gunship + else if ( m_status != GUNSHIP_STATUS_IDLE ) + { + //OH MY GOODNESS, THE GUNSHIP MUST HAVE GOTTEN SHOT DOWN! + setLogicalStatus( GUNSHIP_STATUS_IDLE ); + + cleanUp(); + } + + + + + + + + + + + return UPDATE_SLEEP_NONE; + +} + + +//------------------------------------------------------------------------------------------------- +void 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 * 0.75f ); + +} + + + + + + + +//------------------------------------------------------------------------------------------------- +void KodiakUpdate::cleanUp() +{ + m_attackAreaDecal.clear(); + m_targetingReticleDecal.clear(); + + + Object *gattling = TheGameLogic->findObjectByID( m_gattlingID ); + if ( gattling ) + TheGameLogic->destroyObject( gattling ); + +#if defined TRACKERS + m_howitzerTrackerDecal.clear(); +#endif +} + + + + + +// -------------------------------------------------------------------------------------------------- +void 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 ); + + + + } + + + Object *gattling = TheGameLogic->findObjectByID( m_gattlingID ); + if ( gattling ) + gattling->setDisabled ( DISABLED_PARALYZED ); + + + if ( shipAI) + { + shipAI->chooseLocomotorSet( LOCOMOTORSET_PANIC ); + shipAI->getCurLocomotor()->setAllowInvalidPosition(TRUE); + shipAI->getCurLocomotor()->setUltraAccurate(TRUE); // set ultra-accurate just so AI won't try to adjust our dest + } + + Drawable *draw = gunship->getDrawable(); + if ( draw ) + draw->clearAndSetModelConditionState( MODELCONDITION_DOOR_1_OPENING, MODELCONDITION_DOOR_1_CLOSING ); + friend_enableAfterburners(TRUE); + + + cleanUp(); + + return; + +} + + + + +//------------------------------------------------------------------------------------------------- +Bool 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_satellitePosition ); + // status + xfer->xferUser( &m_status, sizeof( GunshipStatus ) ); + + xfer->xferUnsignedInt( &m_orbitEscapeFrame ); + + + if( version < 2 ) + { + xfer->xferUser( &m_attackAreaDecal, sizeof( RadiusDecal ) ); + xfer->xferUser( &m_targetingReticleDecal, sizeof( RadiusDecal ) ); + +#if defined TRACKERS + xfer->xferUser( &m_howitzerTrackerDecal, sizeof( RadiusDecal ) ); +#endif + } + + if( version >= 2 ) + { + xfer->xferCoord3D( &m_gattlingTargetPosition ); + xfer->xferCoord3D( &m_positionToShootAt ); + xfer->xferUnsignedInt( &m_okToFireHowitzerCounter ); + xfer->xferObjectID( &m_gattlingID ); + } + +} // end xfer + +// ------------------------------------------------------------------------------------------------ +/** Load post process */ +// ------------------------------------------------------------------------------------------------ +void 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..9ca675a7f34 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 @@ -803,9 +803,9 @@ Int APIENTRY WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, Int nCmdShow ) { Int exitcode = 1; -//#ifdef _DEBUG -// WaitForDebugger(); //in debug build, wait for debugger attachment -//#endif +#ifdef _DEBUG + WaitForDebugger(); //in debug build, wait for debugger attachment +#endif #ifdef RTS_PROFILE Profile::StartRange("init"); From 525f7296d77dca3b36f5461c24c72a472817fac9 Mon Sep 17 00:00:00 2001 From: pWn3d Date: Sun, 7 Sep 2025 11:08:39 +0200 Subject: [PATCH 2/7] implement straight path for kodiak --- .../GameLogic/Object/Update/KodiakUpdate.cpp | 32 ++++++++++++++++--- 1 file changed, 28 insertions(+), 4 deletions(-) diff --git a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Update/KodiakUpdate.cpp b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Update/KodiakUpdate.cpp index d1ce6272eae..6aecbff43b7 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Update/KodiakUpdate.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Update/KodiakUpdate.cpp @@ -152,7 +152,8 @@ void KodiakUpdate::onObjectCreated() } m_specialPowerModule = obj->getSpecialPowerModule( data->m_specialPowerTemplate ); - m_satellitePosition.set( obj->getPosition() ); + //m_satellitePosition.set( obj->getPosition() ); + m_satellitePosition = Coord3D(0, 0, 0); // This is now the movement direction } //------------------------------------------------------------------------------------------------- @@ -405,12 +406,35 @@ UpdateSleepTime KodiakUpdate::update() declination.x *= orbitalRadius; declination.y *= orbitalRadius; - m_satellitePosition.x = m_initialTargetPosition.x + declination.x; - m_satellitePosition.y = m_initialTargetPosition.y + declination.y; + //m_satellitePosition.x = m_initialTargetPosition.x + declination.x; + //m_satellitePosition.y = m_initialTargetPosition.y + declination.y; + + Coord3D zero_pos; + zero_pos.zero(); + if (m_satellitePosition.equals(zero_pos)) { + // set the direction + Coord3D direction = m_initialTargetPosition; + direction.sub(gunship->getPosition()); + direction.z = zero; + //Real distanceToTarget = direction.length(); + direction.normalize(); + + m_satellitePosition = direction; + m_satellitePosition.scale(200.0f); + } + + //Real orbitalRadius = data->m_gunshipOrbitRadius; + //DEBUG_LOG(("Initial Location: %f,%f,%f", m_initialTargetPosition.x, m_initialTargetPosition.y, m_initialTargetPosition.z)); + + Coord3D target_move_location = *gunship->getPosition(); + target_move_location.add(&m_satellitePosition); + //DEBUG_LOG(("Target Location: %f,%f,%f" , target_move_location.x, target_move_location.y, target_move_location.z )); + if ( shipAI) { - shipAI->aiMoveToPosition( &m_satellitePosition, CMD_FROM_AI ); + //shipAI->aiMoveToPosition( &m_satellitePosition, CMD_FROM_AI ); + shipAI->aiMoveToPosition( &target_move_location, CMD_FROM_AI ); } Real constraintRadius = data->m_attackAreaRadius - data->m_targetingReticleRadius; From 495dbbb447250c9f3d536e5a0f4587baab9c8337 Mon Sep 17 00:00:00 2001 From: pWn3d Date: Sat, 13 Sep 2025 19:23:03 +0200 Subject: [PATCH 3/7] wip --- .../Include/GameLogic/Module/ContainModule.h | 1 + .../GameLogic/Module/MultiAddOnContain.h | 1 + .../Include/GameLogic/Module/OpenContain.h | 1 + .../GameLogic/Object/Update/KodiakUpdate.cpp | 338 +++++++++++++----- 4 files changed, 253 insertions(+), 88 deletions(-) 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/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/GameLogic/Object/Update/KodiakUpdate.cpp b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Update/KodiakUpdate.cpp index 6aecbff43b7..0851ea2db61 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Update/KodiakUpdate.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Update/KodiakUpdate.cpp @@ -319,6 +319,38 @@ class PartitionFilterLiveMapEnemies : public PartitionFilter 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 +}; //----------------------------------------------------------------------------- @@ -362,52 +394,12 @@ UpdateSleepTime KodiakUpdate::update() 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(); - - // init'l apogee - // target *<-------->* - // posi ^\ / - // | \ / - // perigee | \ / - // | */ declination - // | / \ - // v/ * m_satelliteposition - // * - // | - // | - // | - // | - // | - // * gunship->getPosition() - - //perigee is the point in the orbital arc nearest the satellite being captured - Coord3D perigee = *gunship->getPosition(); - perigee.sub( &m_initialTargetPosition ); - perigee.z = zero; - Real distanceToTarget = perigee.length(); - perigee.normalize(); - - //apogee is the anteclockwise point fathest from the perigee line - Coord3D apogee; - apogee.z = zero; - apogee.x = -perigee.y; - apogee.y = perigee.x; - - // declination intersects line [p][a], given an attack slope of n1/n2 - Coord3D declination; - Real n1 = min( ORBIT_INSERTION_SLOPE_MAX, max(ORBIT_INSERTION_SLOPE_MIN, data->m_orbitInsertionSlope) ); - Real n2 = ONE - n1; - declination.z = zero; - declination.x = ( perigee.x * n1 ) + ( apogee.x * n2 ); - declination.y = ( perigee.y * n1 ) + ( apogee.y * n2 ); - - //scale out to the orbital radius Real orbitalRadius = data->m_gunshipOrbitRadius; - declination.x *= orbitalRadius; - declination.y *= orbitalRadius; - - //m_satellitePosition.x = m_initialTargetPosition.x + declination.x; - //m_satellitePosition.y = m_initialTargetPosition.y + declination.y; Coord3D zero_pos; zero_pos.zero(); @@ -416,7 +408,6 @@ UpdateSleepTime KodiakUpdate::update() Coord3D direction = m_initialTargetPosition; direction.sub(gunship->getPosition()); direction.z = zero; - //Real distanceToTarget = direction.length(); direction.normalize(); m_satellitePosition = direction; @@ -433,7 +424,6 @@ UpdateSleepTime KodiakUpdate::update() if ( shipAI) { - //shipAI->aiMoveToPosition( &m_satellitePosition, CMD_FROM_AI ); shipAI->aiMoveToPosition( &target_move_location, CMD_FROM_AI ); } @@ -576,45 +566,214 @@ UpdateSleepTime KodiakUpdate::update() //lets keep a constant barrage of gattling bullets on the current aim location - if( gattlingAI ) + /*if (gattlingAI) { if ( validTargetObject )// either in the reticle or the targeting area gattlingAI->aiAttackObject( validTargetObject, LOTS_OF_SHOTS, CMD_FROM_AI ); else gattlingAI->aiAttackPosition( &m_gattlingTargetPosition, LOTS_OF_SHOTS, CMD_FROM_AI ); - } + }*/ + // IF THE GATTLING GUN HAS BEEN PLINKING THE TARGET LONG ENOUGH, LETS FOLLOW WITH A VOLLEY OF HOWITZER FIRE + if ( m_okToFireHowitzerCounter > data->m_howitzerFollowLag ) + { + ContainModuleInterface* cmi = getObject()->getContain(); + if (cmi != nullptr) { + ContainedItemsList* addOns = cmi->getAddOnList(); + size_t turret_id = 0U; + for (ContainedItemsList::iterator it = addOns->begin(); it != addOns->end(); it++) { + + if (turret_id == 0U || turret_id == 1U) { + //DEBUG_LOG(("Attacking Position %f,%f,%f", m_gattlingTargetPosition.x, m_gattlingTargetPosition.y, m_gattlingTargetPosition.z)); + AIUpdateInterface* ai = (*it)->getAI(); + if (ai != nullptr) { + ai->aiAttackPosition(&m_gattlingTargetPosition, LOTS_OF_SHOTS, CMD_FROM_AI); + } + } + turret_id++; + } + } + } - // IF THE GATTLING GUN HAS BEEN PLINKING THE TARGET LONG ENOUGH, LETS FOLLOW WITH A VOLLEY OF HOWITZER FIRE - if ( m_okToFireHowitzerCounter > data->m_howitzerFollowLag ) + }//endif frame modulator + + /* Auto Acquire Targets with side Turrets */ + // ONLY EVERY FEW FRAMES DO WE RE_EVALUATE THE TARGET OBJECT + if (TheGameLogic->getFrame() % data->m_howitzerFiringRate < ONE) // TODO own param + { + //DEBUG_LOG(("Check for small turret targets")); + + //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; + + Object* validTargetObjectSide1 = NULL; + Object* validTargetObjectSide2 = NULL; + + // THIS WILL FIND A VALID TARGET WITHIN THE TARGETING RETICLE + ObjectIterator* iter = ThePartitionManager->iterateObjectsInRange(gunship->getPosition(), + data->m_gunshipOrbitRadius, + FROM_BOUNDINGSPHERE_2D, + filters, + ITER_SORTED_CHEAP_TO_EXPENSIVE); + MemoryPoolObjectHolder holder(iter); + for (Object* theEnemy = iter->first(); theEnemy; theEnemy = iter->next()) { - WeaponTemplate *wt = data->m_howitzerWeaponTemplate; - if( wt ) + if (theEnemy) // && isFairDistanceFromShip(theEnemy, 1.25f)) { - Coord3D attackPositionWithRandomOffset; - Real offs = data->m_randomOffsetForHowitzer; - attackPositionWithRandomOffset.x = m_gattlingTargetPosition.x + GameLogicRandomValue( -offs, offs ); - attackPositionWithRandomOffset.y = m_gattlingTargetPosition.y + GameLogicRandomValue( -offs, offs ); - attackPositionWithRandomOffset.z = m_gattlingTargetPosition.z; - TheWeaponStore->createAndFireTempWeapon( wt, gunship, &attackPositionWithRandomOffset ); + validTargetObjectSide2 = validTargetObjectSide1; + validTargetObjectSide1 = theEnemy; + + if (validTargetObjectSide1 != NULL && validTargetObjectSide2 != NULL) { + break; + } + } + } + if (validTargetObjectSide2 == nullptr) { + //Both shoot on 1 target + validTargetObjectSide2 = validTargetObjectSide1; + } - m_howitzerFireSound.setObjectID(gunship->getID()); - TheAudio->addAudioEvent( &m_howitzerFireSound ); + if (validTargetObjectSide1 != NULL && validTargetObjectSide2 != NULL) { + ContainModuleInterface* cmi = getObject()->getContain(); + if (cmi != nullptr) { + ContainedItemsList* addOns = cmi->getAddOnList(); + size_t turret_id = 0U; + for (ContainedItemsList::iterator it = addOns->begin(); it != addOns->end(); it++) { + + if (turret_id == 2U && validTargetObjectSide1 != nullptr) { + AIUpdateInterface* ai = (*it)->getAI(); + if (ai != nullptr) { + //ai->aiForceAttackObject(validTargetObjectSide1, LOTS_OF_SHOTS, CMD_FROM_AI); + ai->aiAttackPosition(validTargetObjectSide1->getPosition(), LOTS_OF_SHOTS, CMD_FROM_AI); + } + } + else if (turret_id == 3U && validTargetObjectSide2 != nullptr) { + AIUpdateInterface* ai = (*it)->getAI(); + if (ai != nullptr) { + //ai->aiForceAttackObject(validTargetObjectSide2, LOTS_OF_SHOTS, CMD_FROM_AI); + ai->aiAttackPosition(validTargetObjectSide2->getPosition(), LOTS_OF_SHOTS, CMD_FROM_AI); + } + } + turret_id++; + } } } + + + } + + //AA TURRET TARGETING + if (TheGameLogic->getFrame() % data->m_howitzerFiringRate < ONE) // TODO own param + { + DEBUG_LOG(("Check for AA turret targets")); + Object* turretAA1 = nullptr; + Object* turretAA2 = nullptr; + ContainModuleInterface* cmi = getObject()->getContain(); + if (cmi != nullptr) { + ContainedItemsList* addOns = cmi->getAddOnList(); + size_t turret_id = 0U; - }//endif frame modulator + for (ContainedItemsList::iterator it = addOns->begin(); it != addOns->end(); it++) { + + if (turret_id == 4U) { + turretAA1 = (*it); + } + else if (turret_id == 5U) { + turretAA2 = (*it); + } + + turret_id++; + } + } + + //m_positionToShootAt = m_overrideTargetDestination; // unless we get a hit, below + if (turretAA1 != nullptr) { + PartitionFilterLiveMapAirEnemies filterObvious(gunship); + PartitionFilterStealthedAndUndetected filterStealth(gunship, false); + PartitionFilterPossibleToAttack filterAttack(ATTACK_NEW_TARGET, turretAA1, 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* validTargetObject1 = NULL; + Object* validTargetObject2 = NULL; + + // THIS WILL FIND A VALID TARGET WITHIN THE TARGETING RETICLE + ObjectIterator* iter = ThePartitionManager->iterateObjectsInRange(gunship->getPosition(), + data->m_gunshipOrbitRadius, + FROM_BOUNDINGSPHERE_2D, + filters, + ITER_SORTED_CHEAP_TO_EXPENSIVE); + MemoryPoolObjectHolder holder(iter); + for (Object* theEnemy = iter->first(); theEnemy; theEnemy = iter->next()) + { + if (theEnemy) // && isFairDistanceFromShip(theEnemy, 1.25f)) + { + validTargetObject2 = validTargetObject1; + validTargetObject1 = theEnemy; + + if (validTargetObject1 != NULL && validTargetObject2 != NULL) { + DEBUG_LOG(("Found Air Targets")); + break; + } + } + } + if (validTargetObject2 == nullptr) { + //Both shoot on 1 target + validTargetObject2 = validTargetObject1; + DEBUG_LOG(("Focus on Air Target 1")); + } + if (validTargetObject1 != nullptr && validTargetObject2 != nullptr) { + DEBUG_LOG(("Attacking Air Targets1")); + if (turretAA1 != nullptr) { + AIUpdateInterface* ai = turretAA1->getAI(); + if (ai != nullptr) { + //ai->aiAttackObject(validTargetObject1, LOTS_OF_SHOTS, CMD_FROM_AI); + DEBUG_LOG(("Attacking AA1")); + ai->aiAttackPosition(validTargetObject1->getPosition(), LOTS_OF_SHOTS, CMD_FROM_AI); + } + } + if (turretAA2 != nullptr) { + AIUpdateInterface* ai = turretAA2->getAI(); + if (ai != nullptr) { + //ai->aiAttackObject(validTargetObject1, LOTS_OF_SHOTS, CMD_FROM_AI); + DEBUG_LOG(("Attacking AA2")); + ai->aiAttackPosition(validTargetObject2->getPosition(), LOTS_OF_SHOTS, CMD_FROM_AI); + } + } + + + } + } + + } // GATTLING TARGETING LOGIC------------------------------------------ - const ParticleSystemTemplate *tmp = data->m_gattlingStrafeFXParticleSystem; + /*const ParticleSystemTemplate* tmp = data->m_gattlingStrafeFXParticleSystem; if (tmp && gattling && gattling->testStatus( OBJECT_STATUS_IS_FIRING_WEAPON) ) { @@ -638,29 +797,23 @@ UpdateSleepTime KodiakUpdate::update() delta.normalize(); delta.scale( data->m_strafingIncrement ); m_gattlingTargetPosition.add( &delta ); - } - - - const Player *localPlayer = ThePlayerList->getLocalPlayer(); - - //Make sure the gunship is visible to the player before drawing effects. - if ( gunship->getShroudedStatus( localPlayer->getPlayerIndex() ) <= OBJECTSHROUD_PARTIAL_CLEAR ) - { - - // This makes the client smoke effects of the gattling cannon strafing the ground toward the attack position - ParticleSystem *sys = TheParticleSystemManager->createParticleSystem(tmp); - if (sys) - { - Coord3D impactPosition; - impactPosition.x = m_gattlingTargetPosition.x + GameClientRandomValueReal( -5.0f, 5.0f ); - impactPosition.y = m_gattlingTargetPosition.y + GameClientRandomValueReal( -5.0f, 5.0f ); - impactPosition.z = TheTerrainLogic->getGroundHeight( impactPosition.x, impactPosition.y ); - sys->setPosition( &impactPosition ); - - } - } - + }*/ + Coord3D delta = m_positionToShootAt; + delta.sub(&m_gattlingTargetPosition); + Real dist = delta.length(); + if (dist < data->m_strafingIncrement) + { + m_gattlingTargetPosition = m_positionToShootAt; + ++m_okToFireHowitzerCounter; + } + else + { + m_okToFireHowitzerCounter = ZERO; + delta.normalize(); + delta.scale(data->m_strafingIncrement); + m_gattlingTargetPosition.add(&delta); } + }// end else @@ -746,7 +899,7 @@ Bool KodiakUpdate::isFairDistanceFromShip( Object *target ) shipToTargetDelta.y = gunshipPosition->y - targetPosition->y; shipToTargetDelta.z = 0.0f; - return (shipToTargetDelta.length() > getKodiakUpdateModuleData()->m_gunshipOrbitRadius * 0.75f ); + return (shipToTargetDelta.length() > getKodiakUpdateModuleData()->m_gunshipOrbitRadius); } @@ -798,12 +951,21 @@ void KodiakUpdate::disengageAndDepartAO( Object *gunship ) + } + + //Disable all turrets + ContainModuleInterface* cmi = getObject()->getContain(); + if (cmi != nullptr) { + ContainedItemsList* addOns = cmi->getAddOnList(); + for (ContainedItemsList::iterator it = addOns->begin(); it != addOns->end(); it++) { + (*it)->setDisabled(DISABLED_PARALYZED); + } } - Object *gattling = TheGameLogic->findObjectByID( m_gattlingID ); - if ( gattling ) - gattling->setDisabled ( DISABLED_PARALYZED ); + //Object *gattling = TheGameLogic->findObjectByID( m_gattlingID ); + //if ( gattling ) + // gattling->setDisabled ( DISABLED_PARALYZED ); if ( shipAI) From bfeed0b4bb45a070d622950b4c8c1e436d14274b Mon Sep 17 00:00:00 2001 From: pWn3d Date: Sun, 14 Sep 2025 21:01:17 +0200 Subject: [PATCH 4/7] Kodiak updates --- .../Include/GameLogic/Module/KodiakUpdate.h | 32 +- .../Object/Contain/MultiAddOnContain.cpp | 17 +- .../GameLogic/Object/Update/KodiakUpdate.cpp | 448 +++++++----------- 3 files changed, 208 insertions(+), 289 deletions(-) diff --git a/GeneralsMD/Code/GameEngine/Include/GameLogic/Module/KodiakUpdate.h b/GeneralsMD/Code/GameEngine/Include/GameLogic/Module/KodiakUpdate.h index 6bc51ee510b..3ea49f521df 100644 --- a/GeneralsMD/Code/GameEngine/Include/GameLogic/Module/KodiakUpdate.h +++ b/GeneralsMD/Code/GameEngine/Include/GameLogic/Module/KodiakUpdate.h @@ -33,27 +33,33 @@ class KodiakUpdateModuleData : public ModuleData { public: SpecialPowerTemplate *m_specialPowerTemplate; - WeaponTemplate *m_howitzerWeaponTemplate; -// AsciiString m_gunshipTemplateName; - AsciiString m_gattlingTemplateName; -// AsciiString m_howitzerTemplateName; + RadiusDecalTemplate m_attackAreaDecalTemplate; RadiusDecalTemplate m_targetingReticleDecalTemplate; UnsignedInt m_orbitFrames; - UnsignedInt m_howitzerFiringRate; - UnsignedInt m_howitzerFollowLag; + UnsignedInt m_mainTargetingRate; + UnsignedInt m_sideTargetingRate; + UnsignedInt m_aaTargetingRate; + Real m_attackAreaRadius; + Real m_sideAttackAreaRadius; + Real m_aaAttackAreaRadius; Real m_targetingReticleRadius; Real m_gunshipOrbitRadius; - Real m_strafingIncrement; - Real m_orbitInsertionSlope; - Real m_randomOffsetForHowitzer; + Real m_missileLockRadius; + UnsignedInt m_numMainTurrets; + UnsignedInt m_numSideTurrets; + UnsignedInt m_numAATurrets; + + std::vector m_scatterTargets; ///< targets for missile attack, amount of targets should match clip size of primary weapon - const ParticleSystemTemplate * m_gattlingStrafeFXParticleSystem; + //const ParticleSystemTemplate * m_gattlingStrafeFXParticleSystem; KodiakUpdateModuleData(); static void buildFieldParse(MultiIniFieldParse& p); + + static void parseScatterTarget(INI* ini, void* instance, void* /*store*/, const void* /*userData*/); private: }; @@ -102,7 +108,7 @@ class KodiakUpdate : public SpecialPowerUpdateModule // 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; } @@ -130,7 +136,6 @@ class KodiakUpdate : public SpecialPowerUpdateModule UnsignedInt m_okToFireHowitzerCounter; UnsignedInt m_orbitEscapeFrame; - // ObjectID m_howitzerID; ObjectID m_gattlingID; @@ -138,9 +143,6 @@ class KodiakUpdate : public SpecialPowerUpdateModule RadiusDecal m_attackAreaDecal; RadiusDecal m_targetingReticleDecal; - - - #if defined TRACKERS RadiusDecal m_howitzerTrackerDecal; #endif diff --git a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Contain/MultiAddOnContain.cpp b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Contain/MultiAddOnContain.cpp index 7cd5bd04d49..08ea9995e01 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Contain/MultiAddOnContain.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Contain/MultiAddOnContain.cpp @@ -341,15 +341,18 @@ Bool MultiAddOnContain::isPassengerAllowedToFire(ObjectID id) const return FALSE; //if (id == INVALID_ID && m_addOnList.size() > 0) { - // return TRUE; // This handles cases where we just check if we can attack at all. + // return TRUE; // This handles cases where we just check if we can attack at all. //} - // Wait, the portable structures aren't actually contained, so we skip this - //for (std::vector::iterator it = m_addOnSlots.begin(); it != m_addOnSlots.end(); ) { - // if (*it->portableID == id && id != INVALID_ID) { - // return TRUE; // Portable structures can always fire - // } - //} + // Wait, the portable structures aren't actually contained, so we skip this -- no we don't! + if (id != INVALID_ID) + { + for (Object* addOn : m_addOnList) { + if (addOn->getID() == id) { + return TRUE; + } + } + } return TransportContain::isPassengerAllowedToFire(id); //extend for everything else } diff --git a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Update/KodiakUpdate.cpp b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Update/KodiakUpdate.cpp index 0851ea2db61..e35eb4149d0 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Update/KodiakUpdate.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Update/KodiakUpdate.cpp @@ -57,16 +57,24 @@ KodiakUpdateModuleData::KodiakUpdateModuleData() { m_specialPowerTemplate = NULL; /******BOTH*******//*BOTH*//******BOTH*******//******BOTH*******/ m_attackAreaRadius = 200.0f; -/*************/ m_gattlingStrafeFXParticleSystem = NULL; -/*************/ m_howitzerWeaponTemplate = NULL; +/*************/ //m_gattlingStrafeFXParticleSystem = NULL; +/*************/ //m_howitzerWeaponTemplate = NULL; /*************/ m_orbitFrames = 0; /*************/ m_targetingReticleRadius = 25.0f; /*************/ m_gunshipOrbitRadius = 250.0f; - m_strafingIncrement = 20.0f; - m_orbitInsertionSlope = 0.7f; - m_howitzerFiringRate = 10; - m_howitzerFollowLag = 0; - m_randomOffsetForHowitzer = 20.0f; + 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_numMainTurrets = 1; + m_numSideTurrets = 0; + m_numAATurrets = 0; } static Real zero = 0.0f; @@ -78,22 +86,22 @@ static Real zero = 0.0f; static const FieldParse dataFieldParse[] = { { "SpecialPowerTemplate", INI::parseSpecialPowerTemplate, NULL, offsetof( KodiakUpdateModuleData, m_specialPowerTemplate ) }, - { "GattlingTemplateName", INI::parseAsciiString, NULL, offsetof( KodiakUpdateModuleData, m_gattlingTemplateName ) }, - { "HowitzerFiringRate", INI::parseDurationUnsignedInt, NULL, offsetof( KodiakUpdateModuleData, m_howitzerFiringRate ) }, + { "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 ) }, - { "HowitzerFollowLag", INI::parseDurationUnsignedInt, NULL, offsetof( KodiakUpdateModuleData, m_howitzerFollowLag ) }, { "AttackAreaRadius", INI::parseReal, NULL, offsetof( KodiakUpdateModuleData, m_attackAreaRadius ) }, - { "StrafingIncrement", INI::parseReal, NULL, offsetof( KodiakUpdateModuleData, m_strafingIncrement ) }, - { "OrbitInsertionSlope", INI::parseReal, NULL, offsetof( KodiakUpdateModuleData, m_orbitInsertionSlope ) }, - { "RandomOffsetForHowitzer", INI::parseReal, NULL, offsetof( KodiakUpdateModuleData, m_randomOffsetForHowitzer ) }, + { "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 ) }, - { "HowitzerWeaponTemplate", INI::parseWeaponTemplate, NULL, offsetof( KodiakUpdateModuleData, m_howitzerWeaponTemplate ) }, - { "GattlingStrafeFXParticleSystem", INI::parseParticleSystemTemplate, NULL, offsetof( KodiakUpdateModuleData, m_gattlingStrafeFXParticleSystem ) }, + { "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) }, { 0, 0, 0, 0 } @@ -101,6 +109,19 @@ static Real zero = 0.0f; 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 ) { @@ -203,34 +224,6 @@ Bool KodiakUpdate::initiateIntentToDoSpecialPower(const SpecialPowerTemplate *sp setLogicalStatus( GUNSHIP_STATUS_INSERTING ); // The gunship is en route to the tharget area, from map-edge - - // TELL THE GUNNERS ABOARD THE GUNSHIP TO HOLD THEIR FIRE UNTIL ORBIT INSERTION - ContainModuleInterface *shipContain = gunShip->getContain(); - if ( shipContain ) - { - - Object *newGattling = TheGameLogic->findObjectByID( m_gattlingID ); - const ThingTemplate *gattlingTemplate = TheThingFactory->findTemplate( data->m_gattlingTemplateName ); - if( newGattling != NULL ) - { - m_gattlingID = INVALID_ID; - newGattling = NULL; - } - if ( gattlingTemplate ) - { - newGattling = TheThingFactory->newObject( gattlingTemplate, getObject()->getTeam() ); - DEBUG_ASSERTCRASH( gunShip, ("SpecterGunshipUpdate failed to find or create a GATTLING object")); - shipContain->addToContain( newGattling ); - m_gattlingID = newGattling->getID(); - } - - - Object *gattling = TheGameLogic->findObjectByID( m_gattlingID ); - if ( gattling ) - gattling->setDisabled ( DISABLED_PARALYZED ); - - } - } @@ -364,17 +357,14 @@ 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 @@ -390,6 +380,18 @@ UpdateSleepTime KodiakUpdate::update() gattlingAI = gattling->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 ) @@ -414,13 +416,8 @@ UpdateSleepTime KodiakUpdate::update() m_satellitePosition.scale(200.0f); } - //Real orbitalRadius = data->m_gunshipOrbitRadius; - //DEBUG_LOG(("Initial Location: %f,%f,%f", m_initialTargetPosition.x, m_initialTargetPosition.y, m_initialTargetPosition.z)); - Coord3D target_move_location = *gunship->getPosition(); target_move_location.add(&m_satellitePosition); - - //DEBUG_LOG(("Target Location: %f,%f,%f" , target_move_location.x, target_move_location.y, target_move_location.z )); if ( shipAI) { @@ -457,10 +454,6 @@ UpdateSleepTime KodiakUpdate::update() setLogicalStatus( GUNSHIP_STATUS_ORBITING ); m_orbitEscapeFrame = TheGameLogic->getFrame() + data->m_orbitFrames; - Object *gattling = TheGameLogic->findObjectByID( m_gattlingID ); - if ( gattling ) - gattling->clearDisabled ( DISABLED_PARALYZED ); - AIUpdateInterface *shipAI = gunship->getAIUpdateInterface(); if ( shipAI) { @@ -498,7 +491,7 @@ UpdateSleepTime KodiakUpdate::update() { // ONLY EVERY FEW FRAMES DO WE RE_EVALUATE THE TARGET OBJECT - if ( TheGameLogic->getFrame() %data->m_howitzerFiringRate < ONE ) + if ( TheGameLogic->getFrame() %data->m_mainTargetingRate < ONE ) { m_positionToShootAt = m_overrideTargetDestination; // unless we get a hit, below @@ -516,7 +509,6 @@ UpdateSleepTime KodiakUpdate::update() filters[numFilters] = NULL; - // THIS WILL FIND A VALID TARGET WITHIN THE TARGETING RETICLE ObjectIterator *iter = ThePartitionManager->iterateObjectsInRange(&m_overrideTargetDestination, data->m_targetingReticleRadius, @@ -563,50 +555,19 @@ UpdateSleepTime KodiakUpdate::update() } } - - - //lets keep a constant barrage of gattling bullets on the current aim location - /*if (gattlingAI) - { - if ( validTargetObject )// either in the reticle or the targeting area - gattlingAI->aiAttackObject( validTargetObject, LOTS_OF_SHOTS, CMD_FROM_AI ); - else - gattlingAI->aiAttackPosition( &m_gattlingTargetPosition, LOTS_OF_SHOTS, CMD_FROM_AI ); - }*/ - - // IF THE GATTLING GUN HAS BEEN PLINKING THE TARGET LONG ENOUGH, LETS FOLLOW WITH A VOLLEY OF HOWITZER FIRE - if ( m_okToFireHowitzerCounter > data->m_howitzerFollowLag ) - { - ContainModuleInterface* cmi = getObject()->getContain(); - if (cmi != nullptr) { - ContainedItemsList* addOns = cmi->getAddOnList(); - size_t turret_id = 0U; - for (ContainedItemsList::iterator it = addOns->begin(); it != addOns->end(); it++) { - - if (turret_id == 0U || turret_id == 1U) { - //DEBUG_LOG(("Attacking Position %f,%f,%f", m_gattlingTargetPosition.x, m_gattlingTargetPosition.y, m_gattlingTargetPosition.z)); - AIUpdateInterface* ai = (*it)->getAI(); - if (ai != nullptr) { - ai->aiAttackPosition(&m_gattlingTargetPosition, LOTS_OF_SHOTS, CMD_FROM_AI); - } - } - - turret_id++; - } + // 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_overrideTargetDestination, LOTS_OF_SHOTS, CMD_FROM_AI); } } - }//endif frame modulator /* Auto Acquire Targets with side Turrets */ - // ONLY EVERY FEW FRAMES DO WE RE_EVALUATE THE TARGET OBJECT - if (TheGameLogic->getFrame() % data->m_howitzerFiringRate < ONE) // TODO own param + if (data->m_numSideTurrets > 0 && TheGameLogic->getFrame() % data->m_sideTargetingRate < ONE) { - //DEBUG_LOG(("Check for small turret targets")); - - //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); @@ -619,201 +580,163 @@ UpdateSleepTime KodiakUpdate::update() filters[numFilters++] = &filterFogged; filters[numFilters] = NULL; - Object* validTargetObjectSide1 = NULL; - Object* validTargetObjectSide2 = NULL; + std::vector targets; + targets.reserve(data->m_numSideTurrets); - // THIS WILL FIND A VALID TARGET WITHIN THE TARGETING RETICLE ObjectIterator* iter = ThePartitionManager->iterateObjectsInRange(gunship->getPosition(), - data->m_gunshipOrbitRadius, + data->m_sideAttackAreaRadius, FROM_BOUNDINGSPHERE_2D, filters, ITER_SORTED_CHEAP_TO_EXPENSIVE); MemoryPoolObjectHolder holder(iter); - for (Object* theEnemy = iter->first(); theEnemy; theEnemy = iter->next()) + for (Object* theEnemy = iter->first(); theEnemy && (targets.size() < data->m_numSideTurrets); theEnemy = iter->next()) { - if (theEnemy) // && isFairDistanceFromShip(theEnemy, 1.25f)) - { - validTargetObjectSide2 = validTargetObjectSide1; - validTargetObjectSide1 = theEnemy; - - if (validTargetObjectSide1 != NULL && validTargetObjectSide2 != NULL) { - break; - } - } + targets.push_back(theEnemy); } - if (validTargetObjectSide2 == nullptr) { - //Both shoot on 1 target - validTargetObjectSide2 = validTargetObjectSide1; - } - - if (validTargetObjectSide1 != NULL && validTargetObjectSide2 != NULL) { - ContainModuleInterface* cmi = getObject()->getContain(); - if (cmi != nullptr) { - ContainedItemsList* addOns = cmi->getAddOnList(); - size_t turret_id = 0U; - for (ContainedItemsList::iterator it = addOns->begin(); it != addOns->end(); it++) { - - if (turret_id == 2U && validTargetObjectSide1 != nullptr) { - AIUpdateInterface* ai = (*it)->getAI(); - if (ai != nullptr) { - //ai->aiForceAttackObject(validTargetObjectSide1, LOTS_OF_SHOTS, CMD_FROM_AI); - ai->aiAttackPosition(validTargetObjectSide1->getPosition(), LOTS_OF_SHOTS, CMD_FROM_AI); - } + 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 if (turret_id == 3U && validTargetObjectSide2 != nullptr) { - AIUpdateInterface* ai = (*it)->getAI(); - if (ai != nullptr) { - //ai->aiForceAttackObject(validTargetObjectSide2, LOTS_OF_SHOTS, CMD_FROM_AI); - ai->aiAttackPosition(validTargetObjectSide2->getPosition(), LOTS_OF_SHOTS, CMD_FROM_AI); - } + else { + target = targets.at(targets.size() - 1); } - - turret_id++; + ai->aiAttackObject(target, LOTS_OF_SHOTS, CMD_FROM_AI); } } } - - } //AA TURRET TARGETING - if (TheGameLogic->getFrame() % data->m_howitzerFiringRate < ONE) // TODO own param + if (data->m_numAATurrets > 0 && TheGameLogic->getFrame() % data->m_aaTargetingRate < ONE) { - DEBUG_LOG(("Check for AA turret targets")); - Object* turretAA1 = nullptr; - Object* turretAA2 = nullptr; - - ContainModuleInterface* cmi = getObject()->getContain(); - if (cmi != nullptr) { - ContainedItemsList* addOns = cmi->getAddOnList(); - size_t turret_id = 0U; + 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); - for (ContainedItemsList::iterator it = addOns->begin(); it != addOns->end(); it++) { - if (turret_id == 4U) { - turretAA1 = (*it); - } - else if (turret_id == 5U) { - turretAA2 = (*it); + // THIS WILL FIND A VALID TARGET WITHIN THE TARGETING RETICLE + 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); } - - turret_id++; } } + } - //m_positionToShootAt = m_overrideTargetDestination; // unless we get a hit, below - if (turretAA1 != nullptr) { - PartitionFilterLiveMapAirEnemies filterObvious(gunship); - PartitionFilterStealthedAndUndetected filterStealth(gunship, false); - PartitionFilterPossibleToAttack filterAttack(ATTACK_NEW_TARGET, turretAA1, 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* validTargetObject1 = NULL; - Object* validTargetObject2 = NULL; - - // THIS WILL FIND A VALID TARGET WITHIN THE TARGETING RETICLE - ObjectIterator* iter = ThePartitionManager->iterateObjectsInRange(gunship->getPosition(), - data->m_gunshipOrbitRadius, - FROM_BOUNDINGSPHERE_2D, - filters, - ITER_SORTED_CHEAP_TO_EXPENSIVE); - MemoryPoolObjectHolder holder(iter); - for (Object* theEnemy = iter->first(); theEnemy; theEnemy = iter->next()) - { - if (theEnemy) // && isFairDistanceFromShip(theEnemy, 1.25f)) + //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); + DEBUG_LOG(("Shot Index: %d @ %f, %f", shot_index, scatterOffset.x, scatterOffset.y)); + Coord3D targetPos = m_initialTargetPosition; + //Calculate Target from Scatter + + //TODO IndividualScalar? + scatterOffset.x *= data->m_attackAreaRadius; + scatterOffset.y *= data->m_attackAreaRadius; + + 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()) { - validTargetObject2 = validTargetObject1; - validTargetObject1 = theEnemy; - - if (validTargetObject1 != NULL && validTargetObject2 != NULL) { - DEBUG_LOG(("Found Air Targets")); - break; + if (theEnemy) + { + targetLock = theEnemy; } } - } - if (validTargetObject2 == nullptr) { - //Both shoot on 1 target - validTargetObject2 = validTargetObject1; - DEBUG_LOG(("Focus on Air Target 1")); - } - - if (validTargetObject1 != nullptr && validTargetObject2 != nullptr) { - DEBUG_LOG(("Attacking Air Targets1")); - - if (turretAA1 != nullptr) { - AIUpdateInterface* ai = turretAA1->getAI(); - if (ai != nullptr) { - //ai->aiAttackObject(validTargetObject1, LOTS_OF_SHOTS, CMD_FROM_AI); - DEBUG_LOG(("Attacking AA1")); - ai->aiAttackPosition(validTargetObject1->getPosition(), LOTS_OF_SHOTS, CMD_FROM_AI); - } + if (targetLock != nullptr) { + weap->fireWeapon(getObject(), targetLock); + //DEBUG_LOG(("TargetUnit: %s", targetLock->getTemplate()->getName().str())); } - if (turretAA2 != nullptr) { - AIUpdateInterface* ai = turretAA2->getAI(); - if (ai != nullptr) { - //ai->aiAttackObject(validTargetObject1, LOTS_OF_SHOTS, CMD_FROM_AI); - DEBUG_LOG(("Attacking AA2")); - ai->aiAttackPosition(validTargetObject2->getPosition(), LOTS_OF_SHOTS, CMD_FROM_AI); - } + else { + weap->fireWeapon(getObject(), &targetPos); } - - + } + else { + weap->fireWeapon(getObject(), getObject()->getPosition()); } } } - // GATTLING TARGETING LOGIC------------------------------------------ - /*const ParticleSystemTemplate* tmp = data->m_gattlingStrafeFXParticleSystem; - if (tmp && gattling && gattling->testStatus( OBJECT_STATUS_IS_FIRING_WEAPON) ) - { - - - - // I am going to wind my gattling gun toward the next good spot, - //whether an object's position or a cursor position - - - Coord3D delta = m_positionToShootAt; - delta.sub( &m_gattlingTargetPosition ); - Real dist = delta.length(); - if ( dist < data->m_strafingIncrement ) - { - m_gattlingTargetPosition = m_positionToShootAt; - ++m_okToFireHowitzerCounter; - } - else - { - m_okToFireHowitzerCounter = ZERO; - delta.normalize(); - delta.scale( data->m_strafingIncrement ); - m_gattlingTargetPosition.add( &delta ); - }*/ - Coord3D delta = m_positionToShootAt; - delta.sub(&m_gattlingTargetPosition); - Real dist = delta.length(); - if (dist < data->m_strafingIncrement) - { - m_gattlingTargetPosition = m_positionToShootAt; - ++m_okToFireHowitzerCounter; - } - else - { - m_okToFireHowitzerCounter = ZERO; - delta.normalize(); - delta.scale(data->m_strafingIncrement); - m_gattlingTargetPosition.add(&delta); - } - }// end else @@ -843,14 +766,6 @@ UpdateSleepTime KodiakUpdate::update() } - - - - - - - - return UPDATE_SLEEP_NONE; } @@ -1041,7 +956,6 @@ void KodiakUpdate::xfer( Xfer *xfer ) xfer->xferUnsignedInt( &m_orbitEscapeFrame ); - if( version < 2 ) { xfer->xferUser( &m_attackAreaDecal, sizeof( RadiusDecal ) ); From 9231750d5efbefa80e4d83aff1f85a5b942cf4fd Mon Sep 17 00:00:00 2001 From: pWn3d Date: Mon, 15 Sep 2025 21:33:13 +0200 Subject: [PATCH 5/7] kodiak tweaks --- .../Include/GameLogic/Module/KodiakUpdate.h | 23 ++---- .../GameLogic/Object/Update/KodiakUpdate.cpp | 80 +++++++------------ 2 files changed, 32 insertions(+), 71 deletions(-) diff --git a/GeneralsMD/Code/GameEngine/Include/GameLogic/Module/KodiakUpdate.h b/GeneralsMD/Code/GameEngine/Include/GameLogic/Module/KodiakUpdate.h index 3ea49f521df..994a33b11e8 100644 --- a/GeneralsMD/Code/GameEngine/Include/GameLogic/Module/KodiakUpdate.h +++ b/GeneralsMD/Code/GameEngine/Include/GameLogic/Module/KodiakUpdate.h @@ -40,6 +40,8 @@ class KodiakUpdateModuleData : public ModuleData UnsignedInt m_mainTargetingRate; UnsignedInt m_sideTargetingRate; UnsignedInt m_aaTargetingRate; + UnsignedInt m_turretRecenterFramesBeforeExit; + UnsignedInt m_initialAttackDelayFrames; Real m_attackAreaRadius; Real m_sideAttackAreaRadius; @@ -51,6 +53,7 @@ class KodiakUpdateModuleData : public ModuleData 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; @@ -64,15 +67,6 @@ class KodiakUpdateModuleData : public ModuleData }; -//enum GunshipStatus CPP_11(: Int) -//{ -// GUNSHIP_STATUS_INSERTING, -// GUNSHIP_STATUS_ORBITING, -// GUNSHIP_STATUS_DEPARTING, -// GUNSHIP_STATUS_IDLE, -//}; - - //------------------------------------------------------------------------------------------------- /** The default update module */ //------------------------------------------------------------------------------------------------- @@ -126,20 +120,14 @@ class KodiakUpdate : public SpecialPowerUpdateModule Coord3D m_initialTargetPosition; Coord3D m_overrideTargetDestination; - Coord3D m_satellitePosition; - Coord3D m_gattlingTargetPosition; + Coord3D m_movementPosition; Coord3D m_positionToShootAt; GunshipStatus m_status; - UnsignedInt m_okToFireHowitzerCounter; UnsignedInt m_orbitEscapeFrame; -// ObjectID m_howitzerID; - ObjectID m_gattlingID; - - RadiusDecal m_attackAreaDecal; RadiusDecal m_targetingReticleDecal; @@ -148,10 +136,9 @@ class KodiakUpdate : public SpecialPowerUpdateModule #endif AudioEventRTS m_afterburnerSound; - AudioEventRTS m_howitzerFireSound; }; -#endif // __SPECTRE_GUNSHIP_UPDATE_H_ +#endif // __KODIAK_UPDATE_H_ diff --git a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Update/KodiakUpdate.cpp b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Update/KodiakUpdate.cpp index e35eb4149d0..fe949b98605 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Update/KodiakUpdate.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Update/KodiakUpdate.cpp @@ -71,10 +71,13 @@ KodiakUpdateModuleData::KodiakUpdateModuleData() 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; @@ -98,10 +101,13 @@ static Real zero = 0.0f; { "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 }, + { "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 } @@ -126,19 +132,14 @@ static Real zero = 0.0f; KodiakUpdate::KodiakUpdate( Thing *thing, const ModuleData* moduleData ) : SpecialPowerUpdateModule( thing, moduleData ) { m_specialPowerModule = NULL; - m_gattlingID = INVALID_ID; m_status = GUNSHIP_STATUS_IDLE; m_initialTargetPosition.zero(); m_overrideTargetDestination.zero(); - m_gattlingTargetPosition.zero(); m_positionToShootAt.zero(); m_attackAreaDecal.clear(); m_targetingReticleDecal.clear(); m_orbitEscapeFrame = 0; m_afterburnerSound = *(getObject()->getTemplate()->getPerUnitSound("Afterburner")); - m_howitzerFireSound = *(getObject()->getTemplate()->getPerUnitSound("HowitzerFire")); - - m_okToFireHowitzerCounter = 0; #if defined TRACKERS m_howitzerTrackerDecal.clear(); @@ -173,8 +174,7 @@ void KodiakUpdate::onObjectCreated() } m_specialPowerModule = obj->getSpecialPowerModule( data->m_specialPowerTemplate ); - //m_satellitePosition.set( obj->getPosition() ); - m_satellitePosition = Coord3D(0, 0, 0); // This is now the movement direction + m_movementPosition = Coord3D(0, 0, 0); } //------------------------------------------------------------------------------------------------- @@ -192,7 +192,6 @@ Bool KodiakUpdate::initiateIntentToDoSpecialPower(const SpecialPowerTemplate *sp { m_initialTargetPosition.set( targetPos ); m_overrideTargetDestination.set( targetPos ); - m_gattlingTargetPosition.set( targetPos ); } else { @@ -372,13 +371,6 @@ UpdateSleepTime KodiakUpdate::update() #endif AIUpdateInterface *shipAI = gunship->getAIUpdateInterface(); - AIUpdateInterface *gattlingAI = NULL; - - Object *gattling = TheGameLogic->findObjectByID( m_gattlingID ); - if ( gattling ) - { - gattlingAI = gattling->getAIUpdateInterface(); - } // get the turrets std::vector turrets; @@ -405,19 +397,19 @@ UpdateSleepTime KodiakUpdate::update() Coord3D zero_pos; zero_pos.zero(); - if (m_satellitePosition.equals(zero_pos)) { + if (m_movementPosition.equals(zero_pos)) { // set the direction Coord3D direction = m_initialTargetPosition; direction.sub(gunship->getPosition()); direction.z = zero; direction.normalize(); - m_satellitePosition = direction; - m_satellitePosition.scale(200.0f); + m_movementPosition = direction; + m_movementPosition.scale(200.0f); } Coord3D target_move_location = *gunship->getPosition(); - target_move_location.add(&m_satellitePosition); + target_move_location.add(&m_movementPosition); if ( shipAI) { @@ -472,7 +464,7 @@ UpdateSleepTime KodiakUpdate::update() } // endif status == ORBITING || INSERTING - if ( m_status == GUNSHIP_STATUS_ORBITING ) + if ( m_status == GUNSHIP_STATUS_ORBITING && TheGameLogic->getFrame() >= (m_orbitEscapeFrame - data->m_orbitFrames + data->m_initialAttackDelayFrames) ) // delay until attack { Object *validTargetObject = NULL; @@ -487,6 +479,16 @@ UpdateSleepTime KodiakUpdate::update() }//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 { @@ -559,7 +561,7 @@ UpdateSleepTime KodiakUpdate::update() for (UnsignedInt i = 0; i < data->m_numMainTurrets; i++) { AIUpdateInterface* ai = turrets.at(i)->getAI(); if (ai != nullptr) { - ai->aiAttackPosition(&m_overrideTargetDestination, LOTS_OF_SHOTS, CMD_FROM_AI); + ai->aiAttackPosition(&m_positionToShootAt, LOTS_OF_SHOTS, CMD_FROM_AI); } } @@ -628,8 +630,6 @@ UpdateSleepTime KodiakUpdate::update() std::vector targets; targets.reserve(data->m_numAATurrets); - - // THIS WILL FIND A VALID TARGET WITHIN THE TARGETING RETICLE ObjectIterator* iter = ThePartitionManager->iterateObjectsInRange(gunship->getPosition(), data->m_aaAttackAreaRadius, FROM_BOUNDINGSPHERE_2D, @@ -669,13 +669,11 @@ UpdateSleepTime KodiakUpdate::update() if (!data->m_scatterTargets.empty()) { size_t target_idx = shot_index % data->m_scatterTargets.size(); Coord2D scatterOffset = data->m_scatterTargets.at(target_idx); - DEBUG_LOG(("Shot Index: %d @ %f, %f", shot_index, scatterOffset.x, scatterOffset.y)); Coord3D targetPos = m_initialTargetPosition; //Calculate Target from Scatter - //TODO IndividualScalar? - scatterOffset.x *= data->m_attackAreaRadius; - scatterOffset.y *= data->m_attackAreaRadius; + scatterOffset.x *= data->m_missileScatterRadius; + scatterOffset.y *= data->m_missileScatterRadius; const Coord3D srcPos = *getObject()->getPosition(); Real angle = 0.0f; // getObject()->getOrientation(); @@ -724,7 +722,6 @@ UpdateSleepTime KodiakUpdate::update() if (targetLock != nullptr) { weap->fireWeapon(getObject(), targetLock); - //DEBUG_LOG(("TargetUnit: %s", targetLock->getTemplate()->getName().str())); } else { weap->fireWeapon(getObject(), &targetPos); @@ -830,11 +827,6 @@ void KodiakUpdate::cleanUp() m_attackAreaDecal.clear(); m_targetingReticleDecal.clear(); - - Object *gattling = TheGameLogic->findObjectByID( m_gattlingID ); - if ( gattling ) - TheGameLogic->destroyObject( gattling ); - #if defined TRACKERS m_howitzerTrackerDecal.clear(); #endif @@ -868,21 +860,6 @@ void KodiakUpdate::disengageAndDepartAO( Object *gunship ) } - //Disable all turrets - ContainModuleInterface* cmi = getObject()->getContain(); - if (cmi != nullptr) { - ContainedItemsList* addOns = cmi->getAddOnList(); - for (ContainedItemsList::iterator it = addOns->begin(); it != addOns->end(); it++) { - (*it)->setDisabled(DISABLED_PARALYZED); - } - } - - - //Object *gattling = TheGameLogic->findObjectByID( m_gattlingID ); - //if ( gattling ) - // gattling->setDisabled ( DISABLED_PARALYZED ); - - if ( shipAI) { shipAI->chooseLocomotorSet( LOCOMOTORSET_PANIC ); @@ -950,7 +927,7 @@ void KodiakUpdate::xfer( Xfer *xfer ) // The manual override target destination. xfer->xferCoord3D( &m_overrideTargetDestination ); // The current move-to point of the gunship. - xfer->xferCoord3D( &m_satellitePosition ); + xfer->xferCoord3D( &m_movementPosition ); // status xfer->xferUser( &m_status, sizeof( GunshipStatus ) ); @@ -968,10 +945,7 @@ void KodiakUpdate::xfer( Xfer *xfer ) if( version >= 2 ) { - xfer->xferCoord3D( &m_gattlingTargetPosition ); xfer->xferCoord3D( &m_positionToShootAt ); - xfer->xferUnsignedInt( &m_okToFireHowitzerCounter ); - xfer->xferObjectID( &m_gattlingID ); } } // end xfer From f434ca7ace21f08ed1790f032ea43cab0be6a064 Mon Sep 17 00:00:00 2001 From: pWn3d Date: Wed, 17 Sep 2025 19:01:57 +0200 Subject: [PATCH 6/7] cleanUp --- .../Source/GameLogic/Object/Update/KodiakUpdate.cpp | 7 ------- 1 file changed, 7 deletions(-) diff --git a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Update/KodiakUpdate.cpp b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Update/KodiakUpdate.cpp index fe949b98605..aa8fadd5f08 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Update/KodiakUpdate.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Update/KodiakUpdate.cpp @@ -39,18 +39,11 @@ #include "GameLogic/Module/ContainModule.h" - - - -#define ORBIT_INSERTION_SLOPE_MAX (0.8f) -#define ORBIT_INSERTION_SLOPE_MIN (0.5f) #define ONE (1.0f) #define ZERO (0.0f) #define LOTS_OF_SHOTS (9999) - - //------------------------------------------------------------------------------------------------- //------------------------------------------------------------------------------------------------- KodiakUpdateModuleData::KodiakUpdateModuleData() From f610a0fa54430dd755f412ff574dbb32d83c3b84 Mon Sep 17 00:00:00 2001 From: pWn3d Date: Wed, 17 Sep 2025 19:45:18 +0200 Subject: [PATCH 7/7] cleanup mr --- GeneralsMD/Code/Main/WinMain.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/GeneralsMD/Code/Main/WinMain.cpp b/GeneralsMD/Code/Main/WinMain.cpp index 9ca675a7f34..13ecdf162e8 100644 --- a/GeneralsMD/Code/Main/WinMain.cpp +++ b/GeneralsMD/Code/Main/WinMain.cpp @@ -803,9 +803,9 @@ Int APIENTRY WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, Int nCmdShow ) { Int exitcode = 1; -#ifdef _DEBUG - WaitForDebugger(); //in debug build, wait for debugger attachment -#endif +//#ifdef _DEBUG + // WaitForDebugger(); //in debug build, wait for debugger attachment +//#endif #ifdef RTS_PROFILE Profile::StartRange("init");