CommandButtonHuntUpdate.cpp 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405
  1. /*
  2. ** Command & Conquer Generals(tm)
  3. ** Copyright 2025 Electronic Arts Inc.
  4. **
  5. ** This program is free software: you can redistribute it and/or modify
  6. ** it under the terms of the GNU General Public License as published by
  7. ** the Free Software Foundation, either version 3 of the License, or
  8. ** (at your option) any later version.
  9. **
  10. ** This program is distributed in the hope that it will be useful,
  11. ** but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. ** GNU General Public License for more details.
  14. **
  15. ** You should have received a copy of the GNU General Public License
  16. ** along with this program. If not, see <http://www.gnu.org/licenses/>.
  17. */
  18. ////////////////////////////////////////////////////////////////////////////////
  19. // //
  20. // (c) 2001-2003 Electronic Arts Inc. //
  21. // //
  22. ////////////////////////////////////////////////////////////////////////////////
  23. // FILE: CommandButtonHuntUpdate.cpp //////////////////////////////////////////////////////////////////////////
  24. // Author: John Ahlquist, Sept. 2002
  25. // Desc: Update module to handle "Hunting" using a special power. If the unit is idle, and the
  26. // power is not active, it targets a new unit with the power. Note that this is an update rather than
  27. // an AI state because many of the special abilities use the ai to perform portions of the special
  28. // ability.
  29. ///////////////////////////////////////////////////////////////////////////////////////////////////
  30. // INCLUDES ///////////////////////////////////////////////////////////////////////////////////////
  31. #include "PreRTS.h" // This must go first in EVERY cpp file in the GameEngine
  32. #include "Common/ActionManager.h"
  33. #include "Common/Player.h"
  34. #include "Common/RandomValue.h"
  35. #include "Common/SpecialPower.h"
  36. #include "Common/ThingTemplate.h"
  37. #include "Common/Xfer.h"
  38. #include "GameClient/ControlBar.h"
  39. #include "GameClient/Drawable.h"
  40. #include "GameLogic/GameLogic.h"
  41. #include "GameLogic/PartitionManager.h"
  42. #include "GameLogic/Object.h"
  43. #include "GameLogic/ObjectIter.h"
  44. #include "GameLogic/Module\CommandButtonHuntUpdate.h"
  45. #include "GameLogic/Module\AIUpdate.h"
  46. #include "GameLogic/Module\CollideModule.h"
  47. #include "GameLogic/Module\SpecialAbilityUpdate.h"
  48. #include "GameLogic/Module\SpecialPowerModule.h"
  49. #include "GameLogic/ScriptEngine.h"
  50. #ifdef _INTERNAL
  51. // for occasional debugging...
  52. //#pragma optimize("", off)
  53. //#pragma MESSAGE("************************************** WARNING, optimization disabled for debugging purposes")
  54. #endif
  55. //-------------------------------------------------------------------------------------------------
  56. //-------------------------------------------------------------------------------------------------
  57. CommandButtonHuntUpdateModuleData::CommandButtonHuntUpdateModuleData()
  58. {
  59. m_scanFrames = LOGICFRAMES_PER_SECOND;
  60. m_scanRange = 9999.0f;
  61. }
  62. //-------------------------------------------------------------------------------------------------
  63. /*static*/ void CommandButtonHuntUpdateModuleData::buildFieldParse(MultiIniFieldParse& p)
  64. {
  65. ModuleData::buildFieldParse(p);
  66. static const FieldParse dataFieldParse[] =
  67. {
  68. { "ScanRate", INI::parseDurationUnsignedInt, NULL, offsetof( CommandButtonHuntUpdateModuleData, m_scanFrames ) },
  69. { "ScanRange", INI::parseReal, NULL, offsetof( CommandButtonHuntUpdateModuleData, m_scanRange ) },
  70. { 0, 0, 0, 0 }
  71. };
  72. p.add(dataFieldParse);
  73. }
  74. //-------------------------------------------------------------------------------------------------
  75. CommandButtonHuntUpdate::CommandButtonHuntUpdate( Thing *thing, const ModuleData* moduleData ) :
  76. UpdateModule( thing, moduleData ),
  77. m_commandButton(NULL)
  78. {
  79. setWakeFrame(getObject(), UPDATE_SLEEP_FOREVER);
  80. m_commandButtonName = AsciiString::TheEmptyString;
  81. }
  82. //-------------------------------------------------------------------------------------------------
  83. //-------------------------------------------------------------------------------------------------
  84. CommandButtonHuntUpdate::~CommandButtonHuntUpdate( void )
  85. {
  86. }
  87. //-------------------------------------------------------------------------------------------------
  88. void CommandButtonHuntUpdate::onObjectCreated()
  89. {
  90. }
  91. //-------------------------------------------------------------------------------------------------
  92. void CommandButtonHuntUpdate::setCommandButton(const AsciiString& buttonName)
  93. {
  94. Object *obj = getObject();
  95. m_commandButtonName = buttonName;
  96. m_commandButton = NULL;
  97. const CommandSet *commandSet = TheControlBar->findCommandSet( obj->getCommandSetString() );
  98. if( commandSet )
  99. {
  100. for( Int i = 0; i < MAX_COMMANDS_PER_SET; i++ )
  101. {
  102. //Get the command button.
  103. m_commandButton = commandSet->getCommandButton(i);
  104. if( m_commandButton )
  105. {
  106. if( !m_commandButton->getName().isEmpty() )
  107. {
  108. if( m_commandButton->getName() == m_commandButtonName )
  109. {
  110. break;
  111. }
  112. }
  113. }
  114. m_commandButton = NULL;
  115. }
  116. }
  117. if (m_commandButton==NULL) {
  118. return;
  119. }
  120. AIUpdateInterface *ai = obj->getAI();
  121. if (ai==NULL ) return;
  122. // Stop whatever we're doing.
  123. ai->aiIdle(CMD_FROM_AI);
  124. update();
  125. setWakeFrame(obj, UPDATE_SLEEP_NONE); // wake up.
  126. }
  127. //-------------------------------------------------------------------------------------------------
  128. /** The update callback. */
  129. //-------------------------------------------------------------------------------------------------
  130. UpdateSleepTime CommandButtonHuntUpdate::update()
  131. {
  132. Object *obj = getObject();
  133. AIUpdateInterface *ai = obj->getAI();
  134. if (ai==NULL || m_commandButton==NULL) return UPDATE_SLEEP_FOREVER;
  135. if (ai->getLastCommandSource() != CMD_FROM_AI) {
  136. // If a script or the player (in this case should only be script, but either way)
  137. // we quit hunting.
  138. m_commandButton = NULL;
  139. m_commandButtonName.clear();
  140. return UPDATE_SLEEP_FOREVER;
  141. }
  142. switch( m_commandButton->getCommandType() )
  143. {
  144. case GUI_COMMAND_SPECIAL_POWER:
  145. return huntSpecialPower(ai);
  146. case GUI_COMMAND_SWITCH_WEAPON:
  147. case GUI_COMMAND_FIRE_WEAPON:
  148. return huntWeapon(ai);
  149. default:
  150. return UPDATE_SLEEP_FOREVER;
  151. }
  152. return UPDATE_SLEEP_FOREVER;
  153. }
  154. //-------------------------------------------------------------------------------------------------
  155. /** Do a special power weapon. */
  156. //-------------------------------------------------------------------------------------------------
  157. UpdateSleepTime CommandButtonHuntUpdate::huntWeapon(AIUpdateInterface *ai)
  158. {
  159. Object *obj = getObject();
  160. if( ai->isIdle() )
  161. {
  162. ai->aiHunt(CMD_FROM_AI);
  163. }
  164. WeaponSlotType weaponSlot = m_commandButton->getWeaponSlot();
  165. // lock it just till the weapon is empty or the attack is "done"
  166. obj->setWeaponLock( weaponSlot, LOCKED_TEMPORARILY );
  167. return UPDATE_SLEEP_NONE;
  168. }
  169. //-------------------------------------------------------------------------------------------------
  170. /** Do a special power hunt. */
  171. //-------------------------------------------------------------------------------------------------
  172. UpdateSleepTime CommandButtonHuntUpdate::huntSpecialPower(AIUpdateInterface *ai)
  173. {
  174. Object *obj = getObject();
  175. const CommandButtonHuntUpdateModuleData *data = getCommandButtonHuntUpdateModuleData();
  176. if( !ai->isIdle() )
  177. {
  178. return UPDATE_SLEEP(data->m_scanFrames);
  179. }
  180. const SpecialPowerTemplate *spTemplate = m_commandButton->getSpecialPowerTemplate();
  181. if( spTemplate )
  182. {
  183. SpecialAbilityUpdate* spUpdate = obj->findSpecialAbilityUpdate( spTemplate->getSpecialPowerType() );
  184. if (spUpdate == NULL) return UPDATE_SLEEP_FOREVER;
  185. if (spUpdate->isActive()) {
  186. return UPDATE_SLEEP(data->m_scanFrames);
  187. }
  188. }
  189. //Periodic scanning (expensive)
  190. Object *victim = scanClosestTarget();
  191. if(victim)
  192. {
  193. obj->doCommandButtonAtObject( m_commandButton, victim, CMD_FROM_AI );
  194. }
  195. return UPDATE_SLEEP(data->m_scanFrames);
  196. }
  197. //-------------------------------------------------------------------------------------------------
  198. Object* CommandButtonHuntUpdate::scanClosestTarget(void)
  199. {
  200. const CommandButtonHuntUpdateModuleData *data = getCommandButtonHuntUpdateModuleData();
  201. Object *me = getObject();
  202. PartitionFilterAlive aliveFilter;
  203. // only consider enemies.
  204. PartitionFilterRelationship filterTeam(me, PartitionFilterRelationship::ALLOW_ENEMIES);
  205. PartitionFilterSameMapStatus filterMapStatus(getObject());
  206. PartitionFilter *filters[4];
  207. filters[0] = &aliveFilter;
  208. filters[1] = &filterMapStatus;
  209. filters[2] = &filterTeam;
  210. filters[3] = NULL;
  211. const SpecialPowerTemplate *spTemplate = m_commandButton->getSpecialPowerTemplate();
  212. if( !spTemplate ) return NULL; // isn't going to happen.
  213. Bool isBlackLotusVehicleHack = (spTemplate->getSpecialPowerType() == SPECIAL_BLACKLOTUS_DISABLE_VEHICLE_HACK);
  214. Bool isCaptureBuilding = (spTemplate->getSpecialPowerType() == SPECIAL_INFANTRY_CAPTURE_BUILDING);
  215. if (isCaptureBuilding) {
  216. filters[2] = NULL; // It's ok (in fact necessary for oil derricks) to capture special buildings.
  217. }
  218. Bool isPlaceExplosive = false;
  219. if (spTemplate->getSpecialPowerType() == SPECIAL_TIMED_CHARGES) isPlaceExplosive = true;
  220. if (spTemplate->getSpecialPowerType() == SPECIAL_TANKHUNTER_TNT_ATTACK) isPlaceExplosive = true;
  221. ObjectIterator *iter = ThePartitionManager->iterateObjectsInRange( me->getPosition(), data->m_scanRange,
  222. FROM_CENTER_2D, filters, ITER_SORTED_NEAR_TO_FAR );
  223. MemoryPoolObjectHolder hold(iter);
  224. Object *bestTarget = NULL;
  225. Int effectivePriority=0;
  226. Int actualPriority=0;
  227. const AttackPriorityInfo *info=NULL;
  228. if (me->getAI()) {
  229. info = me->getAI()->getAttackInfo();
  230. }
  231. SpecialPowerModuleInterface *mod = me->getSpecialPowerModule( spTemplate );
  232. if( mod )
  233. {
  234. for( Object *other = iter->first(); other; other = iter->next() )
  235. {
  236. if (other->isDisabled() && isBlackLotusVehicleHack) {
  237. // The hack disables the vehicle, so we don't want to do it again.
  238. continue;
  239. }
  240. if (isCaptureBuilding) {
  241. if (me->getControllingPlayer() == other->getControllingPlayer()) {
  242. continue; // no need to capture our own buildings.
  243. }
  244. if (me->getRelationship(other) == ALLIES) {
  245. continue; // It's not polite to capture allies buildings.
  246. }
  247. }
  248. if( TheActionManager->canDoSpecialPowerAtObject( me, other, CMD_FROM_AI, spTemplate, 0 ) )
  249. {
  250. if (isPlaceExplosive) {
  251. Real range = spTemplate->getViewObjectRange();
  252. // Don't target things near explosives... It's just not a good idea.
  253. PartitionFilterSamePlayer filterPlayer( me->getControllingPlayer() ); // Look for our own mines.
  254. PartitionFilterAcceptByKindOf filterKind(MAKE_KINDOF_MASK(KINDOF_MINE), KINDOFMASK_NONE);
  255. PartitionFilter *filters[] = { &filterKind, &filterPlayer, NULL };
  256. Object *mine = ThePartitionManager->getClosestObject( other, range, FROM_BOUNDINGSPHERE_2D, filters );// could be null. this is ok.
  257. if (mine) {
  258. continue;
  259. }
  260. }
  261. Real distSqr = ThePartitionManager->getDistanceSquared(me, other, FROM_BOUNDINGSPHERE_2D);
  262. Real dist = sqrt(distSqr);
  263. Int curPriority = data->m_scanRange - dist;
  264. if (info) curPriority = info->getPriority(other->getTemplate());
  265. if (curPriority == 0)
  266. continue; // don't attack 0 priority targets.
  267. Int modifier = dist/TheAI->getAiData()->m_attackPriorityDistanceModifier;
  268. Int modPriority = curPriority-modifier;
  269. if (modPriority < 1)
  270. modPriority = 1;
  271. if (modPriority > effectivePriority)
  272. {
  273. effectivePriority = modPriority;
  274. actualPriority = curPriority;
  275. bestTarget = other;
  276. }
  277. if (modPriority == effectivePriority && curPriority > actualPriority)
  278. {
  279. effectivePriority = modPriority;
  280. actualPriority = curPriority;
  281. bestTarget = other;
  282. }
  283. }
  284. }
  285. }
  286. return bestTarget;
  287. }
  288. // ------------------------------------------------------------------------------------------------
  289. /** CRC */
  290. // ------------------------------------------------------------------------------------------------
  291. void CommandButtonHuntUpdate::crc( Xfer *xfer )
  292. {
  293. // extend base class
  294. UpdateModule::crc( xfer );
  295. } // end crc
  296. // ------------------------------------------------------------------------------------------------
  297. /** Xfer method
  298. * Version Info:
  299. * 1: Initial version */
  300. // ------------------------------------------------------------------------------------------------
  301. void CommandButtonHuntUpdate::xfer( Xfer *xfer )
  302. {
  303. // version
  304. XferVersion currentVersion = 1;
  305. XferVersion version = currentVersion;
  306. xfer->xferVersion( &version, currentVersion );
  307. // extend base class
  308. UpdateModule::xfer( xfer );
  309. // command button name
  310. xfer->xferAsciiString( &m_commandButtonName );
  311. // command button pointer (on loading only)
  312. if( xfer->getXferMode() == XFER_LOAD )
  313. {
  314. // initialize to no command button
  315. m_commandButton = NULL;
  316. // find command button pointer if name is present
  317. if( m_commandButtonName.isEmpty() == FALSE )
  318. {
  319. Object *us = getObject();
  320. const CommandSet *commandSet = TheControlBar->findCommandSet( us->getCommandSetString() );
  321. if( commandSet )
  322. {
  323. const CommandButton *button;
  324. for( Int i = 0; i < MAX_COMMANDS_PER_SET; i++ )
  325. {
  326. button = commandSet->getCommandButton(i);
  327. if( button && button->getName() == m_commandButtonName )
  328. {
  329. m_commandButton = button;
  330. break; // exit for, i
  331. } // end if
  332. } // end for, i
  333. } // end if, commandSet
  334. } // end if, command button name present
  335. } // end if, loading
  336. } // end xfer
  337. // ------------------------------------------------------------------------------------------------
  338. /** Load post process */
  339. // ------------------------------------------------------------------------------------------------
  340. void CommandButtonHuntUpdate::loadPostProcess( void )
  341. {
  342. // extend base class
  343. UpdateModule::loadPostProcess();
  344. } // end loadPostProcess