/* ** Command & Conquer Generals Zero Hour(tm) ** Copyright 2025 Electronic Arts Inc. ** ** This program is free software: you can redistribute it and/or modify ** it under the terms of the GNU General Public License as published by ** the Free Software Foundation, either version 3 of the License, or ** (at your option) any later version. ** ** This program is distributed in the hope that it will be useful, ** but WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ** GNU General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with this program. If not, see . */ //////////////////////////////////////////////////////////////////////////////// // // // (c) 2001-2003 Electronic Arts Inc. // // // //////////////////////////////////////////////////////////////////////////////// // FILE: SpectreGunshipDeploymentUpdate.cpp ////////////////////////////////////////////////////////////////////////// // Author: Mark Lorenzen, April 2003 // Desc: Update module to handle deployment of the SpectreGunship 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\SpectreGunshipDeploymentUpdate.h" #include "GameLogic\Module\PhysicsUpdate.h" #include "GameLogic\Module\LaserUpdate.h" #include "GameLogic\Module\ActiveBody.h" #include "GameLogic\Module\AIUpdate.h" #include "GameLogic\Module\ContainModule.h" #ifdef _INTERNAL // for occasional debugging... //#pragma optimize("", off) //#pragma MESSAGE("************************************** WARNING, optimization disabled for debugging purposes") #endif //------------------------------------------------------------------------------------------------- //------------------------------------------------------------------------------------------------- SpectreGunshipDeploymentUpdateModuleData::SpectreGunshipDeploymentUpdateModuleData() { 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 SpectreGunshipDeploymentUpdateModuleData::buildFieldParse(MultiIniFieldParse& p) { ModuleData::buildFieldParse(p); static const FieldParse dataFieldParse[] = { { "GunshipTemplateName", INI::parseAsciiString, NULL, offsetof( SpectreGunshipDeploymentUpdateModuleData, m_gunshipTemplateName ) }, { "RequiredScience", INI::parseScience, NULL, offsetof( SpectreGunshipDeploymentUpdateModuleData, m_extraRequiredScience ) }, /******BOTH*******/ { "SpecialPowerTemplate", INI::parseSpecialPowerTemplate, NULL, offsetof( SpectreGunshipDeploymentUpdateModuleData, m_specialPowerTemplate ) }, /*******BOTH******/ { "AttackAreaRadius", INI::parseReal, NULL, offsetof( SpectreGunshipDeploymentUpdateModuleData, m_attackAreaRadius ) }, { "CreateLocation", INI::parseIndexList, TheGunshipCreateLocTypeNames, offsetof( SpectreGunshipDeploymentUpdateModuleData, m_createLoc ) }, { 0, 0, 0, 0 } }; p.add(dataFieldParse); } //------------------------------------------------------------------------------------------------- SpectreGunshipDeploymentUpdate::SpectreGunshipDeploymentUpdate( Thing *thing, const ModuleData* moduleData ) : SpecialPowerUpdateModule( thing, moduleData ) { m_specialPowerModule = NULL; m_gunshipID = INVALID_ID; } //------------------------------------------------------------------------------------------------- SpectreGunshipDeploymentUpdate::~SpectreGunshipDeploymentUpdate( void ) { } //------------------------------------------------------------------------------------------------- // Validate that we have the necessary data from the ini file. //------------------------------------------------------------------------------------------------- void SpectreGunshipDeploymentUpdate::onObjectCreated() { const SpectreGunshipDeploymentUpdateModuleData *data = getSpectreGunshipDeploymentUpdateModuleData(); Object *obj = getObject(); if( !data->m_specialPowerTemplate ) { DEBUG_CRASH( ("%s object's SpectreGunshipDeploymentUpdate lacks access to the SpecialPowerTemplate. Needs to be specified in ini.", obj->getTemplate()->getName().str() ) ); return; } m_specialPowerModule = obj->getSpecialPowerModule( data->m_specialPowerTemplate ); } //------------------------------------------------------------------------------------------------- Bool SpectreGunshipDeploymentUpdate::initiateIntentToDoSpecialPower(const SpecialPowerTemplate *specialPowerTemplate, const Object *targetObj, const Coord3D *targetPos, const Waypoint *way, UnsignedInt commandOptions ) { const SpectreGunshipDeploymentUpdateModuleData *data = getSpectreGunshipDeploymentUpdateModuleData(); if( m_specialPowerModule->getSpecialPowerTemplate() != specialPowerTemplate ) { //Check to make sure our modules are connected. return FALSE; } // getObject()->getControllingPlayer()->getAcademyStats()->recordSpecialPowerUsed( specialPowerTemplate ); if( !BitTest( commandOptions, COMMAND_FIRED_BY_SCRIPT ) ) { /******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, ("SpecterGunshipUpdate 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 TheGameLogic->selectObject( newGunship, TRUE, getObject()->getControllingPlayer()->getPlayerMask(), TRUE ); } SpecialPowerModuleInterface *spmInterface = getObject()->getSpecialPowerModule( specialPowerTemplate ); if( spmInterface ) { SpecialPowerModule *spModule = (SpecialPowerModule*)spmInterface; spModule->markSpecialPowerTriggered( &m_initialTargetPosition ); } return TRUE; } //------------------------------------------------------------------------------------------------- /** The update callback. */ //------------------------------------------------------------------------------------------------- UpdateSleepTime SpectreGunshipDeploymentUpdate::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 SpectreGunshipDeploymentUpdate::crc( Xfer *xfer ) { // extend base class UpdateModule::crc( xfer ); } // end crc // ------------------------------------------------------------------------------------------------ /** Xfer method * Version Info: * 1: Initial version */ // ------------------------------------------------------------------------------------------------ void SpectreGunshipDeploymentUpdate::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 SpectreGunshipDeploymentUpdate::loadPostProcess( void ) { // extend base class UpdateModule::loadPostProcess(); } // end loadPostProcess