| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405 |
- /*
- ** Command & Conquer Generals(tm)
- ** Copyright 2025 Electronic Arts Inc.
- **
- ** This program is free software: you can redistribute it and/or modify
- ** it under the terms of the GNU General Public License as published by
- ** the Free Software Foundation, either version 3 of the License, or
- ** (at your option) any later version.
- **
- ** This program is distributed in the hope that it will be useful,
- ** but WITHOUT ANY WARRANTY; without even the implied warranty of
- ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- ** GNU General Public License for more details.
- **
- ** You should have received a copy of the GNU General Public License
- ** along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
- ////////////////////////////////////////////////////////////////////////////////
- // //
- // (c) 2001-2003 Electronic Arts Inc. //
- // //
- ////////////////////////////////////////////////////////////////////////////////
- // FILE: CommandButtonHuntUpdate.cpp //////////////////////////////////////////////////////////////////////////
- // Author: John Ahlquist, Sept. 2002
- // Desc: Update module to handle "Hunting" using a special power. If the unit is idle, and the
- // power is not active, it targets a new unit with the power. Note that this is an update rather than
- // an AI state because many of the special abilities use the ai to perform portions of the special
- // ability.
- ///////////////////////////////////////////////////////////////////////////////////////////////////
- // INCLUDES ///////////////////////////////////////////////////////////////////////////////////////
- #include "PreRTS.h" // This must go first in EVERY cpp file in the GameEngine
- #include "Common/ActionManager.h"
- #include "Common/Player.h"
- #include "Common/RandomValue.h"
- #include "Common/SpecialPower.h"
- #include "Common/ThingTemplate.h"
- #include "Common/Xfer.h"
- #include "GameClient/ControlBar.h"
- #include "GameClient/Drawable.h"
- #include "GameLogic/GameLogic.h"
- #include "GameLogic/PartitionManager.h"
- #include "GameLogic/Object.h"
- #include "GameLogic/ObjectIter.h"
- #include "GameLogic/Module\CommandButtonHuntUpdate.h"
- #include "GameLogic/Module\AIUpdate.h"
- #include "GameLogic/Module\CollideModule.h"
- #include "GameLogic/Module\SpecialAbilityUpdate.h"
- #include "GameLogic/Module\SpecialPowerModule.h"
- #include "GameLogic/ScriptEngine.h"
- #ifdef _INTERNAL
- // for occasional debugging...
- //#pragma optimize("", off)
- //#pragma MESSAGE("************************************** WARNING, optimization disabled for debugging purposes")
- #endif
- //-------------------------------------------------------------------------------------------------
- //-------------------------------------------------------------------------------------------------
- CommandButtonHuntUpdateModuleData::CommandButtonHuntUpdateModuleData()
- {
- m_scanFrames = LOGICFRAMES_PER_SECOND;
- m_scanRange = 9999.0f;
- }
- //-------------------------------------------------------------------------------------------------
- /*static*/ void CommandButtonHuntUpdateModuleData::buildFieldParse(MultiIniFieldParse& p)
- {
- ModuleData::buildFieldParse(p);
- static const FieldParse dataFieldParse[] =
- {
- { "ScanRate", INI::parseDurationUnsignedInt, NULL, offsetof( CommandButtonHuntUpdateModuleData, m_scanFrames ) },
- { "ScanRange", INI::parseReal, NULL, offsetof( CommandButtonHuntUpdateModuleData, m_scanRange ) },
- { 0, 0, 0, 0 }
- };
- p.add(dataFieldParse);
- }
- //-------------------------------------------------------------------------------------------------
- CommandButtonHuntUpdate::CommandButtonHuntUpdate( Thing *thing, const ModuleData* moduleData ) :
- UpdateModule( thing, moduleData ),
- m_commandButton(NULL)
- {
- setWakeFrame(getObject(), UPDATE_SLEEP_FOREVER);
- m_commandButtonName = AsciiString::TheEmptyString;
- }
- //-------------------------------------------------------------------------------------------------
- //-------------------------------------------------------------------------------------------------
- CommandButtonHuntUpdate::~CommandButtonHuntUpdate( void )
- {
- }
- //-------------------------------------------------------------------------------------------------
- void CommandButtonHuntUpdate::onObjectCreated()
- {
-
- }
- //-------------------------------------------------------------------------------------------------
- void CommandButtonHuntUpdate::setCommandButton(const AsciiString& buttonName)
- {
- Object *obj = getObject();
- m_commandButtonName = buttonName;
- m_commandButton = NULL;
- const CommandSet *commandSet = TheControlBar->findCommandSet( obj->getCommandSetString() );
- if( commandSet )
- {
- for( Int i = 0; i < MAX_COMMANDS_PER_SET; i++ )
- {
- //Get the command button.
- m_commandButton = commandSet->getCommandButton(i);
- if( m_commandButton )
- {
- if( !m_commandButton->getName().isEmpty() )
- {
- if( m_commandButton->getName() == m_commandButtonName )
- {
- break;
- }
- }
- }
- m_commandButton = NULL;
- }
- }
- if (m_commandButton==NULL) {
- return;
- }
- AIUpdateInterface *ai = obj->getAI();
- if (ai==NULL ) return;
- // Stop whatever we're doing.
- ai->aiIdle(CMD_FROM_AI);
- update();
- setWakeFrame(obj, UPDATE_SLEEP_NONE); // wake up.
- }
- //-------------------------------------------------------------------------------------------------
- /** The update callback. */
- //-------------------------------------------------------------------------------------------------
- UpdateSleepTime CommandButtonHuntUpdate::update()
- {
- Object *obj = getObject();
- AIUpdateInterface *ai = obj->getAI();
- if (ai==NULL || m_commandButton==NULL) return UPDATE_SLEEP_FOREVER;
- if (ai->getLastCommandSource() != CMD_FROM_AI) {
- // If a script or the player (in this case should only be script, but either way)
- // we quit hunting.
- m_commandButton = NULL;
- m_commandButtonName.clear();
- return UPDATE_SLEEP_FOREVER;
- }
- switch( m_commandButton->getCommandType() )
- {
- case GUI_COMMAND_SPECIAL_POWER:
- return huntSpecialPower(ai);
- case GUI_COMMAND_SWITCH_WEAPON:
- case GUI_COMMAND_FIRE_WEAPON:
- return huntWeapon(ai);
- default:
- return UPDATE_SLEEP_FOREVER;
- }
- return UPDATE_SLEEP_FOREVER;
- }
- //-------------------------------------------------------------------------------------------------
- /** Do a special power weapon. */
- //-------------------------------------------------------------------------------------------------
- UpdateSleepTime CommandButtonHuntUpdate::huntWeapon(AIUpdateInterface *ai)
- {
- Object *obj = getObject();
- if( ai->isIdle() )
- {
- ai->aiHunt(CMD_FROM_AI);
- }
- WeaponSlotType weaponSlot = m_commandButton->getWeaponSlot();
- // lock it just till the weapon is empty or the attack is "done"
- obj->setWeaponLock( weaponSlot, LOCKED_TEMPORARILY );
- return UPDATE_SLEEP_NONE;
- }
- //-------------------------------------------------------------------------------------------------
- /** Do a special power hunt. */
- //-------------------------------------------------------------------------------------------------
- UpdateSleepTime CommandButtonHuntUpdate::huntSpecialPower(AIUpdateInterface *ai)
- {
- Object *obj = getObject();
- const CommandButtonHuntUpdateModuleData *data = getCommandButtonHuntUpdateModuleData();
- if( !ai->isIdle() )
- {
- return UPDATE_SLEEP(data->m_scanFrames);
- }
- const SpecialPowerTemplate *spTemplate = m_commandButton->getSpecialPowerTemplate();
- if( spTemplate )
- {
- SpecialAbilityUpdate* spUpdate = obj->findSpecialAbilityUpdate( spTemplate->getSpecialPowerType() );
- if (spUpdate == NULL) return UPDATE_SLEEP_FOREVER;
- if (spUpdate->isActive()) {
- return UPDATE_SLEEP(data->m_scanFrames);
- }
- }
- //Periodic scanning (expensive)
- Object *victim = scanClosestTarget();
- if(victim)
- {
- obj->doCommandButtonAtObject( m_commandButton, victim, CMD_FROM_AI );
- }
- return UPDATE_SLEEP(data->m_scanFrames);
- }
- //-------------------------------------------------------------------------------------------------
- Object* CommandButtonHuntUpdate::scanClosestTarget(void)
- {
- const CommandButtonHuntUpdateModuleData *data = getCommandButtonHuntUpdateModuleData();
- Object *me = getObject();
- PartitionFilterAlive aliveFilter;
- // only consider enemies.
- PartitionFilterRelationship filterTeam(me, PartitionFilterRelationship::ALLOW_ENEMIES);
- PartitionFilterSameMapStatus filterMapStatus(getObject());
- PartitionFilter *filters[4];
- filters[0] = &aliveFilter;
- filters[1] = &filterMapStatus;
- filters[2] = &filterTeam;
- filters[3] = NULL;
- const SpecialPowerTemplate *spTemplate = m_commandButton->getSpecialPowerTemplate();
- if( !spTemplate ) return NULL; // isn't going to happen.
- Bool isBlackLotusVehicleHack = (spTemplate->getSpecialPowerType() == SPECIAL_BLACKLOTUS_DISABLE_VEHICLE_HACK);
- Bool isCaptureBuilding = (spTemplate->getSpecialPowerType() == SPECIAL_INFANTRY_CAPTURE_BUILDING);
- if (isCaptureBuilding) {
- filters[2] = NULL; // It's ok (in fact necessary for oil derricks) to capture special buildings.
- }
- Bool isPlaceExplosive = false;
- if (spTemplate->getSpecialPowerType() == SPECIAL_TIMED_CHARGES) isPlaceExplosive = true;
- if (spTemplate->getSpecialPowerType() == SPECIAL_TANKHUNTER_TNT_ATTACK) isPlaceExplosive = true;
- ObjectIterator *iter = ThePartitionManager->iterateObjectsInRange( me->getPosition(), data->m_scanRange,
- FROM_CENTER_2D, filters, ITER_SORTED_NEAR_TO_FAR );
- MemoryPoolObjectHolder hold(iter);
- Object *bestTarget = NULL;
- Int effectivePriority=0;
- Int actualPriority=0;
- const AttackPriorityInfo *info=NULL;
- if (me->getAI()) {
- info = me->getAI()->getAttackInfo();
- }
- SpecialPowerModuleInterface *mod = me->getSpecialPowerModule( spTemplate );
- if( mod )
- {
- for( Object *other = iter->first(); other; other = iter->next() )
- {
- if (other->isDisabled() && isBlackLotusVehicleHack) {
- // The hack disables the vehicle, so we don't want to do it again.
- continue;
- }
- if (isCaptureBuilding) {
- if (me->getControllingPlayer() == other->getControllingPlayer()) {
- continue; // no need to capture our own buildings.
- }
- if (me->getRelationship(other) == ALLIES) {
- continue; // It's not polite to capture allies buildings.
- }
- }
- if( TheActionManager->canDoSpecialPowerAtObject( me, other, CMD_FROM_AI, spTemplate, 0 ) )
- {
- if (isPlaceExplosive) {
- Real range = spTemplate->getViewObjectRange();
- // Don't target things near explosives... It's just not a good idea.
- PartitionFilterSamePlayer filterPlayer( me->getControllingPlayer() ); // Look for our own mines.
- PartitionFilterAcceptByKindOf filterKind(MAKE_KINDOF_MASK(KINDOF_MINE), KINDOFMASK_NONE);
- PartitionFilter *filters[] = { &filterKind, &filterPlayer, NULL };
- Object *mine = ThePartitionManager->getClosestObject( other, range, FROM_BOUNDINGSPHERE_2D, filters );// could be null. this is ok.
- if (mine) {
- continue;
- }
- }
- Real distSqr = ThePartitionManager->getDistanceSquared(me, other, FROM_BOUNDINGSPHERE_2D);
- Real dist = sqrt(distSqr);
- Int curPriority = data->m_scanRange - dist;
- if (info) curPriority = info->getPriority(other->getTemplate());
- if (curPriority == 0)
- continue; // don't attack 0 priority targets.
- Int modifier = dist/TheAI->getAiData()->m_attackPriorityDistanceModifier;
- Int modPriority = curPriority-modifier;
- if (modPriority < 1)
- modPriority = 1;
- if (modPriority > effectivePriority)
- {
- effectivePriority = modPriority;
- actualPriority = curPriority;
- bestTarget = other;
- }
- if (modPriority == effectivePriority && curPriority > actualPriority)
- {
- effectivePriority = modPriority;
- actualPriority = curPriority;
- bestTarget = other;
- }
- }
- }
- }
- return bestTarget;
- }
- // ------------------------------------------------------------------------------------------------
- /** CRC */
- // ------------------------------------------------------------------------------------------------
- void CommandButtonHuntUpdate::crc( Xfer *xfer )
- {
- // extend base class
- UpdateModule::crc( xfer );
- } // end crc
- // ------------------------------------------------------------------------------------------------
- /** Xfer method
- * Version Info:
- * 1: Initial version */
- // ------------------------------------------------------------------------------------------------
- void CommandButtonHuntUpdate::xfer( Xfer *xfer )
- {
- // version
- XferVersion currentVersion = 1;
- XferVersion version = currentVersion;
- xfer->xferVersion( &version, currentVersion );
- // extend base class
- UpdateModule::xfer( xfer );
- // command button name
- xfer->xferAsciiString( &m_commandButtonName );
- // command button pointer (on loading only)
- if( xfer->getXferMode() == XFER_LOAD )
- {
- // initialize to no command button
- m_commandButton = NULL;
- // find command button pointer if name is present
- if( m_commandButtonName.isEmpty() == FALSE )
- {
- Object *us = getObject();
- const CommandSet *commandSet = TheControlBar->findCommandSet( us->getCommandSetString() );
-
- if( commandSet )
- {
- const CommandButton *button;
- for( Int i = 0; i < MAX_COMMANDS_PER_SET; i++ )
- {
- button = commandSet->getCommandButton(i);
- if( button && button->getName() == m_commandButtonName )
- {
-
- m_commandButton = button;
- break; // exit for, i
- } // end if
- } // end for, i
- } // end if, commandSet
- } // end if, command button name present
- } // end if, loading
- } // end xfer
- // ------------------------------------------------------------------------------------------------
- /** Load post process */
- // ------------------------------------------------------------------------------------------------
- void CommandButtonHuntUpdate::loadPostProcess( void )
- {
- // extend base class
- UpdateModule::loadPostProcess();
- } // end loadPostProcess
|