From c979ea12546fb8a92319aeecae50e4bf730e8e01 Mon Sep 17 00:00:00 2001 From: Mark Vejvoda Date: Fri, 29 Apr 2011 23:42:16 +0000 Subject: [PATCH] - more AI enhancements, now units that harvest AND attack will be more wise when choosing to attack, (think persian) since they need to harvest more than they need to attack when harvester count is very low. - AI Units now place higher priority on repairing 'castle' style units when looking for something to repair. --- source/glest_game/ai/ai.cpp | 72 ++++++++++++++++++++++++-------- source/glest_game/ai/ai.h | 4 +- source/glest_game/ai/ai_rule.cpp | 72 +++++++++++++++++++++++++++++--- 3 files changed, 123 insertions(+), 25 deletions(-) diff --git a/source/glest_game/ai/ai.cpp b/source/glest_game/ai/ai.cpp index 5b9cebe5..a161971c 100644 --- a/source/glest_game/ai/ai.cpp +++ b/source/glest_game/ai/ai.cpp @@ -202,22 +202,28 @@ int Ai::getCountOfType(const UnitType *ut){ return count; } -int Ai::getCountOfClass(UnitClass uc){ +int Ai::getCountOfClass(UnitClass uc,UnitClass *additionalUnitClassToExcludeFromCount) { int count= 0; - for(int i=0; igetMyUnitCount(); ++i){ - if(aiInterface->getMyUnit(i)->getType()->isOfClass(uc)){ + for(int i = 0; i < aiInterface->getMyUnitCount(); ++i) { + if(aiInterface->getMyUnit(i)->getType()->isOfClass(uc)) { + // Skip unit if it ALSO contains the exclusion unit class type + if(additionalUnitClassToExcludeFromCount != NULL) { + if(aiInterface->getMyUnit(i)->getType()->isOfClass(*additionalUnitClassToExcludeFromCount)) { + continue; + } + } ++count; } } return count; } -float Ai::getRatioOfClass(UnitClass uc){ - if(aiInterface->getMyUnitCount()==0){ +float Ai::getRatioOfClass(UnitClass uc,UnitClass *additionalUnitClassToExcludeFromCount) { + if(aiInterface->getMyUnitCount() == 0) { return 0; } - else{ - return static_cast(getCountOfClass(uc))/aiInterface->getMyUnitCount(); + else { + return static_cast(getCountOfClass(uc,additionalUnitClassToExcludeFromCount)) / aiInterface->getMyUnitCount(); } } @@ -272,9 +278,9 @@ bool Ai::beingAttacked(Vec2i &pos, Field &field, int radius){ */ } -bool Ai::isStableBase(){ - - if(getCountOfClass(ucWarrior)>minWarriors){ +bool Ai::isStableBase() { + UnitClass ucWorkerType = ucWorker; + if(getCountOfClass(ucWarrior,&ucWorkerType) > minWarriors) { aiInterface->printLog(4, "Base is stable\n"); return true; } @@ -436,10 +442,13 @@ void Ai::sendScoutPatrol(){ } void Ai::massiveAttack(const Vec2i &pos, Field field, bool ultraAttack){ + const int minWorkerAttackersHarvesting = 3; + int producerWarriorCount=0; int maxProducerWarriors=random.randRange(1,11); int unitCount = aiInterface->getMyUnitCount(); + int attackerWorkersHarvestingCount = 0; for(int i = 0; i < unitCount; ++i) { bool isWarrior=false; const Unit *unit= aiInterface->getMyUnit(i); @@ -493,24 +502,51 @@ void Ai::massiveAttack(const Vec2i &pos, Field field, bool ultraAttack){ //printf("~~~~~~~~ Unit [%s - %d] found 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()); if(act_forenemy != NULL) { - //printf("~~~~~~~~ Unit [%s - %d] WILL ATTACK [%s - %d]\n",unit->getFullName().c_str(),unit->getId(),enemy->getFullName().c_str(),enemy->getId()); - aiInterface->giveCommand(i, act_forenemy, beingAttacked.second->getPos()); - unitSignalledToAttack = true; + bool shouldAttack = true; + if(unit->getType()->hasSkillClass(scHarvest)) { + shouldAttack = (attackerWorkersHarvestingCount > minWorkerAttackersHarvesting); + if(shouldAttack == false) { + attackerWorkersHarvestingCount++; + } + } + if(shouldAttack) { + //printf("~~~~~~~~ Unit [%s - %d] WILL ATTACK [%s - %d]\n",unit->getFullName().c_str(),unit->getId(),enemy->getFullName().c_str(),enemy->getId()); + aiInterface->giveCommand(i, act_forenemy, beingAttacked.second->getPos()); + unitSignalledToAttack = true; + } } else { const AttackStoppedCommandType *asct_forenemy = unit->getType()->getFirstAttackStoppedCommand(enemy->getCurrField()); //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()); if(asct_forenemy != NULL) { - //printf("~~~~~~~~ Unit [%s - %d] WILL ATTACK [%s - %d]\n",unit->getFullName().c_str(),unit->getId(),enemy->getFullName().c_str(),enemy->getId()); - aiInterface->giveCommand(i, asct_forenemy, beingAttacked.second->getCenteredPos()); - unitSignalledToAttack = true; + bool shouldAttack = true; + if(unit->getType()->hasSkillClass(scHarvest)) { + shouldAttack = (attackerWorkersHarvestingCount > minWorkerAttackersHarvesting); + if(shouldAttack == false) { + attackerWorkersHarvestingCount++; + } + } + if(shouldAttack) { + //printf("~~~~~~~~ Unit [%s - %d] WILL ATTACK [%s - %d]\n",unit->getFullName().c_str(),unit->getId(),enemy->getFullName().c_str(),enemy->getId()); + aiInterface->giveCommand(i, asct_forenemy, beingAttacked.second->getCenteredPos()); + unitSignalledToAttack = true; + } } } } } - if(alreadyAttacking == false && act!=NULL && (ultraAttack || isWarrior) && + if(alreadyAttacking == false && act != NULL && (ultraAttack || isWarrior) && unitSignalledToAttack == false) { - aiInterface->giveCommand(i, act, pos); + bool shouldAttack = true; + if(unit->getType()->hasSkillClass(scHarvest)) { + shouldAttack = (attackerWorkersHarvestingCount > minWorkerAttackersHarvesting); + if(shouldAttack == false) { + attackerWorkersHarvestingCount++; + } + } + if(shouldAttack) { + aiInterface->giveCommand(i, act, pos); + } } } diff --git a/source/glest_game/ai/ai.h b/source/glest_game/ai/ai.h index 126e150f..b709deab 100644 --- a/source/glest_game/ai/ai.h +++ b/source/glest_game/ai/ai.h @@ -158,8 +158,8 @@ public: RandomGen* getRandom() {return &random;} int getCountOfType(const UnitType *ut); - int getCountOfClass(UnitClass uc); - float getRatioOfClass(UnitClass uc); + int getCountOfClass(UnitClass uc,UnitClass *additionalUnitClassToExcludeFromCount=NULL); + float getRatioOfClass(UnitClass uc,UnitClass *additionalUnitClassToExcludeFromCount=NULL); const ResourceType *getNeededResource(int unitIndex); bool isStableBase(); diff --git a/source/glest_game/ai/ai_rule.cpp b/source/glest_game/ai/ai_rule.cpp index 5d42cd2b..68394f33 100644 --- a/source/glest_game/ai/ai_rule.cpp +++ b/source/glest_game/ai/ai_rule.cpp @@ -94,8 +94,69 @@ AiRuleRepair::AiRuleRepair(Ai *ai): bool AiRuleRepair::test(){ AiInterface *aiInterface= ai->getAiInterface(); - //look for a damaged unit - for(int i=0; igetMyUnitCount(); ++i){ + const int minUnitsRepairingCastle = 7; + // look for a damaged unit and give priority to the factions bases + // (units that produce workers and store resources) + for(int i = 0; i < aiInterface->getMyUnitCount(); ++i) { + const Unit *u= aiInterface->getMyUnit(i); + //printf("\n\n\n\n!!!!!! Is damaged unit [%d - %s] u->getHpRatio() = %f, hp = %d, mapHp = %d\n",u->getId(),u->getType()->getName().c_str(),u->getHpRatio(),u->getHp(),u->getType()->getTotalMaxHp(u->getTotalUpgrade())); + if(u->getHpRatio() < 1.f) { + + bool unitCanProduceWorker = false; + for(int j = 0; unitCanProduceWorker == false && + j < u->getType()->getCommandTypeCount(); ++j) { + const CommandType *ct= u->getType()->getCommandType(j); + + //if the command is produce + if(ct->getClass() == ccProduce || ct->getClass() == ccMorph) { + const ProducibleType *pt = ct->getProduced(); + if(pt != NULL) { + const UnitType *ut = dynamic_cast(pt); + if( ut != NULL && ut->hasCommandClass(ccHarvest) == true && + ut->getStoredResourceCount() > 0) { + unitCanProduceWorker = true; + } + } + } + } + + int candidatedamagedUnitIndex=-1; + if(unitCanProduceWorker == true) { + int unitCountAlreadyRepairingDamagedUnit = 0; + // Now check if any other unit is able to repair this unit + for(int i1 = 0; i1 < aiInterface->getMyUnitCount(); ++i1) { + const Unit *u1= aiInterface->getMyUnit(i1); + const RepairCommandType *rct= static_cast(u1->getType()->getFirstCtOfClass(ccRepair)); + //if(rct) printf("\n\n\n\n^^^^^^^^^^ possible repairer unit [%d - %s] current skill [%d] can reapir damaged unit [%d]\n",u1->getId(),u1->getType()->getName().c_str(),u->getCurrSkill()->getClass(),rct->isRepairableUnitType(u->getType())); + + if(rct != NULL) { + if(u1->getCurrSkill()->getClass() == scStop || u1->getCurrSkill()->getClass() == scMove) { + if(rct->isRepairableUnitType(u->getType())) { + candidatedamagedUnitIndex= i; + //return true; + } + } + else if(u1->getCurrSkill()->getClass() == scRepair) { + Command *cmd = u1->getCurrCommand(); + if(cmd != NULL && cmd->getCommandType()->getClass() == ccRepair) { + if(cmd->getUnit() != NULL && cmd->getUnit()->getId() == u->getId()) { + unitCountAlreadyRepairingDamagedUnit++; + } + } + } + } + } + + if(candidatedamagedUnitIndex >= 0 && unitCountAlreadyRepairingDamagedUnit < minUnitsRepairingCastle) { + damagedUnitIndex = candidatedamagedUnitIndex; + return true; + } + } + } + } + + // Normal Repair checking + for(int i = 0; i < aiInterface->getMyUnitCount(); ++i) { const Unit *u= aiInterface->getMyUnit(i); //printf("\n\n\n\n!!!!!! Is damaged unit [%d - %s] u->getHpRatio() = %f, hp = %d, mapHp = %d\n",u->getId(),u->getType()->getName().c_str(),u->getHpRatio(),u->getHp(),u->getType()->getTotalMaxHp(u->getTotalUpgrade())); if(u->getHpRatio() < 1.f) { @@ -191,12 +252,13 @@ AiRuleAddTasks::AiRuleAddTasks(Ai *ai): } bool AiRuleAddTasks::test(){ - return !ai->anyTask() || ai->getCountOfClass(ucWorker)<4; + return !ai->anyTask() || ai->getCountOfClass(ucWorker) < 4; } void AiRuleAddTasks::execute(){ int buildingCount= ai->getCountOfClass(ucBuilding); - int warriorCount= ai->getCountOfClass(ucWarrior); + UnitClass ucWorkerType = ucWorker; + int warriorCount= ai->getCountOfClass(ucWarrior,&ucWorkerType); int workerCount= ai->getCountOfClass(ucWorker); int upgradeCount= ai->getAiInterface()->getMyUpgradeCount(); @@ -632,7 +694,7 @@ void AiRuleProduce::produceSpecific(const ProduceTask *pt){ if( aiInterface->getMyUnit(bestIndex)->getCommandSize() > 2) { // maybe we need another producer of this kind if possible! if(aiInterface->reqsOk(aiInterface->getMyUnit(bestIndex)->getType())) { - if(ai->getCountOfClass(ucBuilding)>5) + if(ai->getCountOfClass(ucBuilding) > 5) ai->addTask(new BuildTask(aiInterface->getMyUnit(bestIndex)->getType())); } // need to calculate another producer, maybe its better to produce another warrior with another producer