1 // ==============================================================
2 // This file is part of Glest (www.glest.org)
3 //
4 // Copyright (C) 2001-2008 Martiño Figueroa
5 //
6 // You can redistribute this code and/or modify it under
7 // the terms of the GNU General Public License as published
8 // by the Free Software Foundation; either version 2 of the
9 // License, or (at your option) any later version
10 // ==============================================================
11
12 #include "ai.h"
13 #include "ai_interface.h"
14 #include "ai_rule.h"
15 #include "unit_type.h"
16 #include "unit.h"
17 #include "map.h"
18 #include "faction_type.h"
19 #include "leak_dumper.h"
20
21 using namespace Shared::Graphics;
22 using namespace Shared::Util;
23
24 namespace Glest { namespace Game {
25
Task()26 Task::Task() {
27 taskClass = tcProduce;
28 }
29
30 //void Task::saveGame(XmlNode *rootNode) const {
31 // std::map<string,string> mapTagReplacements;
32 // XmlNode *taskNode = rootNode->addChild("Task");
33 //}
34
35 // =====================================================
36 // class ProduceTask
37 // =====================================================
ProduceTask()38 ProduceTask::ProduceTask() : Task() {
39 taskClass= tcProduce;
40 unitType= NULL;
41 resourceType= NULL;
42 unitClass = ucWarrior;
43 }
44
ProduceTask(UnitClass unitClass)45 ProduceTask::ProduceTask(UnitClass unitClass) : Task() {
46 taskClass= tcProduce;
47 this->unitClass= unitClass;
48 unitType= NULL;
49 resourceType= NULL;
50 }
51
ProduceTask(const UnitType * unitType)52 ProduceTask::ProduceTask(const UnitType *unitType) : Task() {
53 taskClass= tcProduce;
54 this->unitType= unitType;
55 resourceType= NULL;
56 unitClass = ucWarrior;
57 }
58
ProduceTask(const ResourceType * resourceType)59 ProduceTask::ProduceTask(const ResourceType *resourceType) : Task() {
60 taskClass= tcProduce;
61 unitType= NULL;
62 unitClass = ucWarrior;
63 this->resourceType= resourceType;
64 }
65
toString() const66 string ProduceTask::toString() const{
67 string str= "Produce ";
68 if(unitType!=NULL){
69 str+= unitType->getName(false);
70 }
71 return str;
72 }
73
saveGame(XmlNode * rootNode) const74 void ProduceTask::saveGame(XmlNode *rootNode) const {
75 std::map<string,string> mapTagReplacements;
76 XmlNode *taskNode = rootNode->addChild("Task");
77 taskNode->addAttribute("taskClass",intToStr(taskClass), mapTagReplacements);
78 XmlNode *produceTaskNode = taskNode->addChild("ProduceTask");
79
80 // UnitClass unitClass;
81 produceTaskNode->addAttribute("unitClass",intToStr(unitClass), mapTagReplacements);
82 // const UnitType *unitType;
83 if(unitType != NULL) {
84 produceTaskNode->addAttribute("unitType",unitType->getName(false), mapTagReplacements);
85 }
86 // const ResourceType *resourceType;
87 if(resourceType != NULL) {
88 produceTaskNode->addAttribute("resourceType",resourceType->getName(false), mapTagReplacements);
89 }
90 }
91
loadGame(const XmlNode * rootNode,Faction * faction)92 ProduceTask * ProduceTask::loadGame(const XmlNode *rootNode, Faction *faction) {
93 const XmlNode *produceTaskNode = rootNode->getChild("ProduceTask");
94
95 ProduceTask *newTask = new ProduceTask();
96 // UnitClass unitClass;
97 newTask->unitClass = static_cast<UnitClass>(produceTaskNode->getAttribute("unitClass")->getIntValue());
98 // const UnitType *unitType;
99 if(produceTaskNode->hasAttribute("unitType")) {
100 string unitTypeName = produceTaskNode->getAttribute("unitType")->getValue();
101 newTask->unitType = faction->getType()->getUnitType(unitTypeName);
102 }
103 // const ResourceType *resourceType;
104 if(produceTaskNode->hasAttribute("resourceType")) {
105 string resourceTypeName = produceTaskNode->getAttribute("resourceType")->getValue();
106 newTask->resourceType = faction->getTechTree()->getResourceType(resourceTypeName);
107 }
108
109 return newTask;
110 }
111
112 // =====================================================
113 // class BuildTask
114 // =====================================================
BuildTask()115 BuildTask::BuildTask() {
116 taskClass= tcBuild;
117 this->unitType= NULL;
118 resourceType= NULL;
119 forcePos= false;
120 }
121
BuildTask(const UnitType * unitType)122 BuildTask::BuildTask(const UnitType *unitType){
123 taskClass= tcBuild;
124 this->unitType= unitType;
125 resourceType= NULL;
126 forcePos= false;
127 }
128
BuildTask(const ResourceType * resourceType)129 BuildTask::BuildTask(const ResourceType *resourceType){
130 taskClass= tcBuild;
131 unitType= NULL;
132 this->resourceType= resourceType;
133 forcePos= false;
134 }
135
BuildTask(const UnitType * unitType,const Vec2i & pos)136 BuildTask::BuildTask(const UnitType *unitType, const Vec2i &pos){
137 taskClass= tcBuild;
138 this->unitType= unitType;
139 resourceType= NULL;
140 forcePos= true;
141 this->pos= pos;
142 }
143
toString() const144 string BuildTask::toString() const{
145 string str= "Build ";
146 if(unitType!=NULL){
147 str+= unitType->getName(false);
148 }
149 return str;
150 }
151
saveGame(XmlNode * rootNode) const152 void BuildTask::saveGame(XmlNode *rootNode) const {
153 std::map<string,string> mapTagReplacements;
154 XmlNode *taskNode = rootNode->addChild("Task");
155 taskNode->addAttribute("taskClass",intToStr(taskClass), mapTagReplacements);
156 XmlNode *buildTaskNode = taskNode->addChild("BuildTask");
157
158 // const UnitType *unitType;
159 if(unitType != NULL) {
160 buildTaskNode->addAttribute("unitType",unitType->getName(false), mapTagReplacements);
161 }
162 // const ResourceType *resourceType;
163 if(resourceType != NULL) {
164 buildTaskNode->addAttribute("resourceType",resourceType->getName(), mapTagReplacements);
165 }
166 // bool forcePos;
167 buildTaskNode->addAttribute("forcePos",intToStr(forcePos), mapTagReplacements);
168 // Vec2i pos;
169 buildTaskNode->addAttribute("pos",pos.getString(), mapTagReplacements);
170 }
171
loadGame(const XmlNode * rootNode,Faction * faction)172 BuildTask * BuildTask::loadGame(const XmlNode *rootNode, Faction *faction) {
173 const XmlNode *buildTaskNode = rootNode->getChild("BuildTask");
174
175 BuildTask *newTask = new BuildTask();
176 if(buildTaskNode->hasAttribute("unitType")) {
177 string unitTypeName = buildTaskNode->getAttribute("unitType")->getValue();
178 newTask->unitType = faction->getType()->getUnitType(unitTypeName);
179 }
180 if(buildTaskNode->hasAttribute("resourceType")) {
181 string resourceTypeName = buildTaskNode->getAttribute("resourceType")->getValue();
182 newTask->resourceType = faction->getTechTree()->getResourceType(resourceTypeName);
183 }
184
185 newTask->forcePos = buildTaskNode->getAttribute("forcePos")->getIntValue() != 0;
186 newTask->pos = Vec2i::strToVec2(buildTaskNode->getAttribute("pos")->getValue());
187
188 return newTask;
189 }
190
191 // =====================================================
192 // class UpgradeTask
193 // =====================================================
UpgradeTask()194 UpgradeTask::UpgradeTask() {
195 taskClass= tcUpgrade;
196 this->upgradeType= NULL;
197 }
198
UpgradeTask(const UpgradeType * upgradeType)199 UpgradeTask::UpgradeTask(const UpgradeType *upgradeType){
200 taskClass= tcUpgrade;
201 this->upgradeType= upgradeType;
202 }
203
toString() const204 string UpgradeTask::toString() const{
205 string str= "Build ";
206 if(upgradeType!=NULL){
207 str+= upgradeType->getName();
208 }
209 return str;
210 }
211
saveGame(XmlNode * rootNode) const212 void UpgradeTask::saveGame(XmlNode *rootNode) const {
213 std::map<string,string> mapTagReplacements;
214 XmlNode *taskNode = rootNode->addChild("Task");
215 taskNode->addAttribute("taskClass",intToStr(taskClass), mapTagReplacements);
216 XmlNode *upgradeTaskNode = taskNode->addChild("UpgradeTask");
217
218 if(upgradeType != NULL) {
219 //upgradeType->saveGame(upgradeTaskNode);
220 upgradeTaskNode->addAttribute("upgradeType",upgradeType->getName(), mapTagReplacements);
221 }
222 }
223
loadGame(const XmlNode * rootNode,Faction * faction)224 UpgradeTask * UpgradeTask::loadGame(const XmlNode *rootNode, Faction *faction) {
225 const XmlNode *upgradeTaskNode = rootNode->getChild("UpgradeTask");
226
227 UpgradeTask *newTask = new UpgradeTask();
228 if(upgradeTaskNode->hasAttribute("upgradeType")) {
229 string upgradeTypeName = upgradeTaskNode->getAttribute("upgradeType")->getValue();
230 newTask->upgradeType = faction->getType()->getUpgradeType(upgradeTypeName);
231 }
232 return newTask;
233 }
234
235 // =====================================================
236 // class Ai
237 // =====================================================
238
init(AiInterface * aiInterface,int useStartLocation)239 void Ai::init(AiInterface *aiInterface, int useStartLocation) {
240 this->aiInterface= aiInterface;
241
242 Faction *faction = this->aiInterface->getMyFaction();
243 if(faction->getAIBehaviorStaticOverideValue(aibsvcMaxBuildRadius) != INT_MAX) {
244 maxBuildRadius = faction->getAIBehaviorStaticOverideValue(aibsvcMaxBuildRadius);
245 //printf("Discovered overriden static value for AI, maxBuildRadius = %d\n",maxBuildRadius);
246 }
247 if(faction->getAIBehaviorStaticOverideValue(aibsvcMinMinWarriors) != INT_MAX) {
248 minMinWarriors = faction->getAIBehaviorStaticOverideValue(aibsvcMinMinWarriors);
249 //printf("Discovered overriden static value for AI, minMinWarriors = %d\n",minMinWarriors);
250 }
251 if(faction->getAIBehaviorStaticOverideValue(aibsvcMinMinWarriorsExpandCpuEasy) != INT_MAX) {
252 minMinWarriorsExpandCpuEasy = faction->getAIBehaviorStaticOverideValue(aibsvcMinMinWarriorsExpandCpuEasy);
253 //printf("Discovered overriden static value for AI, minMinWarriorsExpandCpuEasy = %d\n",minMinWarriorsExpandCpuEasy);
254 }
255 if(faction->getAIBehaviorStaticOverideValue(aibsvcMinMinWarriorsExpandCpuMega) != INT_MAX) {
256 minMinWarriorsExpandCpuMega = faction->getAIBehaviorStaticOverideValue(aibsvcMinMinWarriorsExpandCpuMega);
257 //printf("Discovered overriden static value for AI, minMinWarriorsExpandCpuMega = %d\n",minMinWarriorsExpandCpuMega);
258 }
259 if(faction->getAIBehaviorStaticOverideValue(aibsvcMinMinWarriorsExpandCpuUltra) != INT_MAX) {
260 minMinWarriorsExpandCpuUltra = faction->getAIBehaviorStaticOverideValue(aibsvcMinMinWarriorsExpandCpuUltra);
261 //printf("Discovered overriden static value for AI, minMinWarriorsExpandCpuUltra = %d\n",minMinWarriorsExpandCpuUltra);
262 }
263 if(faction->getAIBehaviorStaticOverideValue(aibsvcMinMinWarriorsExpandCpuNormal) != INT_MAX) {
264 minMinWarriorsExpandCpuNormal = faction->getAIBehaviorStaticOverideValue(aibsvcMinMinWarriorsExpandCpuNormal);
265 //printf("Discovered overriden static value for AI, minMinWarriorsExpandCpuNormal = %d\n",minMinWarriorsExpandCpuNormal);
266 }
267 if(faction->getAIBehaviorStaticOverideValue(aibsvcMaxMinWarriors) != INT_MAX) {
268 maxMinWarriors = faction->getAIBehaviorStaticOverideValue(aibsvcMaxMinWarriors);
269 //printf("Discovered overriden static value for AI, maxMinWarriors = %d\n",maxMinWarriors);
270 }
271 if(faction->getAIBehaviorStaticOverideValue(aibsvcMaxExpansions) != INT_MAX) {
272 maxExpansions = faction->getAIBehaviorStaticOverideValue(aibsvcMaxExpansions);
273 //printf("Discovered overriden static value for AI, maxExpansions = %d\n",maxExpansions);
274 }
275 if(faction->getAIBehaviorStaticOverideValue(aibsvcVillageRadius) != INT_MAX) {
276 villageRadius = faction->getAIBehaviorStaticOverideValue(aibsvcVillageRadius);
277 //printf("Discovered overriden static value for AI, villageRadius = %d\n",villageRadius);
278 }
279 if(faction->getAIBehaviorStaticOverideValue(aibsvcScoutResourceRange) != INT_MAX) {
280 scoutResourceRange = faction->getAIBehaviorStaticOverideValue(aibsvcScoutResourceRange);
281 //printf("Discovered overriden static value for AI, scoutResourceRange = %d\n",scoutResourceRange);
282 }
283 if(faction->getAIBehaviorStaticOverideValue(aibsvcMinWorkerAttackersHarvesting) != INT_MAX) {
284 minWorkerAttackersHarvesting = faction->getAIBehaviorStaticOverideValue(aibsvcMinWorkerAttackersHarvesting);
285 //printf("Discovered overriden static value for AI, scoutResourceRange = %d\n",scoutResourceRange);
286 }
287 if(faction->getAIBehaviorStaticOverideValue(aibsvcMinBuildSpacing) != INT_MAX) {
288 minBuildSpacing = faction->getAIBehaviorStaticOverideValue(aibsvcMinBuildSpacing);
289 //printf("Discovered overriden static value for AI, scoutResourceRange = %d\n",scoutResourceRange);
290 }
291
292 if(useStartLocation == -1) {
293 startLoc = random.randRange(0, aiInterface->getMapMaxPlayers()-1);
294 }
295 else {
296 startLoc = useStartLocation;
297 }
298 minWarriors= minMinWarriors;
299 randomMinWarriorsReached= false;
300 //add ai rules
301 aiRules.clear();
302 aiRules.push_back(new AiRuleWorkerHarvest(this));
303 aiRules.push_back(new AiRuleRefreshHarvester(this));
304 aiRules.push_back(new AiRuleScoutPatrol(this));
305 aiRules.push_back(new AiRuleUnBlock(this));
306 aiRules.push_back(new AiRuleReturnBase(this));
307 aiRules.push_back(new AiRuleMassiveAttack(this));
308 aiRules.push_back(new AiRuleAddTasks(this));
309 aiRules.push_back(new AiRuleProduceResourceProducer(this));
310 aiRules.push_back(new AiRuleBuildOneFarm(this));
311 aiRules.push_back(new AiRuleProduce(this));
312 aiRules.push_back(new AiRuleBuild(this));
313 aiRules.push_back(new AiRuleUpgrade(this));
314 aiRules.push_back(new AiRuleExpand(this));
315 aiRules.push_back(new AiRuleRepair(this));
316 aiRules.push_back(new AiRuleRepair(this));
317 }
318
~Ai()319 Ai::~Ai() {
320 if(SystemFlags::getSystemSettingType(SystemFlags::debugSystem).enabled) SystemFlags::OutputDebug(SystemFlags::debugSystem,"In [%s::%s Line: %d] deleting AI aiInterface [%p]\n",__FILE__,__FUNCTION__,__LINE__,aiInterface);
321 deleteValues(tasks.begin(), tasks.end());
322 tasks.clear();
323 if(SystemFlags::getSystemSettingType(SystemFlags::debugSystem).enabled) SystemFlags::OutputDebug(SystemFlags::debugSystem,"In [%s::%s Line: %d] deleting AI aiInterface [%p]\n",__FILE__,__FUNCTION__,__LINE__,aiInterface);
324
325 deleteValues(aiRules.begin(), aiRules.end());
326 aiRules.clear();
327 if(SystemFlags::getSystemSettingType(SystemFlags::debugSystem).enabled) SystemFlags::OutputDebug(SystemFlags::debugSystem,"In [%s::%s Line: %d] deleting AI aiInterface [%p]\n",__FILE__,__FUNCTION__,__LINE__,aiInterface);
328
329 aiInterface = NULL;
330 }
331
getRandom()332 RandomGen* Ai::getRandom() {
333 // if(Thread::isCurrentThreadMainThread() == false) {
334 // throw megaglest_runtime_error("Invalid access to AI random from outside main thread current id = " +
335 // intToStr(Thread::getCurrentThreadId()) + " main = " + intToStr(Thread::getMainThreadId()));
336 // }
337 return &random;
338 }
339
update()340 void Ai::update() {
341
342 Chrono chrono;
343 if(SystemFlags::getSystemSettingType(SystemFlags::debugPerformance).enabled) chrono.start();
344
345 if(SystemFlags::getSystemSettingType(SystemFlags::debugPerformance).enabled && chrono.getMillis() > 0) SystemFlags::OutputDebug(SystemFlags::debugPerformance,"In [%s::%s Line: %d] took msecs: %lld [START]\n",__FILE__,__FUNCTION__,__LINE__,chrono.getMillis());
346
347 if(aiInterface->getMyFaction()->getFirstSwitchTeamVote() != NULL) {
348 const SwitchTeamVote *vote = aiInterface->getMyFaction()->getFirstSwitchTeamVote();
349 aiInterface->getMyFaction()->setCurrentSwitchTeamVoteFactionIndex(vote->factionIndex);
350
351 factionSwitchTeamRequestCount[vote->factionIndex]++;
352 int factionSwitchTeamRequestCountCurrent = factionSwitchTeamRequestCount[vote->factionIndex];
353
354 //int allowJoinTeam = random.randRange(0, 100);
355 //srand(time(NULL) + aiInterface->getMyFaction()->getIndex());
356 Chrono seed(true);
357 srand((unsigned int)seed.getCurTicks() + aiInterface->getMyFaction()->getIndex());
358
359 int allowJoinTeam = rand() % 100;
360
361 SwitchTeamVote *voteResult = aiInterface->getMyFaction()->getSwitchTeamVote(vote->factionIndex);
362 voteResult->voted = true;
363 voteResult->allowSwitchTeam = false;
364
365 const GameSettings *settings = aiInterface->getWorld()->getGameSettings();
366
367 // If AI player already lost game they cannot vote
368 if(aiInterface->getWorld()->factionLostGame(aiInterface->getFactionIndex()) == true) {
369 voteResult->allowSwitchTeam = true;
370 }
371 else {
372 // Can only ask the AI player 2 times max per game
373 if(factionSwitchTeamRequestCountCurrent <= 2) {
374 // x% chance the AI will answer yes
375 if(settings->getAiAcceptSwitchTeamPercentChance() >= 100) {
376 voteResult->allowSwitchTeam = true;
377 }
378 else if(settings->getAiAcceptSwitchTeamPercentChance() <= 0) {
379 voteResult->allowSwitchTeam = false;
380 }
381 else {
382 voteResult->allowSwitchTeam = (allowJoinTeam >= (100 - settings->getAiAcceptSwitchTeamPercentChance()));
383 }
384 }
385 }
386
387 char szBuf[8096]="";
388 snprintf(szBuf,8096,"AI for faction# %d voted %s [%d] CountCurrent [%d] PercentChance [%d]",aiInterface->getMyFaction()->getIndex(),(voteResult->allowSwitchTeam ? "Yes" : "No"),allowJoinTeam,factionSwitchTeamRequestCountCurrent,settings->getAiAcceptSwitchTeamPercentChance());
389 if(SystemFlags::getSystemSettingType(SystemFlags::debugSystem).enabled) SystemFlags::OutputDebug(SystemFlags::debugSystem,"In [%s::%s Line: %d] %s\n",__FILE__,__FUNCTION__,__LINE__,szBuf);
390
391 aiInterface->printLog(3, szBuf);
392
393 aiInterface->giveCommandSwitchTeamVote(aiInterface->getMyFaction(),voteResult);
394 }
395
396 //process ai rules
397 for(unsigned int ruleIdx = 0; ruleIdx < aiRules.size(); ++ruleIdx) {
398 AiRule *rule = aiRules[ruleIdx];
399 if(rule == NULL) {
400 throw megaglest_runtime_error("rule == NULL");
401 }
402
403 if(SystemFlags::getSystemSettingType(SystemFlags::debugPerformance).enabled && chrono.getMillis() > 0) SystemFlags::OutputDebug(SystemFlags::debugPerformance,"In [%s::%s Line: %d] took msecs: %lld [ruleIdx = %d]\n",__FILE__,__FUNCTION__,__LINE__,chrono.getMillis(),ruleIdx);
404
405 if((aiInterface->getTimer() % (rule->getTestInterval() * GameConstants::updateFps / 1000)) == 0) {
406
407 if(SystemFlags::getSystemSettingType(SystemFlags::debugPerformance).enabled && chrono.getMillis() > 0) SystemFlags::OutputDebug(SystemFlags::debugPerformance,"In [%s::%s Line: %d] took msecs: %lld [ruleIdx = %d, before rule->test()]\n",__FILE__,__FUNCTION__,__LINE__,chrono.getMillis(),ruleIdx);
408
409 //printf("Testing AI Faction # %d RULE Name[%s]\n",aiInterface->getFactionIndex(),rule->getName().c_str());
410
411 if(rule->test()) {
412 if(outputAIBehaviourToConsole()) printf("\n\nYYYYY Executing AI Faction # %d RULE Name[%s]\n\n",aiInterface->getFactionIndex(),rule->getName().c_str());
413
414 aiInterface->printLog(3, intToStr(1000 * aiInterface->getTimer() / GameConstants::updateFps) + ": Executing rule: " + rule->getName() + '\n');
415
416 if(SystemFlags::getSystemSettingType(SystemFlags::debugPerformance).enabled && chrono.getMillis() > 0) SystemFlags::OutputDebug(SystemFlags::debugPerformance,"In [%s::%s Line: %d] took msecs: %lld [ruleIdx = %d, before rule->execute() [%s]]\n",__FILE__,__FUNCTION__,__LINE__,chrono.getMillis(),ruleIdx,rule->getName().c_str());
417
418 rule->execute();
419
420 if(SystemFlags::getSystemSettingType(SystemFlags::debugPerformance).enabled && chrono.getMillis() > 0) SystemFlags::OutputDebug(SystemFlags::debugPerformance,"In [%s::%s Line: %d] took msecs: %lld [ruleIdx = %d, after rule->execute() [%s]]\n",__FILE__,__FUNCTION__,__LINE__,chrono.getMillis(),ruleIdx,rule->getName().c_str());
421 }
422 }
423 }
424
425 if(SystemFlags::getSystemSettingType(SystemFlags::debugPerformance).enabled && chrono.getMillis() > 0) SystemFlags::OutputDebug(SystemFlags::debugPerformance,"In [%s::%s Line: %d] took msecs: %lld [END]\n",__FILE__,__FUNCTION__,__LINE__,chrono.getMillis());
426 }
427
428
429 // ==================== state requests ====================
430
getCountOfType(const UnitType * ut)431 int Ai::getCountOfType(const UnitType *ut){
432 int count= 0;
433 for(int i=0; i<aiInterface->getMyUnitCount(); ++i){
434 if(ut == aiInterface->getMyUnit(i)->getType()){
435 count++;
436 }
437 }
438 return count;
439 }
440
getCountOfClass(UnitClass uc,UnitClass * additionalUnitClassToExcludeFromCount)441 int Ai::getCountOfClass(UnitClass uc,UnitClass *additionalUnitClassToExcludeFromCount) {
442 int count= 0;
443 for(int i = 0; i < aiInterface->getMyUnitCount(); ++i) {
444 if(aiInterface->getMyUnit(i)->getType()->isOfClass(uc)) {
445 // Skip unit if it ALSO contains the exclusion unit class type
446 if(additionalUnitClassToExcludeFromCount != NULL) {
447 if(aiInterface->getMyUnit(i)->getType()->isOfClass(*additionalUnitClassToExcludeFromCount)) {
448 continue;
449 }
450 }
451 ++count;
452 }
453 }
454 return count;
455 }
456
getRatioOfClass(UnitClass uc,UnitClass * additionalUnitClassToExcludeFromCount)457 float Ai::getRatioOfClass(UnitClass uc,UnitClass *additionalUnitClassToExcludeFromCount) {
458 if(aiInterface->getMyUnitCount() == 0) {
459 return 0;
460 }
461 else {
462 //return static_cast<float>(getCountOfClass(uc,additionalUnitClassToExcludeFromCount)) / aiInterface->getMyUnitCount();
463 return truncateDecimal<float>(static_cast<float>(getCountOfClass(uc,additionalUnitClassToExcludeFromCount)) / aiInterface->getMyUnitCount(),6);
464 }
465 }
466
getNeededResource(int unitIndex)467 const ResourceType *Ai::getNeededResource(int unitIndex) {
468 int amount = INT_MAX;
469 const ResourceType *neededResource= NULL;
470 const TechTree *tt= aiInterface->getTechTree();
471 const Unit *unit = aiInterface->getMyUnit(unitIndex);
472
473 for(int i = 0; i < tt->getResourceTypeCount(); ++i) {
474 const ResourceType *rt= tt->getResourceType(i);
475 const Resource *r= aiInterface->getResource(rt);
476
477 if( rt->getClass() != rcStatic && rt->getClass() != rcConsumable) {
478 char szBuf[8096]="";
479 snprintf(szBuf,8096,"Examining resource [%s] amount [%d] (previous amount [%d]",rt->getName().c_str(),r->getAmount(),amount);
480 aiInterface->printLog(3, szBuf);
481 }
482
483 if( rt->getClass() != rcStatic && rt->getClass() != rcConsumable &&
484 r->getAmount() < amount) {
485
486 // Only have up to x units going for this resource so we can focus
487 // on other needed resources for other units
488 const int maxUnitsToHarvestResource = 5;
489
490 vector<int> unitsGettingResource = findUnitsHarvestingResourceType(rt);
491 if((int)unitsGettingResource.size() <= maxUnitsToHarvestResource) {
492 // Now MAKE SURE the unit has a harvest command for this resource
493 // AND that the resource is within eye-sight to avoid units
494 // standing around doing nothing.
495 const HarvestCommandType *hct= unit->getType()->getFirstHarvestCommand(rt,unit->getFaction());
496 Vec2i resPos;
497 if(hct != NULL && aiInterface->getNearestSightedResource(rt, aiInterface->getHomeLocation(), resPos, false)) {
498 amount= r->getAmount();
499 neededResource= rt;
500 }
501 }
502 }
503 }
504
505 char szBuf[8096]="";
506 snprintf(szBuf,8096,"Unit [%d - %s] looking for resources (not static or consumable)",unit->getId(),unit->getType()->getName(false).c_str());
507 aiInterface->printLog(3, szBuf);
508 snprintf(szBuf,8096,"[resource type count %d] Needed resource [%s].",tt->getResourceTypeCount(),(neededResource != NULL ? neededResource->getName().c_str() : "<none>"));
509 aiInterface->printLog(3, szBuf);
510
511 return neededResource;
512 }
513
beingAttacked(Vec2i & pos,Field & field,int radius)514 bool Ai::beingAttacked(Vec2i &pos, Field &field, int radius){
515 const Unit *enemy = aiInterface->getFirstOnSightEnemyUnit(pos, field, radius);
516 return (enemy != NULL);
517 }
518
isStableBase()519 bool Ai::isStableBase() {
520 UnitClass ucWorkerType = ucWorker;
521 if(getCountOfClass(ucWarrior,&ucWorkerType) > minWarriors) {
522 char szBuf[8096]="";
523 snprintf(szBuf,8096,"Base is stable [minWarriors = %d found = %d]",minWarriors,ucWorkerType);
524 aiInterface->printLog(4, szBuf);
525
526 return true;
527 }
528 else{
529 char szBuf[8096]="";
530 snprintf(szBuf,8096,"Base is NOT stable [minWarriors = %d found = %d]",minWarriors,ucWorkerType);
531 aiInterface->printLog(4, szBuf);
532
533 return false;
534 }
535 }
536
findAbleUnit(int * unitIndex,CommandClass ability,bool idleOnly)537 bool Ai::findAbleUnit(int *unitIndex, CommandClass ability, bool idleOnly){
538 vector<int> units;
539
540 *unitIndex= -1;
541 for(int i=0; i<aiInterface->getMyUnitCount(); ++i){
542 const Unit *unit= aiInterface->getMyUnit(i);
543 if(unit->getType()->isCommandable() && unit->getType()->hasCommandClass(ability)){
544 if(!idleOnly || !unit->anyCommand() || unit->getCurrCommand()->getCommandType()->getClass()==ccStop){
545 units.push_back(i);
546 }
547 }
548 }
549
550 if(units.empty()){
551 return false;
552 }
553 else{
554 *unitIndex= units[random.randRange(0, (int)units.size()-1)];
555 return true;
556 }
557 }
558
findUnitsHarvestingResourceType(const ResourceType * rt)559 vector<int> Ai::findUnitsHarvestingResourceType(const ResourceType *rt) {
560 vector<int> units;
561
562 Map *map= aiInterface->getMap();
563 for(int i = 0; i < aiInterface->getMyUnitCount(); ++i) {
564 const Unit *unit= aiInterface->getMyUnit(i);
565 if(unit->getType()->isCommandable()) {
566 if(unit->getType()->hasCommandClass(ccHarvest)) {
567 if(unit->anyCommand() && unit->getCurrCommand()->getCommandType()->getClass() == ccHarvest) {
568 Command *command= unit->getCurrCommand();
569 const HarvestCommandType *hct= dynamic_cast<const HarvestCommandType*>(command->getCommandType());
570 if(hct != NULL) {
571 const Vec2i unitTargetPos = unit->getTargetPos();
572 SurfaceCell *sc= map->getSurfaceCell(Map::toSurfCoords(unitTargetPos));
573 Resource *r= sc->getResource();
574 if (r != NULL && r->getType() == rt) {
575 units.push_back(i);
576 }
577 }
578 }
579 }
580 else if(unit->getType()->hasCommandClass(ccProduce)) {
581 if(unit->anyCommand() && unit->getCurrCommand()->getCommandType()->getClass() == ccProduce) {
582 Command *command= unit->getCurrCommand();
583 const ProduceCommandType *pct= dynamic_cast<const ProduceCommandType*>(command->getCommandType());
584 if(pct != NULL) {
585 const UnitType *ut = pct->getProducedUnit();
586 if(ut != NULL) {
587 const Resource *r = ut->getCost(rt);
588 if(r != NULL) {
589 if (r != NULL && r->getAmount() < 0) {
590 units.push_back(i);
591 }
592 }
593 }
594 }
595 }
596 }
597 else if(unit->getType()->hasCommandClass(ccBuild)) {
598 if(unit->anyCommand() && unit->getCurrCommand()->getCommandType()->getClass() == ccBuild) {
599 Command *command= unit->getCurrCommand();
600 const BuildCommandType *bct= dynamic_cast<const BuildCommandType*>(command->getCommandType());
601 if(bct != NULL) {
602 for(int j = 0; j < bct->getBuildingCount(); ++j) {
603 const UnitType *ut = bct->getBuilding(j);
604 if(ut != NULL) {
605 const Resource *r = ut->getCost(rt);
606 if(r != NULL) {
607 if (r != NULL && r->getAmount() < 0) {
608 units.push_back(i);
609 break;
610 }
611 }
612 }
613 }
614 }
615 }
616 }
617 }
618 }
619
620 return units;
621 }
622
findUnitsDoingCommand(CommandClass currentCommand)623 vector<int> Ai::findUnitsDoingCommand(CommandClass currentCommand) {
624 vector<int> units;
625
626 for(int i = 0; i < aiInterface->getMyUnitCount(); ++i) {
627 const Unit *unit= aiInterface->getMyUnit(i);
628 if(unit->getType()->isCommandable() && unit->getType()->hasCommandClass(currentCommand)) {
629 if(unit->anyCommand() && unit->getCurrCommand()->getCommandType()->getClass() == currentCommand) {
630 units.push_back(i);
631 }
632 }
633 }
634
635 return units;
636 }
637
findAbleUnit(int * unitIndex,CommandClass ability,CommandClass currentCommand)638 bool Ai::findAbleUnit(int *unitIndex, CommandClass ability, CommandClass currentCommand){
639 vector<int> units;
640
641 *unitIndex= -1;
642 for(int i=0; i<aiInterface->getMyUnitCount(); ++i){
643 const Unit *unit= aiInterface->getMyUnit(i);
644 if(unit->getType()->isCommandable() && unit->getType()->hasCommandClass(ability)){
645 if(unit->anyCommand() && unit->getCurrCommand()->getCommandType()->getClass()==currentCommand){
646 units.push_back(i);
647 }
648 }
649 }
650
651 if(units.empty()){
652 return false;
653 }
654 else{
655 *unitIndex= units[random.randRange(0, (int)units.size()-1)];
656 return true;
657 }
658 }
659
findPosForBuilding(const UnitType * building,const Vec2i & searchPos,Vec2i & outPos)660 bool Ai::findPosForBuilding(const UnitType* building, const Vec2i &searchPos, Vec2i &outPos){
661
662 for(int currRadius = 0; currRadius < maxBuildRadius; ++currRadius) {
663 for(int i=searchPos.x - currRadius; i < searchPos.x + currRadius; ++i) {
664 for(int j=searchPos.y - currRadius; j < searchPos.y + currRadius; ++j) {
665 outPos= Vec2i(i, j);
666 if(aiInterface->isFreeCells(outPos - Vec2i(minBuildSpacing), building->getAiBuildSize() + minBuildSpacing * 2, fLand)) {
667 int aiBuildSizeDiff= building->getAiBuildSize()- building->getSize();
668 if( aiBuildSizeDiff>0){
669 int halfSize=aiBuildSizeDiff/2;
670 outPos.x+=halfSize;
671 outPos.y+=halfSize;
672 }
673 return true;
674 }
675 }
676 }
677 }
678
679 return false;
680
681 }
682
683
684 // ==================== tasks ====================
685
addTask(const Task * task)686 void Ai::addTask(const Task *task){
687 tasks.push_back(task);
688 aiInterface->printLog(2, "Task added: " + task->toString());
689 }
690
addPriorityTask(const Task * task)691 void Ai::addPriorityTask(const Task *task){
692 deleteValues(tasks.begin(), tasks.end());
693 tasks.clear();
694
695 tasks.push_back(task);
696 aiInterface->printLog(2, "Priority Task added: " + task->toString());
697 }
698
anyTask()699 bool Ai::anyTask(){
700 return !tasks.empty();
701 }
702
getTask() const703 const Task *Ai::getTask() const{
704 if(tasks.empty()){
705 return NULL;
706 }
707 else{
708 return tasks.front();
709 }
710 }
711
removeTask(const Task * task)712 void Ai::removeTask(const Task *task){
713 aiInterface->printLog(2, "Task removed: " + task->toString());
714 tasks.remove(task);
715 delete task;
716 }
717
retryTask(const Task * task)718 void Ai::retryTask(const Task *task){
719 tasks.remove(task);
720 tasks.push_back(task);
721 }
722 // ==================== expansions ====================
723
addExpansion(const Vec2i & pos)724 void Ai::addExpansion(const Vec2i &pos) {
725
726 //check if there is a nearby expansion
727 for(Positions::iterator it = expansionPositions.begin(); it != expansionPositions.end(); ++it) {
728 if((*it).dist(pos) < villageRadius) {
729 return;
730 }
731 }
732
733 //add expansion
734 expansionPositions.push_front(pos);
735
736 //remove expansion if queue is list is full
737 if((int)expansionPositions.size() > maxExpansions){
738 expansionPositions.pop_back();
739 }
740 }
741
getRandomHomePosition()742 Vec2i Ai::getRandomHomePosition() {
743
744 if(expansionPositions.empty() || random.randRange(0, 1) == 0){
745 return aiInterface->getHomeLocation();
746 }
747
748 return expansionPositions[random.randRange(0, (int)expansionPositions.size()-1)];
749 }
750
751 // ==================== actions ====================
752
sendScoutPatrol()753 void Ai::sendScoutPatrol(){
754
755 Vec2i pos;
756 int unit;
757 bool possibleTargetFound= false;
758
759 bool ultraResourceAttack= (aiInterface->getControlType() == ctCpuUltra || aiInterface->getControlType() == ctNetworkCpuUltra)
760 && random.randRange(0, 2) == 1;
761 bool megaResourceAttack=(aiInterface->getControlType() == ctCpuMega || aiInterface->getControlType() == ctNetworkCpuMega)
762 && random.randRange(0, 1) == 1;
763
764 if(possibleTargetFound == false && (megaResourceAttack || ultraResourceAttack)) {
765 Map *map= aiInterface->getMap();
766
767 const TechTree *tt= aiInterface->getTechTree();
768 const ResourceType *rt= tt->getResourceType(0);
769 int tryCount= 0;
770 int height= map->getH();
771 int width= map->getW();
772
773 for(int i= 0; i < tt->getResourceTypeCount(); ++i){
774 const ResourceType *rt_= tt->getResourceType(i);
775 //const Resource *r= aiInterface->getResource(rt);
776
777 if(rt_->getClass() == rcTech){
778 rt=rt_;
779 break;
780 }
781 }
782 //printf("looking for resource %s\n",rt->getName().c_str());
783 while(possibleTargetFound == false){
784 tryCount++;
785 if(tryCount == 4){
786 //printf("no target found\n");
787 break;
788 }
789 pos= Vec2i(random.randRange(2, width - 2), random.randRange(2, height - 2));
790 if(map->isInside(pos) && map->isInsideSurface(map->toSurfCoords(pos))){
791 //printf("is inside map\n");
792 // find first resource in this area
793 Vec2i resPos;
794 if(aiInterface->isResourceInRegion(pos, rt, resPos, scoutResourceRange)){
795 // found a possible target.
796 pos= resPos;
797 //printf("lets try the new target\n");
798 possibleTargetFound= true;
799 break;
800 }
801 }
802 //else printf("is outside map\n");
803 }
804 }
805
806 std::vector<Vec2i> warningEnemyList = aiInterface->getEnemyWarningPositionList();
807 if( (possibleTargetFound == false) && (warningEnemyList.empty() == false)) {
808 //for(int i = (int)warningEnemyList.size() - 1; i <= 0; --i) {
809 //Vec2i &checkPos = warningEnemyList[i];
810 Vec2i &checkPos = warningEnemyList[0];
811 if (random.randRange(0, 1) == 1 ) {
812 pos = checkPos;
813 possibleTargetFound = true;
814 warningEnemyList.clear();
815 } else {
816 aiInterface->removeEnemyWarningPositionFromList(checkPos);
817 }
818 //break;
819 //}
820 }
821
822 if(possibleTargetFound == false){
823 startLoc= (startLoc + 1) % aiInterface->getMapMaxPlayers();
824 pos= aiInterface->getStartLocation(startLoc);
825 //printf("normal target used\n");
826 }
827
828 if(aiInterface->getHomeLocation() != pos){
829 if(findAbleUnit(&unit, ccAttack, false)){
830 if(SystemFlags::getSystemSettingType(SystemFlags::debugSystem).enabled)
831 SystemFlags::OutputDebug(SystemFlags::debugSystem,"In [%s::%s Line: %d]\n",__FILE__,__FUNCTION__,__LINE__);
832
833 aiInterface->giveCommand(unit, ccAttack, pos);
834 aiInterface->printLog(2, "Scout patrol sent to: " + intToStr(pos.x) + "," + intToStr(pos.y) + "\n");
835 }
836 }
837 }
838
massiveAttack(const Vec2i & pos,Field field,bool ultraAttack)839 void Ai::massiveAttack(const Vec2i &pos, Field field, bool ultraAttack){
840 int producerWarriorCount=0;
841 int maxProducerWarriors=random.randRange(1,11);
842 int unitCount = aiInterface->getMyUnitCount();
843 int unitGroupCommandId = -1;
844
845 int attackerWorkersHarvestingCount = 0;
846 for(int i = 0; i < unitCount; ++i) {
847 bool isWarrior=false;
848 bool productionInProgress=false;
849 const Unit *unit= aiInterface->getMyUnit(i);
850 const AttackCommandType *act= unit->getType()->getFirstAttackCommand(field);
851
852 if( aiInterface->getControlType() == ctCpuMega ||
853 aiInterface->getControlType() == ctNetworkCpuMega) {
854 if(producerWarriorCount > maxProducerWarriors) {
855 if(
856 unit->getCommandSize()>0 &&
857 unit->getCurrCommand()->getCommandType()!=NULL && (
858 unit->getCurrCommand()->getCommandType()->getClass()==ccBuild ||
859 unit->getCurrCommand()->getCommandType()->getClass()==ccMorph ||
860 unit->getCurrCommand()->getCommandType()->getClass()==ccProduce
861 )
862 ) {
863 productionInProgress=true;
864 isWarrior=false;
865 producerWarriorCount++;
866 }
867 else {
868 isWarrior =! unit->getType()->hasCommandClass(ccHarvest);
869 }
870
871 }
872 else {
873 isWarrior= !unit->getType()->hasCommandClass(ccHarvest) && !unit->getType()->hasCommandClass(ccProduce);
874 }
875 }
876 else {
877 isWarrior= !unit->getType()->hasCommandClass(ccHarvest) && !unit->getType()->hasCommandClass(ccProduce);
878 }
879
880 bool alreadyAttacking= (unit->getCurrSkill()->getClass() == scAttack);
881
882 bool unitSignalledToAttack = false;
883 if(alreadyAttacking == false && unit->getType()->hasSkillClass(scAttack) && (aiInterface->getControlType()
884 == ctCpuUltra || aiInterface->getControlType() == ctCpuMega || aiInterface->getControlType()
885 == ctNetworkCpuUltra || aiInterface->getControlType() == ctNetworkCpuMega)){
886 //printf("~~~~~~~~ Unit [%s - %d] checking if unit is being attacked\n",unit->getFullName().c_str(),unit->getId());
887
888 std::pair<bool, Unit *> beingAttacked= aiInterface->getWorld()->getUnitUpdater()->unitBeingAttacked(unit);
889 if(beingAttacked.first == true){
890 Unit *enemy= beingAttacked.second;
891 const AttackCommandType *act_forenemy= unit->getType()->getFirstAttackCommand(enemy->getCurrField());
892
893 //printf("~~~~~~~~ Unit [%s - %d] attacked by enemy [%s - %d] act_forenemy [%p] enemy->getCurrField() = %d\n",unit->getFullName().c_str(),unit->getId(),enemy->getFullName().c_str(),enemy->getId(),act_forenemy,enemy->getCurrField());
894
895 if(act_forenemy != NULL){
896 bool shouldAttack= true;
897 if(unit->getType()->hasSkillClass(scHarvest)){
898 shouldAttack= (attackerWorkersHarvestingCount > minWorkerAttackersHarvesting);
899 if(shouldAttack == false){
900 attackerWorkersHarvestingCount++;
901 }
902 }
903 if(shouldAttack) {
904 if(unitGroupCommandId == -1) {
905 unitGroupCommandId = aiInterface->getWorld()->getNextCommandGroupId();
906 }
907
908 aiInterface->giveCommand(i, act_forenemy, beingAttacked.second->getPos(), unitGroupCommandId);
909 unitSignalledToAttack= true;
910 }
911 }
912 else{
913 const AttackStoppedCommandType *asct_forenemy= unit->getType()->getFirstAttackStoppedCommand(
914 enemy->getCurrField());
915 //printf("~~~~~~~~ Unit [%s - %d] found enemy [%s - %d] asct_forenemy [%p] enemy->getCurrField() = %d\n",unit->getFullName().c_str(),unit->getId(),enemy->getFullName().c_str(),enemy->getId(),asct_forenemy,enemy->getCurrField());
916 if(asct_forenemy != NULL){
917 bool shouldAttack= true;
918 if(unit->getType()->hasSkillClass(scHarvest)){
919 shouldAttack= (attackerWorkersHarvestingCount > minWorkerAttackersHarvesting);
920 if(shouldAttack == false){
921 attackerWorkersHarvestingCount++;
922 }
923 }
924 if(shouldAttack){
925 // printf("~~~~~~~~ Unit [%s - %d] WILL AttackStoppedCommand [%s - %d]\n", unit->getFullName().c_str(),
926 // unit->getId(), enemy->getFullName().c_str(), enemy->getId());
927
928 if(unitGroupCommandId == -1) {
929 unitGroupCommandId = aiInterface->getWorld()->getNextCommandGroupId();
930 }
931
932 aiInterface->giveCommand(i, asct_forenemy, beingAttacked.second->getCenteredPos(), unitGroupCommandId);
933 unitSignalledToAttack= true;
934 }
935 }
936 }
937 }
938 }
939 if(alreadyAttacking == false && act != NULL && (ultraAttack || isWarrior) &&
940 unitSignalledToAttack == false) {
941 bool shouldAttack = true;
942 if(unit->getType()->hasSkillClass(scHarvest)) {
943 shouldAttack = (attackerWorkersHarvestingCount > minWorkerAttackersHarvesting);
944 if(shouldAttack == false) {
945 attackerWorkersHarvestingCount++;
946 }
947 }
948
949 // Mega CPU does not send ( far away ) units which are currently producing something
950 if(aiInterface->getControlType() == ctCpuMega || aiInterface->getControlType() == ctNetworkCpuMega){
951 if(!isWarrior ){
952 if(!productionInProgress){
953 shouldAttack= false;
954 //printf("no attack \n ");
955 }
956 }
957 }
958 if(shouldAttack) {
959 if(unitGroupCommandId == -1) {
960 unitGroupCommandId = aiInterface->getWorld()->getNextCommandGroupId();
961 }
962
963 aiInterface->giveCommand(i, act, pos, unitGroupCommandId);
964 }
965 }
966 }
967
968 if( aiInterface->getControlType() == ctCpuEasy ||
969 aiInterface->getControlType() == ctNetworkCpuEasy) {
970 minWarriors += minMinWarriorsExpandCpuEasy;
971 }
972 else if(aiInterface->getControlType() == ctCpuMega ||
973 aiInterface->getControlType() == ctNetworkCpuMega) {
974 minWarriors += minMinWarriorsExpandCpuMega;
975 if(minWarriors > maxMinWarriors-1 || randomMinWarriorsReached) {
976 randomMinWarriorsReached=true;
977 minWarriors=random.randRange(maxMinWarriors-10, maxMinWarriors*2);
978 }
979 }
980 else if(minWarriors < maxMinWarriors) {
981 if(aiInterface->getControlType() == ctCpuUltra ||
982 aiInterface->getControlType() == ctNetworkCpuUltra) {
983 minWarriors += minMinWarriorsExpandCpuUltra;
984 }
985 else {
986 minWarriors+= minMinWarriorsExpandCpuNormal;
987 }
988 }
989 aiInterface->printLog(2, "Massive attack to pos: "+ intToStr(pos.x)+", "+intToStr(pos.y)+"\n");
990 }
991
returnBase(int unitIndex)992 void Ai::returnBase(int unitIndex) {
993 Vec2i pos;
994 //std::pair<CommandResult,string> r(crFailUndefined,"");
995 //aiInterface->getFactionIndex();
996 pos= Vec2i(
997 random.randRange(-villageRadius, villageRadius),
998 random.randRange(-villageRadius, villageRadius)) +
999 getRandomHomePosition();
1000
1001 if(SystemFlags::getSystemSettingType(SystemFlags::debugSystem).enabled) SystemFlags::OutputDebug(SystemFlags::debugSystem,"In [%s::%s Line: %d]\n",__FILE__,__FUNCTION__,__LINE__);
1002 //r= aiInterface->giveCommand(unitIndex, ccMove, pos);
1003 aiInterface->giveCommand(unitIndex, ccMove, pos);
1004
1005 //aiInterface->printLog(1, "Order return to base pos:" + intToStr(pos.x)+", "+intToStr(pos.y)+": "+rrToStr(r)+"\n");
1006 }
1007
harvest(int unitIndex)1008 void Ai::harvest(int unitIndex) {
1009 const ResourceType *rt= getNeededResource(unitIndex);
1010 if(rt != NULL) {
1011 const HarvestCommandType *hct= aiInterface->getMyUnit(unitIndex)->getType()->getFirstHarvestCommand(rt,aiInterface->getMyUnit(unitIndex)->getFaction());
1012
1013 Vec2i resPos;
1014 if(hct != NULL && aiInterface->getNearestSightedResource(rt, aiInterface->getHomeLocation(), resPos, false)) {
1015 resPos= resPos+Vec2i(random.randRange(-2, 2), random.randRange(-2, 2));
1016 aiInterface->giveCommand(unitIndex, hct, resPos, -1);
1017 //aiInterface->printLog(4, "Order harvest pos:" + intToStr(resPos.x)+", "+intToStr(resPos.y)+": "+rrToStr(r)+"\n");
1018 }
1019 }
1020 }
1021
haveBlockedUnits()1022 bool Ai::haveBlockedUnits() {
1023 Chrono chrono;
1024 if(SystemFlags::getSystemSettingType(SystemFlags::debugPerformance).enabled) chrono.start();
1025
1026 if(SystemFlags::getSystemSettingType(SystemFlags::debugPerformance).enabled && chrono.getMillis() > 0) SystemFlags::OutputDebug(SystemFlags::debugPerformance,"In [%s::%s Line: %d] took msecs: %lld [START]\n",__FILE__,__FUNCTION__,__LINE__,chrono.getMillis());
1027
1028 int unitCount = aiInterface->getMyUnitCount();
1029 Map *map = aiInterface->getMap();
1030 //If there is no close store
1031 for(int j=0; j < unitCount; ++j) {
1032 const Unit *u= aiInterface->getMyUnit(j);
1033 const UnitType *ut= u->getType();
1034
1035 // If this building is a store
1036 if(u->isAlive() && ut->isMobile() && u->getPath() != NULL && (u->getPath()->isBlocked() || u->getPath()->getBlockCount())) {
1037 Vec2i unitPos = u->getPosNotThreadSafe();
1038
1039 //printf("#1 AI found blocked unit [%d - %s]\n",u->getId(),u->getFullName().c_str());
1040
1041 int failureCount = 0;
1042 int cellCount = 0;
1043
1044 for(int i = -1; i <= 1; ++i) {
1045 for(int j = -1; j <= 1; ++j) {
1046 Vec2i pos = unitPos + Vec2i(i, j);
1047 if(map->isInside(pos) && map->isInsideSurface(map->toSurfCoords(pos))) {
1048 if(pos != unitPos) {
1049 bool canUnitMoveToCell = map->aproxCanMove(u, unitPos, pos);
1050 if(canUnitMoveToCell == false) {
1051 failureCount++;
1052 }
1053 cellCount++;
1054 }
1055 }
1056 }
1057 }
1058 bool unitImmediatelyBlocked = (failureCount == cellCount);
1059 //printf("#1 unitImmediatelyBlocked = %d, failureCount = %d, cellCount = %d\n",unitImmediatelyBlocked,failureCount,cellCount);
1060
1061 if(unitImmediatelyBlocked) {
1062 //printf("#1 AI unit IS BLOCKED [%d - %s]\n",u->getId(),u->getFullName().c_str());
1063 if(SystemFlags::getSystemSettingType(SystemFlags::debugPerformance).enabled && chrono.getMillis() > 0) SystemFlags::OutputDebug(SystemFlags::debugPerformance,"In [%s::%s Line: %d] took msecs: %lld [START]\n",__FILE__,__FUNCTION__,__LINE__,chrono.getMillis());
1064 return true;
1065 }
1066 }
1067 }
1068
1069 if(SystemFlags::getSystemSettingType(SystemFlags::debugPerformance).enabled && chrono.getMillis() > 0) SystemFlags::OutputDebug(SystemFlags::debugPerformance,"In [%s::%s Line: %d] took msecs: %lld [START]\n",__FILE__,__FUNCTION__,__LINE__,chrono.getMillis());
1070 return false;
1071 }
1072
getAdjacentUnits(std::map<float,std::map<int,const Unit * >> & signalAdjacentUnits,const Unit * unit)1073 bool Ai::getAdjacentUnits(std::map<float, std::map<int, const Unit *> > &signalAdjacentUnits, const Unit *unit) {
1074 //printf("In getAdjacentUnits...\n");
1075
1076 bool result = false;
1077 Map *map = aiInterface->getMap();
1078 Vec2i unitPos = unit->getPosNotThreadSafe();
1079 for(int i = -1; i <= 1; ++i) {
1080 for(int j = -1; j <= 1; ++j) {
1081 Vec2i pos = unitPos + Vec2i(i, j);
1082 if(map->isInside(pos) && map->isInsideSurface(map->toSurfCoords(pos))) {
1083 if(pos != unitPos) {
1084 Unit *adjacentUnit = map->getCell(pos)->getUnit(unit->getCurrField());
1085 if(adjacentUnit != NULL && adjacentUnit->getFactionIndex() == unit->getFactionIndex()) {
1086 if(adjacentUnit->getType()->isMobile() && adjacentUnit->getPath() != NULL) {
1087 //signalAdjacentUnits.push_back(adjacentUnit);
1088 float dist = unitPos.dist(adjacentUnit->getPos());
1089
1090 std::map<float, std::map<int, const Unit *> >::iterator iterFind1 = signalAdjacentUnits.find(dist);
1091 if(iterFind1 == signalAdjacentUnits.end()) {
1092 signalAdjacentUnits[dist][adjacentUnit->getId()] = adjacentUnit;
1093
1094 getAdjacentUnits(signalAdjacentUnits, adjacentUnit);
1095 result = true;
1096 }
1097 else {
1098 std::map<int, const Unit *>::iterator iterFind2 = iterFind1->second.find(adjacentUnit->getId());
1099 if(iterFind2 == iterFind1->second.end()) {
1100 signalAdjacentUnits[dist][adjacentUnit->getId()] = adjacentUnit;
1101 getAdjacentUnits(signalAdjacentUnits, adjacentUnit);
1102 result = true;
1103 }
1104 }
1105 }
1106 }
1107 }
1108 }
1109 }
1110 }
1111 return result;
1112 }
1113
unblockUnits()1114 void Ai::unblockUnits() {
1115 Chrono chrono;
1116 if(SystemFlags::getSystemSettingType(SystemFlags::debugPerformance).enabled) chrono.start();
1117
1118 if(SystemFlags::getSystemSettingType(SystemFlags::debugPerformance).enabled && chrono.getMillis() > 0) SystemFlags::OutputDebug(SystemFlags::debugPerformance,"In [%s::%s Line: %d] took msecs: %lld [START]\n",__FILE__,__FUNCTION__,__LINE__,chrono.getMillis());
1119
1120 int unitCount = aiInterface->getMyUnitCount();
1121 Map *map = aiInterface->getMap();
1122 // Find blocked units and move surrounding units out of the way
1123 std::map<float, std::map<int, const Unit *> > signalAdjacentUnits;
1124 for(int idx=0; idx < unitCount; ++idx) {
1125 const Unit *u= aiInterface->getMyUnit(idx);
1126 const UnitType *ut= u->getType();
1127
1128 // If this building is a store
1129 if(u->isAlive() && ut->isMobile() && u->getPath() != NULL && (u->getPath()->isBlocked() || u->getPath()->getBlockCount())) {
1130 Vec2i unitPos = u->getPosNotThreadSafe();
1131
1132 //printf("#2 AI found blocked unit [%d - %s]\n",u->getId(),u->getFullName().c_str());
1133
1134 int failureCount = 0;
1135 int cellCount = 0;
1136
1137 for(int i = -1; i <= 1; ++i) {
1138 for(int j = -1; j <= 1; ++j) {
1139 Vec2i pos = unitPos + Vec2i(i, j);
1140 if(map->isInside(pos) && map->isInsideSurface(map->toSurfCoords(pos))) {
1141 if(pos != unitPos) {
1142 bool canUnitMoveToCell = map->aproxCanMove(u, unitPos, pos);
1143 if(canUnitMoveToCell == false) {
1144 failureCount++;
1145 getAdjacentUnits(signalAdjacentUnits, u);
1146 }
1147 cellCount++;
1148 }
1149 }
1150 }
1151 }
1152 //bool unitImmediatelyBlocked = (failureCount == cellCount);
1153 //printf("#2 unitImmediatelyBlocked = %d, failureCount = %d, cellCount = %d, signalAdjacentUnits.size() = %d\n",unitImmediatelyBlocked,failureCount,cellCount,signalAdjacentUnits.size());
1154 }
1155 }
1156
1157 if(SystemFlags::getSystemSettingType(SystemFlags::debugPerformance).enabled && chrono.getMillis() > 0) SystemFlags::OutputDebug(SystemFlags::debugPerformance,"In [%s::%s Line: %d] took msecs: %lld [START]\n",__FILE__,__FUNCTION__,__LINE__,chrono.getMillis());
1158
1159 if(signalAdjacentUnits.empty() == false) {
1160 //printf("#2 AI units ARE BLOCKED about to unblock\n");
1161
1162 int unitGroupCommandId = -1;
1163
1164 for(std::map<float, std::map<int, const Unit *> >::reverse_iterator iterMap = signalAdjacentUnits.rbegin();
1165 iterMap != signalAdjacentUnits.rend(); ++iterMap) {
1166
1167 for(std::map<int, const Unit *>::iterator iterMap2 = iterMap->second.begin();
1168 iterMap2 != iterMap->second.end(); ++iterMap2) {
1169 //int idx = iterMap2->first;
1170 const Unit *adjacentUnit = iterMap2->second;
1171 if(adjacentUnit != NULL && adjacentUnit->getType()->getFirstCtOfClass(ccMove) != NULL) {
1172 const CommandType *ct = adjacentUnit->getType()->getFirstCtOfClass(ccMove);
1173
1174 for(int moveAttempt = 1; moveAttempt <= villageRadius; ++moveAttempt) {
1175 Vec2i pos= Vec2i(
1176 random.randRange(-villageRadius*2, villageRadius*2),
1177 random.randRange(-villageRadius*2, villageRadius*2)) +
1178 adjacentUnit->getPosNotThreadSafe();
1179
1180 bool canUnitMoveToCell = map->aproxCanMove(adjacentUnit, adjacentUnit->getPosNotThreadSafe(), pos);
1181 if(canUnitMoveToCell == true) {
1182
1183 if(ct != NULL) {
1184 if(unitGroupCommandId == -1) {
1185 unitGroupCommandId = aiInterface->getWorld()->getNextCommandGroupId();
1186 }
1187
1188 //std::pair<CommandResult,string> r = aiInterface->giveCommand(adjacentUnit,ct, pos, unitGroupCommandId);
1189 aiInterface->giveCommand(adjacentUnit,ct, pos, unitGroupCommandId);
1190 }
1191 }
1192 }
1193 }
1194 }
1195 }
1196 }
1197
1198 if(SystemFlags::getSystemSettingType(SystemFlags::debugPerformance).enabled && chrono.getMillis() > 0) SystemFlags::OutputDebug(SystemFlags::debugPerformance,"In [%s::%s Line: %d] took msecs: %lld [START]\n",__FILE__,__FUNCTION__,__LINE__,chrono.getMillis());
1199 }
1200
outputAIBehaviourToConsole() const1201 bool Ai::outputAIBehaviourToConsole() const {
1202 return false;
1203 }
1204
saveGame(XmlNode * rootNode) const1205 void Ai::saveGame(XmlNode *rootNode) const {
1206 std::map<string,string> mapTagReplacements;
1207 XmlNode *aiNode = rootNode->addChild("Ai");
1208
1209 // AiInterface *aiInterface;
1210 // AiRules aiRules;
1211 // int startLoc;
1212 aiNode->addAttribute("startLoc",intToStr(startLoc), mapTagReplacements);
1213 // bool randomMinWarriorsReached;
1214 aiNode->addAttribute("randomMinWarriorsReached",intToStr(randomMinWarriorsReached), mapTagReplacements);
1215 // Tasks tasks;
1216 for(Tasks::const_iterator it = tasks.begin(); it != tasks.end(); ++it) {
1217 (*it)->saveGame(aiNode);
1218 }
1219 // Positions expansionPositions;
1220 for(Positions::const_iterator it = expansionPositions.begin(); it != expansionPositions.end(); ++it) {
1221 XmlNode *expansionPositionsNode = aiNode->addChild("expansionPositions");
1222 expansionPositionsNode->addAttribute("pos",(*it).getString(), mapTagReplacements);
1223 }
1224
1225 // RandomGen random;
1226 aiNode->addAttribute("random",intToStr(random.getLastNumber()), mapTagReplacements);
1227 // std::map<int,int> factionSwitchTeamRequestCount;
1228
1229 // int maxBuildRadius;
1230 aiNode->addAttribute("maxBuildRadius",intToStr(maxBuildRadius), mapTagReplacements);
1231 // int minMinWarriors;
1232 aiNode->addAttribute("minMinWarriors",intToStr(minMinWarriors), mapTagReplacements);
1233 // int minMinWarriorsExpandCpuEasy;
1234 aiNode->addAttribute("minMinWarriorsExpandCpuEasy",intToStr(minMinWarriorsExpandCpuEasy), mapTagReplacements);
1235 // int minMinWarriorsExpandCpuMega;
1236 aiNode->addAttribute("minMinWarriorsExpandCpuMega",intToStr(minMinWarriorsExpandCpuMega), mapTagReplacements);
1237 // int minMinWarriorsExpandCpuUltra;
1238 aiNode->addAttribute("minMinWarriorsExpandCpuUltra",intToStr(minMinWarriorsExpandCpuUltra), mapTagReplacements);
1239 // int minMinWarriorsExpandCpuNormal;
1240 aiNode->addAttribute("minMinWarriorsExpandCpuNormal",intToStr(minMinWarriorsExpandCpuNormal), mapTagReplacements);
1241 // int maxMinWarriors;
1242 aiNode->addAttribute("maxMinWarriors",intToStr(maxMinWarriors), mapTagReplacements);
1243 // int maxExpansions;
1244 aiNode->addAttribute("maxExpansions",intToStr(maxExpansions), mapTagReplacements);
1245 // int villageRadius;
1246 aiNode->addAttribute("villageRadius",intToStr(villageRadius), mapTagReplacements);
1247 // int scoutResourceRange;
1248 aiNode->addAttribute("scoutResourceRange",intToStr(scoutResourceRange), mapTagReplacements);
1249 // int minWorkerAttackersHarvesting;
1250 aiNode->addAttribute("minWorkerAttackersHarvesting",intToStr(minWorkerAttackersHarvesting), mapTagReplacements);
1251 }
1252
loadGame(const XmlNode * rootNode,Faction * faction)1253 void Ai::loadGame(const XmlNode *rootNode, Faction *faction) {
1254 const XmlNode *aiNode = rootNode->getChild("Ai");
1255
1256 startLoc = aiNode->getAttribute("startLoc")->getIntValue();
1257 randomMinWarriorsReached = aiNode->getAttribute("randomMinWarriorsReached")->getIntValue() != 0;
1258
1259 vector<XmlNode *> taskNodeList = aiNode->getChildList("Task");
1260 for(unsigned int i = 0; i < taskNodeList.size(); ++i) {
1261 XmlNode *taskNode = taskNodeList[i];
1262 TaskClass taskClass = static_cast<TaskClass>(taskNode->getAttribute("taskClass")->getIntValue());
1263 switch(taskClass) {
1264 case tcProduce:
1265 {
1266 ProduceTask *newTask = ProduceTask::loadGame(taskNode, faction);
1267 tasks.push_back(newTask);
1268 }
1269 break;
1270 case tcBuild:
1271 {
1272 BuildTask *newTask = BuildTask::loadGame(taskNode, faction);
1273 tasks.push_back(newTask);
1274 }
1275 break;
1276 case tcUpgrade:
1277 {
1278 UpgradeTask *newTask = UpgradeTask::loadGame(taskNode, faction);
1279 tasks.push_back(newTask);
1280 }
1281 break;
1282 }
1283 }
1284
1285 vector<XmlNode *> expansionPositionsNodeList = aiNode->getChildList("expansionPositions");
1286 for(unsigned int i = 0; i < expansionPositionsNodeList.size(); ++i) {
1287 XmlNode *expansionPositionsNode = expansionPositionsNodeList[i];
1288 Vec2i pos = Vec2i::strToVec2(expansionPositionsNode->getAttribute("pos")->getValue());
1289 expansionPositions.push_back(pos);
1290 }
1291
1292 // RandomGen random;
1293 random.setLastNumber(aiNode->getAttribute("random")->getIntValue());
1294 // std::map<int,int> factionSwitchTeamRequestCount;
1295
1296 // int maxBuildRadius;
1297 maxBuildRadius = aiNode->getAttribute("maxBuildRadius")->getIntValue();
1298 // int minMinWarriors;
1299 minMinWarriors = aiNode->getAttribute("minMinWarriors")->getIntValue();
1300 // int minMinWarriorsExpandCpuEasy;
1301 minMinWarriorsExpandCpuEasy = aiNode->getAttribute("minMinWarriorsExpandCpuEasy")->getIntValue();
1302 // int minMinWarriorsExpandCpuMega;
1303 minMinWarriorsExpandCpuMega = aiNode->getAttribute("minMinWarriorsExpandCpuMega")->getIntValue();
1304 // int minMinWarriorsExpandCpuUltra;
1305 minMinWarriorsExpandCpuUltra = aiNode->getAttribute("minMinWarriorsExpandCpuUltra")->getIntValue();
1306 // int minMinWarriorsExpandCpuNormal;
1307 minMinWarriorsExpandCpuNormal = aiNode->getAttribute("minMinWarriorsExpandCpuNormal")->getIntValue();
1308 // int maxMinWarriors;
1309 maxMinWarriors = aiNode->getAttribute("maxMinWarriors")->getIntValue();
1310 // int maxExpansions;
1311 maxExpansions = aiNode->getAttribute("maxExpansions")->getIntValue();
1312 // int villageRadius;
1313 villageRadius = aiNode->getAttribute("villageRadius")->getIntValue();
1314 // int scoutResourceRange;
1315 scoutResourceRange = aiNode->getAttribute("scoutResourceRange")->getIntValue();
1316 // int minWorkerAttackersHarvesting;
1317 minWorkerAttackersHarvesting = aiNode->getAttribute("minWorkerAttackersHarvesting")->getIntValue();
1318 }
1319
1320 }}//end namespace
1321