AI.cpp 32 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038
  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. // AI.cpp
  24. // The Artificial Intelligence system
  25. // Author: Michael S. Booth, November 2000
  26. #include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
  27. #include "Common/CRCDebug.h"
  28. #include "Common/GameState.h"
  29. #include "Common/PerfTimer.h"
  30. #include "Common/Player.h"
  31. #include "Common/PlayerList.h"
  32. #include "Common/ThingTemplate.h"
  33. #include "Common/Xfer.h"
  34. #include "Common/XferCRC.h"
  35. #include "GameLogic/AI.h"
  36. #include "GameLogic/PartitionManager.h"
  37. #include "GameLogic/Module/AIUpdate.h"
  38. #include "GameLogic/Module/ContainModule.h"
  39. #include "GameLogic/ScriptEngine.h"
  40. #include "GameLogic/SidesList.h"
  41. #include "GameLogic/AIPathfind.h"
  42. #include "GameLogic/Weapon.h"
  43. extern void addIcon(const Coord3D *pos, Real width, Int numFramesDuration, RGBColor color);
  44. #ifdef _INTERNAL
  45. // for occasional debugging...
  46. //#pragma optimize("", off)
  47. //#pragma MESSAGE("************************************** WARNING, optimization disabled for debugging purposes")
  48. #endif
  49. ///////////////////////////////////////////////////////////////////////////////////////////////////
  50. // PRIVATE CLASS ///////////////////////////////////////////////////////////////////////////////////
  51. ///////////////////////////////////////////////////////////////////////////////////////////////////
  52. void TAiData::addSideInfo(AISideInfo *infoToAdd)
  53. {
  54. infoToAdd->m_next = m_sideInfo;
  55. m_sideInfo = infoToAdd;
  56. }
  57. void TAiData::addFactionBuildList(AISideBuildList *buildList)
  58. {
  59. AISideBuildList *info = m_sideBuildLists;
  60. while (info) {
  61. if (buildList->m_side == info->m_side) {
  62. if (info->m_buildList)
  63. info->m_buildList->deleteInstance();
  64. info->m_buildList = buildList->m_buildList;
  65. buildList->m_buildList = NULL;
  66. buildList->m_next = NULL;
  67. buildList->deleteInstance();
  68. return;
  69. }
  70. info = info->m_next;
  71. }
  72. buildList->m_next = m_sideBuildLists;
  73. m_sideBuildLists = buildList;
  74. }
  75. TAiData::~TAiData()
  76. {
  77. AISideInfo *info = m_sideInfo;
  78. m_sideInfo = NULL;
  79. while (info) {
  80. AISideInfo *cur = info;
  81. info = info->m_next;
  82. cur->deleteInstance();
  83. }
  84. AISideBuildList *build = m_sideBuildLists;
  85. m_sideBuildLists = NULL;
  86. while (build) {
  87. AISideBuildList *cur = build;
  88. build = build->m_next;
  89. cur->deleteInstance();
  90. }
  91. }
  92. ///////////////////////////////////////////////////////////////////////////////////////////////////
  93. // PRIVATE CLASS ///////////////////////////////////////////////////////////////////////////////////
  94. ///////////////////////////////////////////////////////////////////////////////////////////////////
  95. AISideBuildList::AISideBuildList( AsciiString side ) :
  96. m_side(side),
  97. m_buildList(NULL),
  98. m_next(NULL)
  99. {
  100. }
  101. AISideBuildList::~AISideBuildList()
  102. {
  103. if (m_buildList) {
  104. m_buildList->deleteInstance(); // note - deletes all in the list.
  105. }
  106. m_buildList = NULL;
  107. }
  108. void AISideBuildList::addInfo(BuildListInfo *info)
  109. {
  110. // Add to the end of the list.
  111. if (m_buildList == NULL) {
  112. m_buildList = info;
  113. } else {
  114. BuildListInfo *cur = m_buildList;
  115. while (cur && cur->getNext()) {
  116. cur = cur->getNext();
  117. }
  118. DEBUG_ASSERTCRASH(cur && cur->getNext()==NULL, ("Logic error."));
  119. cur->setNextBuildList(info);
  120. }
  121. info->setNextBuildList(NULL); // should be at the end of the list.
  122. }
  123. ///////////////////////////////////////////////////////////////////////////////////////////////////
  124. // PRIVATE DATA ///////////////////////////////////////////////////////////////////////////////////
  125. ///////////////////////////////////////////////////////////////////////////////////////////////////
  126. static const FieldParse TheAIFieldParseTable[] =
  127. {
  128. { "StructureSeconds", INI::parseReal,NULL, offsetof( TAiData, m_structureSeconds ) },
  129. { "TeamSeconds", INI::parseReal,NULL, offsetof( TAiData, m_teamSeconds ) },
  130. { "Wealthy", INI::parseInt,NULL, offsetof( TAiData, m_resourcesWealthy ) },
  131. { "Poor", INI::parseInt,NULL, offsetof( TAiData, m_resourcesPoor ) },
  132. { "ForceIdleMSEC", INI::parseDurationUnsignedInt,NULL,offsetof( TAiData, m_forceIdleFramesCount ) },
  133. { "StructuresWealthyRate", INI::parseReal,NULL, offsetof( TAiData, m_structuresWealthyMod ) },
  134. { "TeamsWealthyRate", INI::parseReal,NULL, offsetof( TAiData, m_teamWealthyMod ) },
  135. { "StructuresPoorRate", INI::parseReal,NULL, offsetof( TAiData, m_structuresPoorMod ) },
  136. { "TeamsPoorRate", INI::parseReal,NULL, offsetof( TAiData, m_teamPoorMod ) },
  137. { "TeamResourcesToStart", INI::parseReal,NULL, offsetof( TAiData, m_teamResourcesToBuild ) },
  138. { "GuardInnerModifierAI", INI::parseReal,NULL, offsetof( TAiData, m_guardInnerModifierAI ) },
  139. { "GuardOuterModifierAI", INI::parseReal,NULL, offsetof( TAiData, m_guardOuterModifierAI ) },
  140. { "GuardInnerModifierHuman",INI::parseReal,NULL, offsetof( TAiData, m_guardInnerModifierHuman ) },
  141. { "GuardOuterModifierHuman",INI::parseReal,NULL, offsetof( TAiData, m_guardOuterModifierHuman ) },
  142. { "GuardChaseUnitsDuration", INI::parseDurationUnsignedInt,NULL, offsetof( TAiData, m_guardChaseUnitFrames ) },
  143. { "GuardEnemyScanRate", INI::parseDurationUnsignedInt,NULL, offsetof( TAiData, m_guardEnemyScanRate ) },
  144. { "GuardEnemyReturnScanRate", INI::parseDurationUnsignedInt,NULL, offsetof( TAiData, m_guardEnemyReturnScanRate ) },
  145. { "SkirmishGroupFudgeDistance", INI::parseReal,NULL, offsetof( TAiData, m_skirmishGroupFudgeValue ) },
  146. { "RepulsedDistance", INI::parseReal,NULL, offsetof( TAiData, m_repulsedDistance ) },
  147. { "EnableRepulsors", INI::parseBool,NULL, offsetof( TAiData, m_enableRepulsors ) },
  148. { "AlertRangeModifier", INI::parseReal,NULL, offsetof( TAiData, m_alertRangeModifier) },
  149. { "AggressiveRangeModifier",INI::parseReal,NULL, offsetof( TAiData, m_aggressiveRangeModifier) },
  150. { "ForceSkirmishAI", INI::parseBool,NULL, offsetof( TAiData, m_forceSkirmishAI ) },
  151. { "RotateSkirmishBases", INI::parseBool,NULL, offsetof( TAiData, m_rotateSkirmishBases ) },
  152. { "AttackUsesLineOfSight", INI::parseBool,NULL, offsetof( TAiData, m_attackUsesLineOfSight ) },
  153. { "AttackIgnoreInsignificantBuildings", INI::parseBool,NULL, offsetof( TAiData, m_attackIgnoreInsignificantBuildings ) },
  154. { "AttackPriorityDistanceModifier", INI::parseReal,NULL, offsetof( TAiData, m_attackPriorityDistanceModifier) },
  155. { "MaxRecruitRadius", INI::parseReal,NULL, offsetof( TAiData, m_maxRecruitDistance ) },
  156. { "WallHeight", INI::parseReal,NULL, offsetof( TAiData, m_wallHeight ) },
  157. { "SideInfo", AI::parseSideInfo, NULL, NULL },
  158. { "SkirmishBuildList", AI::parseSkirmishBuildList, NULL, NULL },
  159. { "MinInfantryForGroup", INI::parseInt,NULL, offsetof( TAiData, m_minInfantryForGroup ) },
  160. { "MinVehiclesForGroup", INI::parseInt,NULL, offsetof( TAiData, m_minVehiclesForGroup ) },
  161. { "MinDistanceForGroup", INI::parseReal,NULL, offsetof( TAiData, m_minDistanceForGroup ) },
  162. { "DistanceRequiresGroup", INI::parseReal,NULL, offsetof( TAiData, m_distanceRequiresGroup ) },
  163. { "MinClumpDensity", INI::parseReal,NULL, offsetof( TAiData, m_minClumpDensity ) },
  164. { "InfantryPathfindDiameter", INI::parseInt,NULL, offsetof( TAiData, m_infantryPathfindDiameter ) },
  165. { "VehiclePathfindDiameter", INI::parseInt,NULL, offsetof( TAiData, m_vehiclePathfindDiameter ) },
  166. { "RebuildDelayTimeSeconds", INI::parseInt,NULL, offsetof( TAiData, m_rebuildDelaySeconds ) },
  167. { "SupplyCenterSafeRadius", INI::parseReal,NULL, offsetof( TAiData, m_supplyCenterSafeRadius ) },
  168. { "AIDozerBoredRadiusModifier", INI::parseReal,NULL, offsetof( TAiData, m_aiDozerBoredRadiusModifier ) },
  169. { "AICrushesInfantry", INI::parseBool,NULL, offsetof( TAiData, m_aiCrushesInfantry ) },
  170. { NULL, NULL, NULL, 0 } // keep this last
  171. };
  172. void AI::parseSideInfo(INI *ini, void *instance, void* /*store*/, const void* /*userData*/)
  173. {
  174. const char* c = ini->getNextToken();
  175. AsciiString side(c);
  176. static const FieldParse myFieldParse[] =
  177. {
  178. { "ResourceGatherersEasy", INI::parseInt, NULL, offsetof( AISideInfo, m_easy ) },
  179. { "ResourceGatherersNormal", INI::parseInt, NULL, offsetof( AISideInfo, m_normal ) },
  180. { "ResourceGatherersHard", INI::parseInt, NULL, offsetof( AISideInfo, m_hard ) },
  181. { "BaseDefenseStructure1", INI::parseAsciiString, NULL, offsetof( AISideInfo, m_baseDefenseStructure1 ) },
  182. { "SkillSet1", AI::parseSkillSet, NULL, offsetof( AISideInfo, m_skillSet1 ) },
  183. { "SkillSet2", AI::parseSkillSet, NULL, offsetof( AISideInfo, m_skillSet2 ) },
  184. { "SkillSet3", AI::parseSkillSet, NULL, offsetof( AISideInfo, m_skillSet3 ) },
  185. { "SkillSet4", AI::parseSkillSet, NULL, offsetof( AISideInfo, m_skillSet4 ) },
  186. { "SkillSet5", AI::parseSkillSet, NULL, offsetof( AISideInfo, m_skillSet5 ) },
  187. { NULL, NULL, NULL, 0 } // keep this last
  188. };
  189. AISideInfo *resourceInfo = ((TAiData*)instance)->m_sideInfo;
  190. while (resourceInfo) {
  191. if (side == resourceInfo->m_side) {
  192. break;
  193. }
  194. resourceInfo = resourceInfo->m_next;
  195. }
  196. if (resourceInfo==NULL)
  197. {
  198. resourceInfo = newInstance(AISideInfo);
  199. ((TAiData*)instance)->addSideInfo(resourceInfo);
  200. }
  201. resourceInfo->m_side = side;
  202. ini->initFromINI(resourceInfo, myFieldParse);
  203. }
  204. void AI::parseSkillSet(INI *ini, void *instance, void* store, const void* /*userData*/)
  205. {
  206. static const FieldParse myFieldParse[] =
  207. {
  208. { "Science", AI::parseScience, NULL, NULL },
  209. { NULL, NULL, NULL, 0 } // keep this last
  210. };
  211. TSkillSet *skillset = ((TSkillSet*)store);
  212. skillset->m_numSkills = 0;
  213. ini->initFromINI(store, myFieldParse);
  214. }
  215. void AI::parseScience(INI *ini, void *instance, void* /*store*/, const void* /*userData*/)
  216. {
  217. TSkillSet *skillset = ((TSkillSet*)instance);
  218. if (skillset->m_numSkills>=MAX_AI_UPGRADES) {
  219. #ifdef DEBUG_CRASHING
  220. const char* c = ini->getNextToken();
  221. DEBUG_CRASH(("Too many SCIENCE skills in skillset. Skill = %s, max is %d", c, MAX_AI_UPGRADES));
  222. #endif
  223. return;
  224. }
  225. skillset->m_skills[skillset->m_numSkills] = SCIENCE_INVALID;
  226. INI::parseScience(ini, instance, skillset->m_skills+skillset->m_numSkills, NULL);
  227. ScienceType science = skillset->m_skills[skillset->m_numSkills];
  228. if (science != SCIENCE_INVALID) {
  229. if (TheScienceStore->getSciencePurchaseCost(science)==0) {
  230. DEBUG_CRASH(("Science %s is not purchaseable, can't be bought.",
  231. TheScienceStore->getInternalNameForScience(science).str()));
  232. return;
  233. }
  234. skillset->m_numSkills++;
  235. }
  236. }
  237. void AI::parseSkirmishBuildList(INI *ini, void *instance, void* /*store*/, const void* /*userData*/)
  238. {
  239. const char* c = ini->getNextToken();
  240. AsciiString faction(c);
  241. static const FieldParse myFieldParse[] =
  242. {
  243. { "Structure", BuildListInfo::parseStructure, NULL, NULL },
  244. { NULL, NULL, NULL, 0 } // keep this last
  245. };
  246. AISideBuildList *build = newInstance(AISideBuildList)(faction);
  247. ini->initFromINI(build, myFieldParse);
  248. ((TAiData*)instance)->addFactionBuildList(build);
  249. }
  250. //--------------------------------------------------------------------------------------------------------
  251. /// The AI system singleton
  252. AI *TheAI = NULL;
  253. /**
  254. * Constructor for the AI system
  255. */
  256. AI::AI( void )
  257. {
  258. m_aiData = NEW TAiData;
  259. m_pathfinder = NEW Pathfinder;
  260. m_nextFormationID = NO_FORMATION_ID;
  261. }
  262. /**
  263. * Initialize the AI system
  264. */
  265. void AI::init( void )
  266. {
  267. m_nextGroupID = 0;
  268. }
  269. /**
  270. * Reset the AI system in preparation for a new map
  271. */
  272. void AI::reset( void )
  273. {
  274. m_pathfinder->reset();
  275. while (m_aiData && m_aiData->m_next) {
  276. TAiData *cur = m_aiData;
  277. m_aiData = m_aiData->m_next;
  278. delete cur;
  279. }
  280. while (m_groupList.size())
  281. {
  282. AIGroup *groupToRemove = m_groupList.front();
  283. if (groupToRemove)
  284. {
  285. destroyGroup(groupToRemove);
  286. }
  287. else
  288. {
  289. m_groupList.pop_front(); // NULL group, just kill from list. Shouldn't really happen, but just in case.
  290. }
  291. }
  292. m_nextGroupID = 0;
  293. m_nextFormationID = NO_FORMATION_ID;
  294. getNextFormationID(); // increment once past NO_FORMATION_ID. jba.
  295. }
  296. /**
  297. * Update the AI system
  298. */
  299. void AI::update( void )
  300. {
  301. // Do pathfinding.
  302. m_pathfinder->processPathfindQueue();
  303. // run player updates
  304. {
  305. ThePlayerList->UPDATE();
  306. }
  307. }
  308. /**
  309. * Destroy the AI system
  310. */
  311. AI::~AI()
  312. {
  313. if (m_pathfinder) {
  314. delete m_pathfinder;
  315. }
  316. m_pathfinder = NULL;
  317. while (m_aiData)
  318. {
  319. TAiData *cur = m_aiData;
  320. m_aiData = m_aiData->m_next;
  321. delete cur;
  322. }
  323. }
  324. void AI::newOverride(void)
  325. {
  326. TAiData *cur = m_aiData;
  327. m_aiData = NEW TAiData;
  328. *m_aiData = *cur;
  329. m_aiData->m_sideInfo = NULL;
  330. AISideInfo *info = cur->m_sideInfo;
  331. while (info) {
  332. AISideInfo *newInfo = newInstance(AISideInfo);
  333. *newInfo = *info;
  334. newInfo->m_next = NULL;
  335. addSideInfo(newInfo);
  336. info = info->m_next;
  337. }
  338. m_aiData->m_sideBuildLists = NULL;
  339. AISideBuildList *build = cur->m_sideBuildLists;
  340. while (build) {
  341. AISideBuildList *newbuild = newInstance(AISideBuildList)(build->m_side);
  342. newbuild->m_next = NULL;
  343. newbuild->m_buildList = build->m_buildList->duplicate();
  344. m_aiData->addFactionBuildList(newbuild);
  345. build = build->m_next;
  346. }
  347. m_aiData->m_next = cur;
  348. }
  349. void AI::addSideInfo(AISideInfo *infoToAdd)
  350. {
  351. m_aiData->addSideInfo(infoToAdd);
  352. }
  353. //-------------------------------------------------------------------------------------------------
  354. /** Parse GameData entry */
  355. //-------------------------------------------------------------------------------------------------
  356. void AI::parseAiDataDefinition( INI* ini )
  357. {
  358. if( TheAI )
  359. {
  360. //
  361. // if the type of loading we're doing creates override data, we need to
  362. // be loading into a new override item
  363. //
  364. if( ini->getLoadType() == INI_LOAD_CREATE_OVERRIDES )
  365. TheAI->newOverride();
  366. } // end if
  367. // parse the ini weapon definition
  368. ini->initFromINI( TheAI->m_aiData, TheAIFieldParseTable );
  369. }
  370. //--------------------------------------------------------------------------------------------------------
  371. /**
  372. * Create a new AI Group
  373. */
  374. AIGroup *AI::createGroup( void )
  375. {
  376. // create a new instance
  377. AIGroup *group = newInstance(AIGroup);
  378. // add it to the list
  379. // DEBUG_LOG(("***AIGROUP %x is being added to m_groupList.\n", group ));
  380. m_groupList.push_back( group );
  381. return group;
  382. }
  383. /**
  384. * Destroy the given AI Group
  385. */
  386. void AI::destroyGroup( AIGroup *group )
  387. {
  388. std::list<AIGroup *>::iterator i = std::find( m_groupList.begin(), m_groupList.end(), group );
  389. // make sure group is actually in the list
  390. if (i == m_groupList.end())
  391. return;
  392. DEBUG_ASSERTCRASH(group != NULL, ("A NULL group made its way into the AIGroup list.. jkmcd"));
  393. // remove it
  394. // DEBUG_LOG(("***AIGROUP %x is being removed from m_groupList.\n", group ));
  395. m_groupList.erase( i );
  396. // destroy group
  397. group->deleteInstance();
  398. }
  399. /**
  400. * Given an ID, return the associated AIGroup
  401. */
  402. AIGroup *AI::findGroup( UnsignedInt id )
  403. {
  404. /// @todo Optimize this (MSB)
  405. std::list<AIGroup *>::iterator i;
  406. for( i=m_groupList.begin(); i!=m_groupList.end(); ++i )
  407. if ((*i)->getID() == id)
  408. return (*i);
  409. return NULL;
  410. }
  411. //--------------------------------------------------------------------------------------------------------
  412. /**
  413. * Get the next formation id.
  414. */
  415. FormationID AI::getNextFormationID(void )
  416. {
  417. FormationID nextVal = m_nextFormationID;
  418. m_nextFormationID = (FormationID) (nextVal+1);
  419. return nextVal;
  420. }
  421. //-----------------------------------------------------------------------------
  422. class PartitionFilterLiveMapEnemies : public PartitionFilter
  423. {
  424. private:
  425. const Object *m_obj;
  426. public:
  427. PartitionFilterLiveMapEnemies(const Object *obj) : m_obj(obj) { }
  428. virtual Bool allow(Object *objOther)
  429. {
  430. // this is way fast (bit test) so do it first.
  431. if (objOther->isEffectivelyDead())
  432. return false;
  433. // this is also way fast (bit test) so do it next.
  434. if (objOther->isOffMap() != m_obj->isOffMap())
  435. return false;
  436. Relationship r = m_obj->getRelationship(objOther);
  437. if (r != ENEMIES)
  438. return false;
  439. return true;
  440. }
  441. #if defined(_DEBUG) || defined(_INTERNAL)
  442. virtual const char* debugGetName() { return "PartitionFilterLiveMapEnemies"; }
  443. #endif
  444. };
  445. //-----------------------------------------------------------------------------
  446. class PartitionFilterWithinAttackRange : public PartitionFilter
  447. {
  448. private:
  449. const Object* m_obj;
  450. public:
  451. PartitionFilterWithinAttackRange(const Object* obj) : m_obj(obj) { }
  452. virtual Bool allow(Object* objOther)
  453. {
  454. for (Int i = 0; i < WEAPONSLOT_COUNT; i++ )
  455. {
  456. // ignore empty slots.
  457. const Weapon* w = m_obj->getWeaponInWeaponSlot((WeaponSlotType)i);
  458. if (w == NULL)
  459. continue;
  460. if (w->isWithinAttackRange(m_obj, objOther))
  461. {
  462. return true;
  463. }
  464. }
  465. return false;
  466. }
  467. #if defined(_DEBUG) || defined(_INTERNAL)
  468. virtual const char* debugGetName() { return "PartitionFilterWithinAttackRange"; }
  469. #endif
  470. };
  471. typedef struct
  472. {
  473. Int priority;
  474. const AttackPriorityInfo *info;
  475. } TPriorityInfo;
  476. static void priorityFunc(Object *obj, void *userData)
  477. {
  478. TPriorityInfo *dp;
  479. dp = (TPriorityInfo*)userData;
  480. Int curPriority = dp->info->getPriority(obj->getTemplate());
  481. if (curPriority>dp->priority) {
  482. dp->priority = curPriority;
  483. }
  484. }
  485. //-----------------------------------------------------------------------------
  486. /**
  487. * Return the closest enemy, according to the qualifiers.
  488. */
  489. Object *AI::findClosestEnemy( const Object *me, Real range, UnsignedInt qualifiers,
  490. const AttackPriorityInfo *info, PartitionFilter *optionalFilter)
  491. {
  492. if ((qualifiers & CAN_ATTACK) && !me->isAbleToAttack())
  493. {
  494. /*
  495. PartitionFilterPossibleToAttack would filter out everything anyway,
  496. so just punt here.
  497. */
  498. return NULL;
  499. }
  500. // only consider live, on-map enemies.
  501. // since this gets called a ton, I made a special custom filter to
  502. // combine several canned ones, in the name of speed (srj)
  503. PartitionFilterLiveMapEnemies filterObvious(me);
  504. PartitionFilterWithinAttackRange filterWithinAttackRange(me);
  505. // never target buildings (unless they can attack)
  506. PartitionFilterRejectBuildings filterBldgs(me);
  507. // and only stuff that isn't stealthed (and not detected)
  508. // (note that stealthed allies aren't hidden from us, but we're only looking for enemies here)
  509. // *** This doesn't cut it anymore. Bombtrucks can be disguised as an enemy member meaning we
  510. // still want to acquire it -- however this old filter fails because the unit is stealthed.
  511. //PartitionFilterRejectByObjectStatus filterStealth(OBJECT_STATUS_STEALTHED, OBJECT_STATUS_DETECTED);
  512. // *** Use this new filter
  513. PartitionFilterStealthedAndUndetected filterStealth( me, false );
  514. // (optional) only stuff we can see.
  515. PartitionFilterLineOfSight filterLOS(me);
  516. // (optional) only stuff we can attack
  517. PartitionFilterPossibleToAttack filterAttack(ATTACK_NEW_TARGET, me, CMD_FROM_AI);
  518. // (optional) only stuff that is significant
  519. PartitionFilterInsignificantBuildings filterInsignificant(true, false);
  520. // (optional) only stuff clear of fog
  521. PartitionFilterFreeOfFog filterFogged(me->getControllingPlayer()->getPlayerIndex());
  522. PartitionFilter *filters[16];
  523. Int numFilters = 0;
  524. // Important note: the filters are called in order, and once one rejects an object,
  525. // the remaining ones are not called, so you should endeavor to
  526. // arrange them such that the most-coarse (ie, the ones that will usually REJECT
  527. // the most objects) should come first.
  528. //
  529. // srj sez: I actually did profiling on USA04 (enabling FILTER_PROFILING in partition mgr)
  530. // to determine the order of these. some observations:
  531. //
  532. // -- filterTeam is BY FAR the best to put first (since most things near you tend to be nonenemies).
  533. // -- filterLOS tends to reject a very large number, but is computationally expensive
  534. // -- filterStealth is BY FAR the least common to be useful, so it goes last.
  535. // GS Fog check used to be inside can attack, so it feels right to be right after it
  536. filters[numFilters++] = &filterObvious;
  537. if( !(qualifiers & ATTACK_BUILDINGS) )
  538. filters[numFilters++] = &filterBldgs;
  539. if (qualifiers & WITHIN_ATTACK_RANGE)
  540. filters[numFilters++] = &filterWithinAttackRange;
  541. if (qualifiers & CAN_SEE)
  542. filters[numFilters++] = &filterLOS;
  543. if (qualifiers & CAN_ATTACK)
  544. filters[numFilters++] = &filterAttack;
  545. if (qualifiers & UNFOGGED)
  546. filters[numFilters++] = &filterFogged;
  547. if (qualifiers & IGNORE_INSIGNIFICANT_BUILDINGS)
  548. filters[numFilters++] = &filterInsignificant;
  549. filters[numFilters++] = &filterStealth;
  550. if (optionalFilter)
  551. {
  552. filters[numFilters++] = optionalFilter;
  553. }
  554. filters[numFilters] = NULL;
  555. if (info == NULL || info == TheScriptEngine->getDefaultAttackInfo())
  556. {
  557. // No additional attack info, so just return the closest one.
  558. Object* o = ThePartitionManager->getClosestObject( me, range, FROM_BOUNDINGSPHERE_2D, filters );
  559. return o;
  560. }
  561. Object *bestEnemy = NULL;
  562. Int effectivePriority=0;
  563. Int actualPriority=0;
  564. ObjectIterator *iter = ThePartitionManager->iterateObjectsInRange(me, range, FROM_BOUNDINGSPHERE_2D, filters, ITER_SORTED_NEAR_TO_FAR);
  565. MemoryPoolObjectHolder holder(iter);
  566. for (Object *theEnemy = iter->first(); theEnemy; theEnemy = iter->next())
  567. {
  568. Int curPriority = info->getPriority(theEnemy->getTemplate());
  569. if (curPriority == 0)
  570. continue; // don't attack 0 priority targets.
  571. /* check for garrisoned buildings/vehicles & see if a higher priority unit is inside. */
  572. ContainModuleInterface* contain = theEnemy->getContain();
  573. if (contain) {
  574. TPriorityInfo priorityInfo;
  575. priorityInfo.priority = curPriority;
  576. priorityInfo.info = info;
  577. contain->iterateContained( priorityFunc, &priorityInfo, false ) ;
  578. if (priorityInfo.priority > curPriority) {
  579. curPriority = priorityInfo.priority;
  580. }
  581. }
  582. Real distSqr = ThePartitionManager->getDistanceSquared(me, theEnemy, FROM_BOUNDINGSPHERE_2D);
  583. Real dist = sqrt(distSqr);
  584. Int modifier = dist/TheAI->getAiData()->m_attackPriorityDistanceModifier;
  585. Int modPriority = curPriority-modifier;
  586. if (modPriority < 1)
  587. modPriority = 1;
  588. if (modPriority > effectivePriority)
  589. {
  590. effectivePriority = modPriority;
  591. actualPriority = curPriority;
  592. bestEnemy = theEnemy;
  593. }
  594. if (modPriority == effectivePriority && curPriority > actualPriority)
  595. {
  596. effectivePriority = modPriority;
  597. actualPriority = curPriority;
  598. bestEnemy = theEnemy;
  599. }
  600. }
  601. if (bestEnemy) {
  602. //DEBUG_LOG(("Find closest found %s, hunter %s, info %s\n", bestEnemy->getTemplate()->getName().str(),
  603. // me->getTemplate()->getName().str(), info->getName().str()));
  604. }
  605. return bestEnemy;
  606. }
  607. /////////////////////////////
  608. /**
  609. * Return the closest ally, according to the qualifiers.
  610. */
  611. Object *AI::findClosestAlly( const Object *me, Real range, UnsignedInt qualifiers)
  612. {
  613. // never target buildings (unless they can attack)
  614. PartitionFilterRejectBuildings filterBldgs(me);
  615. // only consider allies.
  616. PartitionFilterRelationship filterTeam(me, PartitionFilterRelationship::ALLOW_ALLIES);
  617. // and only stuff that is not dead
  618. PartitionFilterAlive filterAlive;
  619. // and on map (or not)
  620. PartitionFilterSameMapStatus filterMapStatus(me);
  621. // (optional) only stuff we can see.
  622. PartitionFilterLineOfSight filterLOS(me);
  623. PartitionFilter *filters[16];
  624. Int numFilters = 0;
  625. filters[numFilters++] = &filterBldgs;
  626. filters[numFilters++] = &filterTeam;
  627. filters[numFilters++] = &filterAlive;
  628. filters[numFilters++] = &filterMapStatus;
  629. if (qualifiers & CAN_SEE)
  630. filters[numFilters++] = &filterLOS;
  631. filters[numFilters] = NULL;
  632. return ThePartitionManager->getClosestObject( me, range, FROM_BOUNDINGSPHERE_2D, filters );
  633. }
  634. /////////////////////////////
  635. /////////////////////////////
  636. /**
  637. * Return the closest repulsor.
  638. */
  639. Object *AI::findClosestRepulsor( const Object *me, Real range)
  640. {
  641. if (!getAiData()->m_enableRepulsors) {
  642. return NULL;
  643. }
  644. // never target buildings (unless they can attack)
  645. PartitionFilterRepulsor filter(me);
  646. // and only stuff that isn't stealthed (and not detected)
  647. // (note that stealthed allies aren't hidden from us, but that's ok. jba.)
  648. PartitionFilterRejectByObjectStatus filterStealth(OBJECT_STATUS_STEALTHED, OBJECT_STATUS_DETECTED);
  649. PartitionFilter *filters[16];
  650. Int numFilters = 0;
  651. filters[numFilters++] = &filter;
  652. filters[numFilters++] = &filterStealth;
  653. filters[numFilters] = NULL;
  654. return ThePartitionManager->getClosestObject( me, range, FROM_BOUNDINGSPHERE_2D, filters );
  655. }
  656. /////////////////////////////
  657. Real AI::getAdjustedVisionRangeForObject(const Object *object, Int factorsToConsider)
  658. {
  659. Real originalRange = object->getVisionRange();
  660. const AIUpdateInterface *ai = object->getAI();
  661. const TAiData *aiData = TheAI->getAiData();
  662. if (!ai)
  663. {
  664. DEBUG_CRASH(("Unit without AI ('%s') calling AI::getAdjustedVisionRangeForObject. Notify jkmcd.", object->getTemplate()->getName().str()));
  665. return 0.0f;
  666. }
  667. UnsignedInt moodMatrixVal = ai->getMoodMatrixValue();
  668. if (factorsToConsider & AI_VISIONFACTOR_OWNERTYPE)
  669. {
  670. Bool playerIsHuman = (moodMatrixVal & MM_Controller_Player) != 0;
  671. if (playerIsHuman)
  672. {
  673. if (factorsToConsider & AI_VISIONFACTOR_GUARDINNER)
  674. originalRange *= aiData->m_guardInnerModifierHuman;
  675. else
  676. originalRange *= aiData->m_guardOuterModifierHuman;
  677. }
  678. else
  679. {
  680. if (factorsToConsider & AI_VISIONFACTOR_GUARDINNER)
  681. originalRange *= aiData->m_guardInnerModifierAI;
  682. else
  683. originalRange *= aiData->m_guardOuterModifierAI;
  684. }
  685. }
  686. if (object->getContainedBy() != NULL)
  687. {
  688. originalRange = object->getLargestWeaponRange();
  689. }
  690. else
  691. {
  692. if ((factorsToConsider & AI_VISIONFACTOR_MOOD) && ((moodMatrixVal & MM_Controller_Player) == 0) )
  693. {
  694. switch(moodMatrixVal & MM_Mood_Bitmask)
  695. {
  696. case MM_Mood_Sleep:
  697. return 0.0f;
  698. case MM_Mood_Passive:
  699. case MM_Mood_Normal:
  700. break;
  701. case MM_Mood_Alert:
  702. originalRange *= TheAI->getAiData()->m_alertRangeModifier;
  703. break;
  704. case MM_Mood_Aggressive:
  705. originalRange *= TheAI->getAiData()->m_aggressiveRangeModifier;
  706. break;
  707. }
  708. }
  709. }
  710. #if defined(_DEBUG) || defined(_INTERNAL)
  711. if (TheGlobalData->m_debugVisibility)
  712. {
  713. // ICK. This really nasty statement is used so that we only initialize this color once.
  714. // It should be exactly double the intensity of its targettable brother.
  715. static RGBColor theAdjustedVisionColor = {
  716. (TheGlobalData->m_debugVisibilityTargettableColor.red * 2 <= 1.0f ?
  717. TheGlobalData->m_debugVisibilityTargettableColor.red * 2 :
  718. 1.0f),
  719. (TheGlobalData->m_debugVisibilityTargettableColor.green * 2 <= 1.0f ?
  720. TheGlobalData->m_debugVisibilityTargettableColor.green * 2 :
  721. 1.0f),
  722. (TheGlobalData->m_debugVisibilityTargettableColor.blue * 2 <= 1.0f ?
  723. TheGlobalData->m_debugVisibilityTargettableColor.blue * 2 :
  724. 1.0f)
  725. };
  726. Vector3 pos(originalRange, 0, 0);
  727. for (int i = 0; i < TheGlobalData->m_debugVisibilityTileCount; ++i)
  728. {
  729. pos.Rotate_Z(1.0f * i / TheGlobalData->m_debugVisibilityTileCount * 2 * PI);
  730. Coord3D coord = { pos.X + object->getPosition()->x, pos.Y + object->getPosition()->y, pos.Z + object->getPosition()->z };
  731. addIcon(&coord, TheGlobalData->m_debugVisibilityTileWidth,
  732. TheGlobalData->m_debugVisibilityTileDuration,
  733. theAdjustedVisionColor);
  734. }
  735. }
  736. #endif
  737. return originalRange;
  738. }
  739. //-------------------------------------------------------------------------------------------------
  740. TAiData::TAiData() :
  741. m_next(NULL),
  742. m_sideInfo(NULL),
  743. m_attackIgnoreInsignificantBuildings(false),
  744. m_skirmishGroupFudgeValue(0.0f),
  745. m_structureSeconds(0),
  746. m_teamSeconds(0),
  747. m_resourcesWealthy(0),
  748. m_resourcesPoor(0),
  749. m_forceIdleFramesCount(1),
  750. m_structuresWealthyMod(0),
  751. m_teamPoorMod(0),
  752. m_teamResourcesToBuild(0),
  753. m_guardInnerModifierAI(0),
  754. m_guardOuterModifierAI(0),
  755. m_guardInnerModifierHuman(0),
  756. m_guardOuterModifierHuman(0),
  757. m_guardChaseUnitFrames(0),
  758. m_guardEnemyScanRate(LOGICFRAMES_PER_SECOND/2),
  759. m_guardEnemyReturnScanRate(LOGICFRAMES_PER_SECOND),
  760. m_wallHeight(0),
  761. m_alertRangeModifier(0),
  762. m_aggressiveRangeModifier(0),
  763. m_attackPriorityDistanceModifier(0),
  764. m_maxRecruitDistance(0),
  765. m_repulsedDistance(0),
  766. m_enableRepulsors(false),
  767. m_forceSkirmishAI(false),
  768. m_rotateSkirmishBases(false),
  769. m_attackUsesLineOfSight(true),
  770. m_minInfantryForGroup(3),
  771. m_minVehiclesForGroup(4),
  772. m_minDistanceForGroup(100),
  773. m_minClumpDensity(0.5f),
  774. m_infantryPathfindDiameter(6),
  775. m_vehiclePathfindDiameter(6),
  776. m_supplyCenterSafeRadius(250),
  777. m_rebuildDelaySeconds(10),
  778. //Added By Sadullah Nader
  779. //Initialization(s) inserted
  780. m_distanceRequiresGroup(0.0f),
  781. m_sideBuildLists(NULL),
  782. m_structuresPoorMod(0.0f),
  783. m_teamWealthyMod(0.0f),
  784. m_aiDozerBoredRadiusModifier(2.0),
  785. m_aiCrushesInfantry(true)
  786. //
  787. {
  788. }
  789. //-------------------------------------------------------------------------------------------------
  790. void TAiData::crc( Xfer *xfer )
  791. {
  792. xfer->xferReal( &m_structureSeconds );
  793. xfer->xferReal( &m_teamSeconds );
  794. xfer->xferInt( &m_resourcesWealthy );
  795. xfer->xferInt( &m_resourcesPoor );
  796. xfer->xferUnsignedInt( &m_forceIdleFramesCount );
  797. xfer->xferReal( &m_structuresWealthyMod );
  798. xfer->xferReal( &m_teamWealthyMod );
  799. xfer->xferReal( &m_structuresPoorMod );
  800. xfer->xferReal( &m_teamPoorMod );
  801. xfer->xferReal( &m_teamResourcesToBuild );
  802. xfer->xferReal( &m_guardInnerModifierAI );
  803. xfer->xferReal( &m_guardOuterModifierAI );
  804. xfer->xferReal( &m_guardInnerModifierHuman );
  805. xfer->xferReal( &m_guardOuterModifierHuman );
  806. xfer->xferUnsignedInt( &m_guardChaseUnitFrames );
  807. xfer->xferUnsignedInt( &m_guardEnemyScanRate );
  808. xfer->xferUnsignedInt( &m_guardEnemyReturnScanRate );
  809. xfer->xferReal( &m_alertRangeModifier );
  810. xfer->xferReal( &m_aggressiveRangeModifier );
  811. xfer->xferReal( &m_attackPriorityDistanceModifier );
  812. xfer->xferReal( &m_maxRecruitDistance );
  813. xfer->xferReal( &m_repulsedDistance );
  814. xfer->xferBool( &m_enableRepulsors );
  815. CRCGEN_LOG(("CRC after AI TAiData for frame %d is 0x%8.8X\n", TheGameLogic->getFrame(), ((XferCRC *)xfer)->getCRC()));
  816. } // end crc
  817. //-----------------------------------------------------------------------------
  818. void TAiData::xfer( Xfer *xfer )
  819. {
  820. // version
  821. XferVersion currentVersion = 1;
  822. XferVersion version = currentVersion;
  823. xfer->xferVersion( &version, currentVersion );
  824. } // end xfer
  825. //-----------------------------------------------------------------------------
  826. void TAiData::loadPostProcess( void )
  827. {
  828. } // end loadPostProcess
  829. //-----------------------------------------------------------------------------
  830. void AI::crc( Xfer *xfer )
  831. {
  832. xfer->xferSnapshot( m_pathfinder );
  833. CRCGEN_LOG(("CRC after AI pathfinder for frame %d is 0x%8.8X\n", TheGameLogic->getFrame(), ((XferCRC *)xfer)->getCRC()));
  834. AsciiString marker;
  835. TAiData *aiData = m_aiData;
  836. while (aiData)
  837. {
  838. marker = "MARKER:TAiData";
  839. xfer->xferAsciiString(&marker);
  840. xfer->xferSnapshot( aiData );
  841. aiData = aiData->m_next;
  842. }
  843. for (std::list<AIGroup *>::iterator groupIt = m_groupList.begin(); groupIt != m_groupList.end(); ++groupIt)
  844. {
  845. if (*groupIt)
  846. {
  847. marker = "MARKER:AIGroup";
  848. xfer->xferAsciiString(&marker);
  849. xfer->xferSnapshot( (*groupIt) );
  850. }
  851. }
  852. } // end crc
  853. //-----------------------------------------------------------------------------
  854. void AI::xfer( Xfer *xfer )
  855. {
  856. // version
  857. XferVersion currentVersion = 1;
  858. XferVersion version = currentVersion;
  859. xfer->xferVersion( &version, currentVersion );
  860. } // end xfer
  861. //-----------------------------------------------------------------------------
  862. void AI::loadPostProcess( void )
  863. {
  864. } // end loadPostProcess