AI.cpp 32 KB

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