-
t800
- Ветеран
-
- Сообщения: 982
- Зарегистрирован: 22 июл 2015, 11:36
- Пол:
- Награды: 4
-
- Поблагодарили: 191 раз.
-
|
t800 » 31 окт 2015, 10:39
Я сейчас посмотрел ИИ он очень простой наверное потому компьютер и играет так глупо. ai_kingdom.cpp - Код: Выделить всё
/******************************************************************************** * Copyright (C) 2010 by Andrey Afletdinov <fheroes2@gmail.com> * * All rights reserved. * * * * Part of the Free Heroes2 Engine: * * http://sourceforge.net/projects/fheroes2 * * * * Redistribution and use in source and binary forms, with or without * * modification, are permitted provided that the following conditions * * are met: * * - Redistributions may not be sold, nor may they be used in a * * commercial product or activity. * * - Redistributions of source code and/or in binary form must reproduce * * the above copyright notice, this list of conditions and the * * following disclaimer in the documentation and/or other materials * * provided with the distribution. * * * * THIS SOFTWARE IS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, * * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS * * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE * * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, * * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * *******************************************************************************/ #include <functional> #include <algorithm> #include "game.h" #include "cursor.h" #include "engine.h" #include "game_interface.h" #include "interface_gamearea.h" #include "heroes.h" #include "castle.h" #include "world.h" #include "settings.h" #include "kingdom.h" #include "agg.h" #include "ai.h" #include "ai_simple.h" void AICastleTurn(Castle*); void AIHeroesTurn(Heroes*); void AIHeroesEnd(Heroes*); void AIHeroesCaptureNearestTown(Heroes*); void AIHeroesSetHunterWithTarget(Heroes*, s32); AIKingdoms & AIKingdoms::Get(void) { static AIKingdoms ai_kingdoms; return ai_kingdoms; } AIKingdom & AIKingdoms::Get(int color) { return AIKingdoms::Get().at(Color::GetIndex(color)); } void AIKingdoms::Reset(void) { AIKingdoms & ai = AIKingdoms::Get(); std::for_each(ai.begin(), ai.end(), std::mem_fun_ref(&AIKingdom::Reset)); } void AIKingdom::Reset(void) { capital = NULL; scans.clear(); } void IndexObjectMap::DumpObjects(const IndexDistance & id) { IndexObjectMap::const_iterator it = find(id.first); if(it != end()) DEBUG(DBG_AI, DBG_TRACE, MP2::StringObject((*it).second) \ << ", maps index: " << id.first << ", dist: " << id.second); } void WorldStoreObjects(int color, IndexObjectMap & store) { for(s32 it = 0; it < world.w() * world.h(); ++it) { const Maps::Tiles & tile = world.GetTiles(it); if(tile.isFog(color)) continue; if(MP2::isGroundObject(tile.GetObject()) || MP2::isWaterObject(tile.GetObject()) || MP2::OBJ_HEROES == tile.GetObject()) { // if quantity object is empty if(MP2::isQuantityObject(tile.GetObject()) && ! MP2::isPickupObject(tile.GetObject()) && ! tile.QuantityIsValid()) continue; // skip captured obj if(MP2::isCaptureObject(tile.GetObject()) && Players::isFriends(color, tile.QuantityColor())) continue; // skip for meeting heroes if(MP2::OBJ_HEROES == tile.GetObject()) { const Heroes* hero = tile.GetHeroes(); if(hero && color == hero->GetColor()) continue; } // check: is visited objects switch(tile.GetObject()) { case MP2::OBJ_MAGELLANMAPS: case MP2::OBJ_OBSERVATIONTOWER: if(world.GetKingdom(color).isVisited(tile)) continue; break; default: break; } store[it] = tile.GetObject(); } } } void AI::KingdomTurn(Kingdom & kingdom) { KingdomHeroes & heroes = kingdom.GetHeroes(); KingdomCastles & castles = kingdom.GetCastles(); const int color = kingdom.GetColor(); if(kingdom.isLoss() || color == Color::NONE) { kingdom.LossPostActions(); return; } DEBUG(DBG_AI, DBG_INFO, Color::String(kingdom.GetColor()) << " funds: " << kingdom.GetFunds().String()); if(! Settings::Get().MusicMIDI()) AGG::PlayMusic(MUS::COMPUTER); Interface::StatusWindow & status = Interface::Basic::Get().GetStatusWindow(); AIKingdom & ai = AIKingdoms::Get(color); // turn indicator status.RedrawTurnProgress(0); // scan map ai.scans.clear(); WorldStoreObjects(color, ai.scans); DEBUG(DBG_AI, DBG_INFO, Color::String(color) << ", size cache objects: " << ai.scans.size()); // set capital if(NULL == ai.capital && castles.size()) { KingdomCastles::iterator it = std::find_if(castles.begin(), castles.end(), Castle::PredicateIsCastle); if(castles.end() != it) { if(*it) { ai.capital = *it; ai.capital->SetModes(Castle::CAPITAL); } } else // first town { ai.capital = castles.front(); ai.capital->SetModes(Castle::CAPITAL); } } // turn indicator status.RedrawTurnProgress(1); // castles AI turn std::for_each(castles.begin(), castles.end(), AICastleTurn); // need capture town? if(castles.empty()) std::for_each(heroes.begin(), heroes.end(), AIHeroesCaptureNearestTown); // buy hero in capital if(ai.capital && ai.capital->isCastle()) { u32 modes = 0; const u32 maxhero = Maps::XLARGE > world.w() ? (Maps::LARGE > world.w() ? 3 : 2) : 4; if(heroes.empty()) modes = AI::HEROES_HUNTER|AI::HEROES_SCOUTER; else if(heroes.size() < maxhero || 0 == std::count_if(heroes.begin(), heroes.end(), std::bind2nd(std::mem_fun(&Heroes::Modes), AI::HEROES_SCOUTER))) modes = AI::HEROES_SCOUTER; if(modes && heroes.size() < Kingdom::GetMaxHeroes()) { Recruits & rec = kingdom.GetRecruits(); Heroes* hero = ai.capital->GetHeroes().Guest(); if(!hero) { if(rec.GetHero1() && rec.GetHero2()) hero = ai.capital->RecruitHero(rec.GetHero1()->GetLevel() >= rec.GetHero2()->GetLevel() ? rec.GetHero1() : rec.GetHero2()); else if(rec.GetHero1()) hero = ai.capital->RecruitHero(rec.GetHero1()); else if(rec.GetHero2()) hero = ai.capital->RecruitHero(rec.GetHero2()); if(hero) hero->SetModes(modes); } } } // set hunters if(ai.capital) { const size_t hunters = std::count_if(heroes.begin(), heroes.end(), std::bind2nd(std::mem_fun(&Heroes::Modes), AI::HEROES_HUNTER)); // every time if(0 == hunters && heroes.size()) { KingdomHeroes::iterator it = std::find_if(heroes.begin(), heroes.end(), std::not1(std::bind2nd(std::mem_fun(&Heroes::Modes), Heroes::PATROL))); if(it != heroes.end() && ! ai.capital->GetHeroes().Guest()) AIHeroesSetHunterWithTarget((*it), ai.capital->GetIndex()); } else // each month if(world.BeginMonth() && 1 < world.CountDay()) { KingdomHeroes::iterator it = std::find_if(heroes.begin(), heroes.end(), std::bind2nd(std::mem_fun(&Heroes::Modes), AI::HEROES_HUNTER)); if(it != heroes.end() && ! ai.capital->GetHeroes().Guest()) AIHeroesSetHunterWithTarget(*it, ai.capital->GetIndex()); } } // update roles { std::for_each(heroes.begin(), heroes.end(), std::bind2nd(std::mem_fun(&Heroes::ResetModes), AI::HEROES_STUPID|AI::HEROES_WAITING)); // init roles if(heroes.end() != std::find_if(heroes.begin(), heroes.end(), std::not1(std::bind2nd(std::mem_fun(&Heroes::Modes), AI::HEROES_SCOUTER|AI::HEROES_HUNTER)))) { KingdomHeroes::iterator ith, first = heroes.end(); while(heroes.end() != (ith = std::find_if(heroes.begin(), heroes.end(), std::not1(std::bind2nd(std::mem_fun(&Heroes::Modes), // also skip patrol AI::HEROES_HUNTER|AI::HEROES_SCOUTER|Heroes::PATROL))))) { if(first == heroes.end()) { first = ith; if(*ith) (*ith)->SetModes(AI::HEROES_HUNTER|AI::HEROES_SCOUTER); } else if(*ith) (*ith)->SetModes(AI::HEROES_SCOUTER); } } } // turn indicator status.RedrawTurnProgress(2); // heroes turns std::for_each(heroes.begin(), heroes.end(), std::ptr_fun(&AIHeroesTurn)); //std::for_each(heroes.begin(), heroes.end(), std::bind2nd(std::mem_fun(&Heroes::ResetModes), AI::HEROES_STUPID|AI::HEROES_WAITING)); std::for_each(heroes.begin(), heroes.end(), std::ptr_fun(&AIHeroesTurn)); std::for_each(heroes.begin(), heroes.end(), std::ptr_fun(&AIHeroesEnd)); // turn indicator status.RedrawTurnProgress(9); DEBUG(DBG_AI, DBG_INFO, Color::String(color) << " moved"); }
ai_battle.cpp - Код: Выделить всё
/******************************************************************************** * Copyright (C) 2010 by Andrey Afletdinov <fheroes2@gmail.com> * * All rights reserved. * * * * Part of the Free Heroes2 Engine: * * http://sourceforge.net/projects/fheroes2 * * * * Redistribution and use in source and binary forms, with or without * * modification, are permitted provided that the following conditions * * are met: * * - Redistributions may not be sold, nor may they be used in a * * commercial product or activity. * * - Redistributions of source code and/or in binary form must reproduce * * the above copyright notice, this list of conditions and the * * following disclaimer in the documentation and/or other materials * * provided with the distribution. * * * * THIS SOFTWARE IS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, * * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS * * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE * * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, * * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * *******************************************************************************/ #include <algorithm> #include <functional> #include "settings.h" #include "heroes.h" #include "castle.h" #include "speed.h" #include "battle_arena.h" #include "battle_cell.h" #include "battle_troop.h" #include "battle_interface.h" #include "battle_command.h" #include "ai_simple.h" namespace Battle { bool AIApplySpell(const Spell &, const Unit*, const HeroBase &, Actions &); s32 AIShortDistance(s32, const Indexes &); s32 AIAttackPosition(Arena &, const Unit &, const Indexes &); s32 AIMaxQualityPosition(const Indexes &); const Unit* AIGetEnemyAbroadMaxQuality(s32, int color); const Unit* AIGetEnemyAbroadMaxQuality(const Unit &); s32 AIAreaSpellDst(const HeroBase &); bool MaxDstCount(const std::pair<s32, u32> & p1, const std::pair<s32, u32> & p2) { return p1.second < p2.second; } } s32 Battle::AIAreaSpellDst(const HeroBase & hero) { std::map<s32, u32> dstcount; Arena* arena = GetArena(); Units enemies(arena->GetForce(hero.GetColor(), true), true); for(Units::const_iterator it1 = enemies.begin(); it1 != enemies.end(); ++it1) { const Indexes around = Board::GetAroundIndexes(**it1); for(Indexes::const_iterator it2 = around.begin(); it2 != around.end(); ++it2) dstcount[*it2] += 1; } // find max std::map<s32, u32>::const_iterator max = std::max_element(dstcount.begin(), dstcount.end(), MaxDstCount); return max != dstcount.end() ? (*max).first : -1; } s32 Battle::AIMaxQualityPosition(const Indexes & positions) { s32 res = -1; for(Indexes::const_iterator it = positions.begin(); it != positions.end(); ++it) if(Board::isValidIndex(*it)) { if(res < 0) res = *it; else if(Board::GetCell(res)->GetQuality() < Board::GetCell(*it)->GetQuality()) res = *it; } return res; } const Battle::Unit* Battle::AIGetEnemyAbroadMaxQuality(s32 position, int color) { const Unit* res = NULL; s32 quality = 0; const Indexes around = Board::GetAroundIndexes(position); for(Indexes::const_iterator it = around.begin(); it != around.end(); ++it) { const Cell* cell = Board::GetCell(*it); const Unit* enemy = cell ? cell->GetUnit() : NULL; if(enemy && enemy->GetColor() != color && quality < cell->GetQuality()) { res = enemy; quality = cell->GetQuality(); } } return res; } const Battle::Unit* Battle::AIGetEnemyAbroadMaxQuality(const Unit & b) { const Unit* res1 = AIGetEnemyAbroadMaxQuality(b.GetHeadIndex(), b.GetColor()); if(b.isWide()) { const Unit* res2 = AIGetEnemyAbroadMaxQuality(b.GetTailIndex(), b.GetColor()); if(!res1) return res2; else if(!res2) return res1; else { const s32 & quality1 = res1->GetPosition().GetHead()->GetQuality(); const s32 & quality2 = res2->GetPosition().GetHead()->GetQuality(); return quality1 > quality2 ? res1 : res2; } } return res1; } s32 Battle::AIShortDistance(s32 from, const Indexes & indexes) { u32 len = MAXU16; s32 res = -1; for(Indexes::const_iterator it = indexes.begin(); it != indexes.end(); ++it) { const u32 length = Board::GetDistance(from, *it); if(len > length) { len = length; res = *it; } } DEBUG(DBG_BATTLE, DBG_TRACE, res); return res; } s32 Battle::AIAttackPosition(Arena & arena, const Unit & b, const Indexes & positions) { s32 res = -1; if(b.isMultiCellAttack()) { res = AIMaxQualityPosition(positions); } else if(b.isDoubleCellAttack()) { Indexes results; results.reserve(12); const Units enemies(arena.GetForce(b.GetColor(), true), true); if(1 < enemies.size()) { for(Units::const_iterator it1 = enemies.begin(); it1 != enemies.end(); ++it1) { const Indexes around = Board::GetAroundIndexes(**it1); for(Indexes::const_iterator it2 = around.begin(); it2 != around.end(); ++it2) { const Unit* unit = Board::GetCell(*it2)->GetUnit(); if(unit && enemies.end() != std::find(enemies.begin(), enemies.end(), unit)) results.push_back(*it2); } } if(results.size()) { // find passable results Indexes passable = Arena::GetBoard()->GetPassableQualityPositions(b); Indexes::iterator it2 = results.begin(); for(Indexes::const_iterator it = results.begin(); it != results.end(); ++it) if(passable.end() != std::find(passable.begin(), passable.end(), *it)) *it2++ = *it; if(it2 != results.end()) results.resize(std::distance(results.begin(), it2)); // get max quality if(results.size()) res = AIMaxQualityPosition(results); } } } return 0 > res ? AIShortDistance(b.GetHeadIndex(), positions) : res; } using namespace Battle; void AI::BattleTurn(Arena & arena, const Unit & b, Actions & a) { Board* board = Arena::GetBoard(); // reset quality param for board board->Reset(); // set quality for enemy troop board->SetEnemyQuality(b); const Unit* enemy = NULL; bool attack = false; if(b.isArchers() && !b.isHandFighting()) { enemy = arena.GetEnemyMaxQuality(b.GetColor()); if(BattleMagicTurn(arena, b, a, enemy)) return; /* repeat turn: correct spell ability */ attack = true; } else if(b.isHandFighting()) { enemy = AIGetEnemyAbroadMaxQuality(b); if(BattleMagicTurn(arena, b, a, enemy)) return; /* repeat turn: correct spell ability */ attack = true; } else { s32 move = -1; if(b.Modes(SP_BERSERKER)) { const Indexes positions = board->GetNearestTroopIndexes(b.GetHeadIndex(), NULL); if(positions.size()) move = *Rand::Get(positions); } else { if(BattleMagicTurn(arena, b, a, NULL)) return; /* repeat turn: correct spell ability */ // set quality position from enemy board->SetPositionQuality(b); // get passable quality positions const Indexes positions = board->GetPassableQualityPositions(b); attack = true; if(positions.size()) move = AIAttackPosition(arena, b, positions); } if(Board::isValidIndex(move)) { if(b.isFly()) { enemy = AIGetEnemyAbroadMaxQuality(move, b.GetColor()); if(BattleMagicTurn(arena, b, a, enemy)) return; /* repeat turn: correct spell ability */ a.push_back(Battle::Command(MSG_BATTLE_MOVE, b.GetUID(), move)); attack = true; } else { Position dst = Position::GetCorrect(b, move); Indexes path = arena.GetPath(b, dst); if(path.empty()) { const u32 direction = b.GetPosition().GetHead()->GetPos().x > dst.GetHead()->GetPos().x ? RIGHT : LEFT; // find near position while(path.empty() && Board::isValidDirection(dst.GetHead()->GetIndex(), direction)) { const s32 pos = Board::GetIndexDirection(dst.GetHead()->GetIndex(), direction); if(b.GetHeadIndex() == pos) break; dst.Set(pos, b.isWide(), direction == RIGHT); path = arena.GetPath(b, dst); } } if(path.size()) { if(b.isWide()) { const s32 head = dst.GetHead()->GetIndex(); const s32 tail = dst.GetTail()->GetIndex(); if(path.back() == head || path.back() == tail) { enemy = AIGetEnemyAbroadMaxQuality(head, b.GetColor()); if(!enemy) enemy = AIGetEnemyAbroadMaxQuality(tail, b.GetColor()); } } if(! enemy) enemy = AIGetEnemyAbroadMaxQuality(path.back(), b.GetColor()); a.push_back(Battle::Command(MSG_BATTLE_MOVE, b.GetUID(), path.back())); // archers move and short attack only attack = b.isArchers() ? false : true; } } } else enemy = AIGetEnemyAbroadMaxQuality(b); } if(enemy) { if(attack) a.push_back(Battle::Command(MSG_BATTLE_ATTACK, b.GetUID(), enemy->GetUID(), enemy->GetHeadIndex(), 0)); } else { DEBUG(DBG_BATTLE, DBG_TRACE, "enemy: " << "is NULL" << ", board: " << board->AllUnitsInfo()); } // end action a.push_back(Battle::Command(MSG_BATTLE_END_TURN, b.GetUID())); } bool AI::BattleMagicTurn(Arena & arena, const Unit & b, Actions & a, const Unit* enemy) { const HeroBase* hero = b.GetCommander(); if(b.Modes(SP_BERSERKER) || !hero || hero->Modes(Heroes::SPELLCASTED) || !hero->HaveSpellBook() || arena.isDisableCastSpell(Spell(), NULL) || a.HaveCommand(Battle::MSG_BATTLE_CAST)) return false; const Force & my_army = arena.GetForce(b.GetArmyColor(), false); const Force & enemy_army = arena.GetForce(b.GetArmyColor(), true); Units friends(my_army, true); Units enemies(enemy_army, true); // sort strongest friends.SortStrongest(); // troop bad spell - clean { // sort strongest Units::iterator it = std::find_if(friends.begin(), friends.end(), std::bind2nd(std::mem_fun(&Unit::Modes), IS_BAD_MAGIC)); if(it != friends.end()) { if(AIApplySpell(Spell::DISPEL, *it, *hero, a)) return true; if(AIApplySpell(Spell::CURE, *it, *hero, a)) return true; } } // area damage spell { const u8 areasp[] = { Spell::METEORSHOWER, Spell::FIREBLAST, Spell::CHAINLIGHTNING, Spell::FIREBALL, Spell::COLDRING }; s32 dst = AIAreaSpellDst(*hero); if(Board::isValidIndex(dst)) for(u32 ii = 0; ii < ARRAY_COUNT(areasp); ++ii) { if(hero->CanCastSpell(areasp[ii])) { a.push_back(Battle::Command(MSG_BATTLE_CAST, areasp[ii], dst)); return true; } } } // if handfighting if(enemy) { // kill dragons if(enemy->isDragons() && !b.Modes(SP_DRAGONSLAYER) && AIApplySpell(Spell::DRAGONSLAYER, &b, *hero, a)) return true; // curse if(!enemy->Modes(SP_CURSE) && AIApplySpell(Spell::CURSE, enemy, *hero, a)) return true; // enemy good spell - clean if(enemy->Modes(IS_GOOD_MAGIC) && AIApplySpell(Spell::DISPEL, enemy, *hero, a)) return true; // up defense if(!b.Modes(SP_STEELSKIN) && !b.Modes(SP_STONESKIN) && AIApplySpell(Spell::STEELSKIN, &b, *hero, a)) return true; if(!b.Modes(SP_STONESKIN) && !b.Modes(SP_STEELSKIN) && AIApplySpell(Spell::STONESKIN, &b, *hero, a)) return true; } // my army blessing if(b.isArchers()) { if(!b.Modes(SP_BLESS) && AIApplySpell(Spell::BLESS, &b, *hero, a)) return true; if(!b.Modes(SP_BLOODLUST) && AIApplySpell(Spell::BLOODLUST, &b, *hero, a)) return true; } // up speed if(hero->HaveSpell(Spell::HASTE) && !enemy) { // sort strongest Units::iterator it = std::find_if(friends.begin(), friends.end(), std::not1(std::bind2nd(std::mem_fun(&Unit::Modes), SP_HASTE))); if(it != friends.end() && AIApplySpell(Spell::HASTE, *it, *hero, a)) return true; } // shield spell conditions { Units::iterator it = std::find_if(enemies.begin(), enemies.end(), std::mem_fun(&Unit::isArchers)); const Castle* castle = Arena::GetCastle(); // find enemy archers if(it != enemies.end() || // or archers tower (castle && castle->GetColor() != b.GetColor() && castle->isCastle())) { // find strongest archers for(it = friends.begin(); it != friends.end(); ++it) if((*it)->isArchers() && ! (*it)->Modes(SP_SHIELD)) break; // or other strongest friends if(it == friends.end()) it = std::find_if(friends.begin(), friends.end(), std::not1(std::bind2nd(std::mem_fun(&Unit::Modes), SP_SHIELD))); if(it != friends.end() && AIApplySpell(Spell::SHIELD, *it, *hero, a)) return true; } } // enemy army spell { // find mirror image or summon elem Units::iterator it = std::find_if(enemies.begin(), enemies.end(), std::bind2nd(std::mem_fun(&Unit::Modes), CAP_MIRRORIMAGE | CAP_SUMMONELEM)); if(it != enemies.end()) { if(AIApplySpell(Spell::ARROW, *it, *hero, a)) return true; if(AIApplySpell(Spell::LIGHTNINGBOLT, *it, *hero, a)) return true; } // find good magic it = std::find_if(enemies.begin(), enemies.end(), std::bind2nd(std::mem_fun(&Unit::Modes), IS_GOOD_MAGIC)); if(it != enemies.end()) { // slow if((*it)->Modes(SP_HASTE) && AIApplySpell(Spell::SLOW, *it, *hero, a)) return true; // curse if((*it)->Modes(SP_CURSE) && AIApplySpell(Spell::CURSE, *it, *hero, a)) return true; // if(AIApplySpell(Spell::DISPEL, *it, *hero, a)) return true; } // check undead if(std::count_if(friends.begin(), friends.end(), std::mem_fun(&Unit::isUndead)) < std::count_if(enemies.begin(), enemies.end(), std::mem_fun(&Unit::isUndead))) { if(AIApplySpell(Spell::HOLYSHOUT, NULL, *hero, a)) return true; if(AIApplySpell(Spell::HOLYWORD, NULL, *hero, a)) return true; } // check alife if(std::count_if(friends.begin(), friends.end(), std::mem_fun(&Unit::isAlive)) < std::count_if(enemies.begin(), enemies.end(), std::mem_fun(&Unit::isAlive))) { if(AIApplySpell(Spell::DEATHRIPPLE, NULL, *hero, a)) return true; if(AIApplySpell(Spell::DEATHWAVE, NULL, *hero, a)) return true; } Unit* stats = *Rand::Get(enemies); if(AIApplySpell(Spell::LIGHTNINGBOLT, stats, *hero, a)) return true; if(AIApplySpell(Spell::ARROW, stats, *hero, a)) return true; } /* FIX: Damage Spell: */ if(AIApplySpell(Spell::ARMAGEDDON, NULL, *hero, a)) return true; if(AIApplySpell(Spell::ELEMENTALSTORM, NULL, *hero, a)) return true; return false; } bool Battle::AIApplySpell(const Spell & spell, const Unit* b, const HeroBase & hero, Actions & a) { u32 mass = Spell::NONE; switch(spell()) { case Spell::CURE: mass = Spell::MASSCURE; break; case Spell::HASTE: mass = Spell::MASSHASTE; break; case Spell::SLOW: mass = Spell::MASSSLOW; break; case Spell::BLESS: mass = Spell::MASSBLESS; break; case Spell::CURSE: mass = Spell::MASSCURSE; break; case Spell::DISPEL: mass = Spell::MASSDISPEL; break; case Spell::SHIELD: mass = Spell::MASSSHIELD; break; default: break; } if(mass != Spell::NONE && AIApplySpell(mass, b, hero, a)) return true; if(hero.CanCastSpell(spell) && (!b || b->AllowApplySpell(spell, &hero))) { a.push_back(Battle::Command(MSG_BATTLE_CAST, spell(), (b ? b->GetHeadIndex() : -1))); return true; } return false; }
ai_heroes.cpp - Код: Выделить всё
/******************************************************************************** * Copyright (C) 2010 by Andrey Afletdinov <fheroes2@gmail.com> * * All rights reserved. * * * * Part of the Free Heroes2 Engine: * * http://sourceforge.net/projects/fheroes2 * * * * Redistribution and use in source and binary forms, with or without * * modification, are permitted provided that the following conditions * * are met: * * - Redistributions may not be sold, nor may they be used in a * * commercial product or activity. * * - Redistributions of source code and/or in binary form must reproduce * * the above copyright notice, this list of conditions and the * * following disclaimer in the documentation and/or other materials * * provided with the distribution. * * * * THIS SOFTWARE IS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, * * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS * * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE * * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, * * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * *******************************************************************************/ #include <functional> #include <algorithm> #include "settings.h" #include "kingdom.h" #include "castle.h" #include "army.h" #include "battle.h" #include "luck.h" #include "morale.h" #include "race.h" #include "difficulty.h" #include "world.h" #include "payment.h" #include "heroes.h" #include "cursor.h" #include "game_interface.h" #include "interface_gamearea.h" #include "maps_tiles.h" #include "ai_simple.h" #define HERO_MAX_SHEDULED_TASK 7 AIHeroes & AIHeroes::Get(void) { static AIHeroes ai_heroes; return ai_heroes; } AIHero & AIHeroes::Get(const Heroes & ht) { return AIHeroes::Get().at(ht.GetID()); } void AIHeroes::Reset(void) { AIHeroes & ai = AIHeroes::Get(); std::for_each(ai.begin(), ai.end(), std::mem_fun_ref(&AIHero::Reset)); } void AIHero::Reset(void) { primary_target = -1; sheduled_visit.clear(); fix_loop = 0; } bool AI::HeroesSkipFog(void) { return false; } void AI::HeroesActionComplete(Heroes &, s32) { } std::string AI::HeroesString(const Heroes & hero) { std::ostringstream os; AIHero & ai_hero = AIHeroes::Get(hero); Queue & task = ai_hero.sheduled_visit; os << "flags : " << (hero.Modes(AI::HEROES_SCOUTER) ? "SCOUTER," : "") << (hero.Modes(AI::HEROES_HUNTER) ? "HUNTER," : "") << (hero.Modes(AI::HEROES_WAITING) ? "WAITING," : "") << (hero.Modes(AI::HEROES_STUPID) ? "STUPID" : "") << std::endl; os << "ai primary target: " << ai_hero.primary_target << std::endl << "ai sheduled visit: "; for(Queue::const_iterator it = task.begin(); it != task.end(); ++it) os << *it << "(" << MP2::StringObject(world.GetTiles(*it).GetObject()) << "), "; os << std::endl; return os.str(); } void AI::HeroesPostLoad(Heroes & hero) { hero.SetModes(AI::HEROES_HUNTER); } void AI::HeroesLevelUp(Heroes & hero) { if(4 < hero.GetLevel() && !hero.Modes(AI::HEROES_HUNTER)) hero.SetModes(AI::HEROES_HUNTER); if(9 < hero.GetLevel() && hero.Modes(AI::HEROES_SCOUTER)) hero.ResetModes(AI::HEROES_SCOUTER); } void AI::HeroesPreBattle(HeroBase & hero) { Castle* castle = world.GetCastle(hero.GetCenter()); if(castle && hero.GetType() != HeroBase::CAPTAIN) hero.GetArmy().JoinTroops(castle->GetArmy()); } void AI::HeroesAfterBattle(HeroBase & hero) { } void AI::HeroesClearTask(const Heroes & hero) { AIHeroes::Get(hero).ClearTasks(); } bool AIHeroesValidObject2(const Heroes* hero, s32 index) { const Heroes & hero2 = *hero; return AI::HeroesValidObject(hero2, index); } // get priority object for AI independent of distance (1 day) bool AIHeroesPriorityObject(const Heroes & hero, s32 index) { Maps::Tiles & tile = world.GetTiles(index); if(MP2::OBJ_CASTLE == tile.GetObject()) { const Castle* castle = world.GetCastle(Maps::GetPoint(index)); if(castle) { if(hero.GetColor() == castle->GetColor()) { // maybe need join army return hero.Modes(AI::HEROES_HUNTER) && castle->GetArmy().isValid() && ! hero.isVisited(world.GetTiles(castle->GetIndex())); } else if(! hero.isFriends(castle->GetColor())) return AI::HeroesValidObject(hero, index); } } else if(MP2::OBJ_HEROES == tile.GetObject()) { // kill enemy hero const Heroes* hero2 = tile.GetHeroes(); return hero2 && ! hero.isFriends(hero2->GetColor()) && AI::HeroesValidObject(hero, index); } switch(tile.GetObject()) { case MP2::OBJ_MONSTER: case MP2::OBJ_SAWMILL: case MP2::OBJ_MINES: case MP2::OBJ_ALCHEMYLAB: case MP2::OBJ_ARTIFACT: case MP2::OBJ_RESOURCE: case MP2::OBJ_CAMPFIRE: case MP2::OBJ_TREASURECHEST: return AI::HeroesValidObject(hero, index); default: break; } return false; } s32 FindUncharteredTerritory(Heroes & hero, u32 scoute) { Maps::Indexes v = Maps::GetAroundIndexes(hero.GetIndex(), scoute, true); Maps::Indexes res; v.resize(std::distance(v.begin(), std::remove_if(v.begin(), v.end(), std::ptr_fun(&Maps::TileIsUnderProtection)))); #if defined(ANDROID) const MapsIndexes::const_reverse_iterator crend = v.rend(); for(MapsIndexes::const_reverse_iterator it = v.rbegin(); it != crend && res.size() < 4; ++it) #else for(MapsIndexes::const_reverse_iterator it = v.rbegin(); it != v.rend() && res.size() < 4; ++it) #endif { // find fogs if(world.GetTiles(*it).isFog(hero.GetColor()) && world.GetTiles(*it).isPassable(&hero, Direction::CENTER, true) && hero.GetPath().Calculate(*it)) res.push_back(*it); } const s32 result = res.size() ? *Rand::Get(res) : -1; if(0 <= result) { DEBUG(DBG_AI, DBG_INFO, Color::String(hero.GetColor()) << ", hero: " << hero.GetName() << ", added task: " << result); } return result; } s32 GetRandomHeroesPosition(Heroes & hero, u32 scoute) { Maps::Indexes v = Maps::GetAroundIndexes(hero.GetIndex(), scoute, true); Maps::Indexes res; v.resize(std::distance(v.begin(), std::remove_if(v.begin(), v.end(), std::ptr_fun(&Maps::TileIsUnderProtection)))); #if defined(ANDROID) const MapsIndexes::const_reverse_iterator crend = v.rend(); for(MapsIndexes::const_reverse_iterator it = v.rbegin(); it != crend && res.size() < 4; ++it) #else for(MapsIndexes::const_reverse_iterator it = v.rbegin(); it != v.rend() && res.size() < 4; ++it) #endif { if(world.GetTiles(*it).isPassable(&hero, Direction::CENTER, true) && hero.GetPath().Calculate(*it)) res.push_back(*it); } const s32 result = res.size() ? *Rand::Get(res) : -1; if(0 <= result) { DEBUG(DBG_AI, DBG_INFO, Color::String(hero.GetColor()) << ", hero: " << hero.GetName() << ", added task: " << result); } return result; } void AIHeroesAddedRescueTask(Heroes & hero) { AIHero & ai_hero = AIHeroes::Get(hero); Queue & task = ai_hero.sheduled_visit; DEBUG(DBG_AI, DBG_TRACE, hero.GetName()); u32 scoute = hero.GetScoute(); switch(Settings::Get().GameDifficulty()) { case Difficulty::NORMAL: scoute += 2; break; case Difficulty::HARD: scoute += 3; break; case Difficulty::EXPERT: scoute += 4; break; case Difficulty::IMPOSSIBLE:scoute += 6; break; default: break; } // find unchartered territory s32 index = FindUncharteredTerritory(hero, scoute); const Maps::Tiles & tile = world.GetTiles(hero.GetIndex()); if(index < 0) { // check teleports if(MP2::OBJ_STONELIGHTS == tile.GetObject(false) || MP2::OBJ_WHIRLPOOL == tile.GetObject(false)) { AI::HeroesAction(hero, hero.GetIndex()); } else { // random index = GetRandomHeroesPosition(hero, scoute); } } if(0 <= index) task.push_back(index); } void AIHeroesAddedTask(Heroes & hero) { AIHero & ai_hero = AIHeroes::Get(hero); AIKingdom & ai_kingdom = AIKingdoms::Get(hero.GetColor()); Queue & task = ai_hero.sheduled_visit; IndexObjectMap & ai_objects = ai_kingdom.scans; // load minimal distance tasks std::vector<IndexDistance> objs; objs.reserve(ai_objects.size()); for(std::map<s32, int>::const_iterator it = ai_objects.begin(); it != ai_objects.end(); ++it) { const Maps::Tiles & tile = world.GetTiles((*it).first); if(hero.isShipMaster()) { if(MP2::OBJ_COAST != tile.GetObject() && ! tile.isWater()) continue; // check previous positions if(MP2::OBJ_COAST == (*it).second && hero.isVisited(world.GetTiles((*it).first))) continue; } else { if(tile.isWater() && MP2::OBJ_BOAT != tile.GetObject()) continue; } objs.push_back(IndexDistance((*it).first, Maps::GetApproximateDistance(hero.GetIndex(), (*it).first))); } DEBUG(DBG_AI, DBG_INFO, Color::String(hero.GetColor()) << ", hero: " << hero.GetName() << ", task prepare: " << objs.size()); std::sort(objs.begin(), objs.end(), IndexDistance::Shortest); for(std::vector<IndexDistance>::const_iterator it = objs.begin(); it != objs.end(); ++it) { if(task.size() >= HERO_MAX_SHEDULED_TASK) break; const bool validobj = AI::HeroesValidObject(hero, (*it).first); if(validobj && hero.GetPath().Calculate((*it).first)) { DEBUG(DBG_AI, DBG_INFO, Color::String(hero.GetColor()) << ", hero: " << hero.GetName() << ", added tasks: " << MP2::StringObject(ai_objects[(*it).first]) << ", index: " << (*it).first << ", distance: " << (*it).second); task.push_back((*it).first); ai_objects.erase((*it).first); } else { DEBUG(DBG_AI, DBG_TRACE, Color::String(hero.GetColor()) << ", hero: " << hero.GetName() << (!validobj ? ", invalid: " : ", impossible: ") << MP2::StringObject(ai_objects[(*it).first]) << ", index: " << (*it).first << ", distance: " << (*it).second); } } if(task.empty()) AIHeroesAddedRescueTask(hero); } void AI::HeroesActionNewPosition(Heroes & hero) { AIHero & ai_hero = AIHeroes::Get(hero); //AIKingdom & ai_kingdom = AIKingdoms::Get(hero.GetColor()); Queue & task = ai_hero.sheduled_visit; const u8 objs[] = { MP2::OBJ_ARTIFACT, MP2::OBJ_RESOURCE, MP2::OBJ_CAMPFIRE, MP2::OBJ_TREASURECHEST, 0 }; Maps::Indexes pickups = Maps::ScanAroundObjects(hero.GetIndex(), objs); if(pickups.size() && hero.GetPath().isValid() && pickups.end() == std::find(pickups.begin(), pickups.end(), hero.GetPath().GetDestinationIndex())) hero.GetPath().Reset(); for(MapsIndexes::const_iterator it = pickups.begin(); it != pickups.end(); ++it) if(*it != hero.GetPath().GetDestinationIndex()) task.push_front(*it); } bool AI::HeroesGetTask(Heroes & hero) { std::vector<s32> results; results.reserve(5); const Settings & conf = Settings::Get(); AIHero & ai_hero = AIHeroes::Get(hero); AIKingdom & ai_kingdom = AIKingdoms::Get(hero.GetColor()); Queue & task = ai_hero.sheduled_visit; IndexObjectMap & ai_objects = ai_kingdom.scans; const u8 objs1[] = { MP2::OBJ_ARTIFACT, MP2::OBJ_RESOURCE, MP2::OBJ_CAMPFIRE, MP2::OBJ_TREASURECHEST, 0 }; const u8 objs2[] = { MP2::OBJ_SAWMILL, MP2::OBJ_MINES, MP2::OBJ_ALCHEMYLAB, 0 }; const u8 objs3[] = { MP2::OBJ_CASTLE, MP2::OBJ_HEROES, MP2::OBJ_MONSTER, 0 }; // rescan path hero.RescanPath(); Castle* castle = hero.inCastle(); // if hero in castle if(castle) { DEBUG(DBG_AI, DBG_TRACE, hero.GetName() << ", in castle"); castle->RecruitAllMonster(); hero.GetArmy().UpgradeTroops(*castle); // recruit army if(hero.Modes(AI::HEROES_HUNTER)) hero.GetArmy().JoinStrongestFromArmy(castle->GetArmy()); else if(hero.Modes(AI::HEROES_SCOUTER)) hero.GetArmy().KeepOnlyWeakestTroops(castle->GetArmy()); DEBUG(DBG_AI, DBG_TRACE, hero.GetName() << ", " << hero.GetArmy().String()); } // patrol task if(hero.Modes(Heroes::PATROL)) { DEBUG(DBG_AI, DBG_TRACE, hero.GetName() << ", is patrol mode"); // goto patrol center if(hero.GetCenterPatrol() != hero.GetCenter() && hero.GetPath().Calculate(Maps::GetIndexFromAbsPoint(hero.GetCenterPatrol()))) return true; // scan enemy hero if(hero.GetSquarePatrol()) { const Maps::Indexes & results = Maps::ScanAroundObject(Maps::GetIndexFromAbsPoint(hero.GetCenterPatrol()), hero.GetSquarePatrol(), MP2::OBJ_HEROES); for(MapsIndexes::const_iterator it = results.begin(); it != results.end(); ++it) { const Heroes* enemy = world.GetTiles(*it).GetHeroes(); if(enemy && ! enemy->isFriends(hero.GetColor())) { if(hero.GetPath().Calculate(enemy->GetIndex())) { DEBUG(DBG_AI, DBG_TRACE, hero.GetName() << ", find enemy"); return true; } } } } // can pickup objects if(conf.ExtHeroPatrolAllowPickup()) { const Maps::Indexes & results = Maps::ScanAroundObjects(hero.GetIndex(), hero.GetSquarePatrol(), objs1); for(MapsIndexes::const_iterator it = results.begin(); it != results.end(); ++it) if(AI::HeroesValidObject(hero, *it) && hero.GetPath().Calculate(*it)) { ai_objects.erase(*it); DEBUG(DBG_AI, DBG_TRACE, hero.GetName() << ": find object: " << MP2::StringObject(world.GetTiles(*it).GetObject()) << "(" << *it << ")"); return true; } } // random move /* // disable move: https://sourceforge.net/tracker/?func=detail&aid=3157397&group_id=96859&atid=616180 { Maps::ScanAroundObject(hero.GetIndex(), hero.GetSquarePatrol(), MP2::OBJ_ZERO); if(results.size()) { std::random_shuffle(results.begin(), results.end()); std::vector<s32>::const_iterator it = results.begin(); for(; it != results.end(); ++it) if(world.GetTiles(*it).isPassable(&hero, Direction::CENTER, true) && hero.GetPath().Calculate(*it)) { DEBUG(DBG_AI, Color::String(hero.GetColor()) << ", Patrol " << hero.GetName() << ": move: " << *it); return; } } } */ hero.SetModes(AI::HEROES_STUPID); return false; } if(ai_hero.fix_loop > 3) { DEBUG(DBG_AI, DBG_TRACE, hero.GetName() << ": loop"); hero.SetModes(hero.Modes(AI::HEROES_WAITING) ? AI::HEROES_STUPID : AI::HEROES_WAITING); return false; } // primary target if(Maps::isValidAbsIndex(ai_hero.primary_target)) { if(hero.GetIndex() == ai_hero.primary_target) { ai_hero.primary_target = -1; hero.GetPath().Reset(); DEBUG(DBG_AI, DBG_TRACE, hero.GetName() << ", reset path"); } else { DEBUG(DBG_AI, DBG_TRACE, hero.GetName() << ", primary target: " << ai_hero.primary_target << ", " << MP2::StringObject(world.GetTiles(ai_hero.primary_target).GetObject())); const Castle* castle = NULL; if(NULL != (castle = world.GetCastle(Maps::GetPoint(ai_hero.primary_target))) && NULL != castle->GetHeroes().Guest() && castle->isFriends(hero.GetColor())) { hero.SetModes(AI::HEROES_WAITING); DEBUG(DBG_AI, DBG_TRACE, hero.GetName() << ", castle busy.."); } // make path if(ai_hero.primary_target != hero.GetPath().GetDestinationIndex() && !hero.GetPath().Calculate(ai_hero.primary_target)) { DEBUG(DBG_AI, DBG_TRACE, hero.GetName() << ", path unknown, primary target reset"); ai_hero.primary_target = -1; } } if(hero.GetPath().isValid()) return true; } // scan heroes and castle const Maps::Indexes & enemies = Maps::ScanAroundObjects(hero.GetIndex(), hero.GetScoute(), objs3); for(MapsIndexes::const_iterator it = enemies.begin(); it != enemies.end(); ++it) if(AIHeroesPriorityObject(hero, *it) && hero.GetPath().Calculate(*it)) { DEBUG(DBG_AI, DBG_TRACE, hero.GetName() << ", set primary target: " << MP2::StringObject(world.GetTiles(*it).GetObject()) << "(" << *it << ")"); ai_hero.primary_target = *it; return true; } // check destination if(hero.GetPath().isValid()) { if(! AI::HeroesValidObject(hero, hero.GetPath().GetDestinationIndex())) hero.GetPath().Reset(); else if(hero.GetPath().size() < 5) { DEBUG(DBG_AI, DBG_TRACE, hero.GetName() << ", continue short"); ai_hero.fix_loop++; return true; } } // scan 2x2 pickup objects Maps::Indexes pickups = Maps::ScanAroundObjects(hero.GetIndex(), 2, objs1); // scan 3x3 capture objects const Maps::Indexes & captures = Maps::ScanAroundObjects(hero.GetIndex(), 3, objs2); if(captures.size()) pickups.insert(pickups.end(), captures.begin(), captures.end()); if(pickups.size()) { hero.GetPath().Reset(); for(MapsIndexes::const_iterator it = pickups.begin(); it != pickups.end(); ++it) if(AI::HeroesValidObject(hero, *it)) { task.push_front(*it); DEBUG(DBG_AI, DBG_TRACE, hero.GetName() << ", find object: " << MP2::StringObject(world.GetTiles(*it).GetObject()) << "(" << *it << ")"); } } if(hero.GetPath().isValid()) { DEBUG(DBG_AI, DBG_TRACE, hero.GetName() << ", continue"); ai_hero.fix_loop++; return true; } if(task.empty()) { // get task from kingdom DEBUG(DBG_AI, DBG_TRACE, hero.GetName() << ", empty task"); AIHeroesAddedTask(hero); } else // remove invalid task task.remove_if(std::not1(std::bind1st(std::ptr_fun(&AIHeroesValidObject2), &hero))); // random shuffle if(1 < task.size() && Rand::Get(1)) { Queue::iterator it1, it2; it2 = it1 = task.begin(); ++it2; std::swap(*it1, *it2); } // find passable index while(task.size()) { const s32 & index = task.front(); DEBUG(DBG_AI, DBG_TRACE, hero.GetName() << ", look for: " << MP2::StringObject(world.GetTiles(index).GetObject()) << "(" << index << ")"); if(hero.GetPath().Calculate(index)) break; DEBUG(DBG_AI, DBG_TRACE, hero.GetName() << " say: unable get object: " << index << ", remove task..."); task.pop_front(); } // success if(task.size()) { const s32 & index = task.front(); DEBUG(DBG_AI, DBG_TRACE, hero.GetName() << " go to: " << index); ai_objects.erase(index); task.pop_front(); DEBUG(DBG_AI, DBG_TRACE, hero.GetName() << ", route: " << hero.GetPath().String()); return true; } else if(hero.Modes(AI::HEROES_WAITING)) { hero.GetPath().Reset(); DEBUG(DBG_AI, DBG_TRACE, hero.GetName() << " say: unknown task., help my please.."); hero.ResetModes(AI::HEROES_WAITING); hero.SetModes(AI::HEROES_STUPID); } else { DEBUG(DBG_AI, DBG_TRACE, hero.GetName() << " say: waiting..."); hero.SetModes(AI::HEROES_WAITING); } return false; } void AIHeroesTurn(Heroes* hero) { if(hero) AI::HeroesTurn(*hero); } void AI::HeroesTurn(Heroes & hero) { DEBUG(DBG_AI, DBG_TRACE, hero.GetName() << ", start: " << (hero.Modes(Heroes::SHIPMASTER) ? "SHIPMASTER," : "") << (hero.Modes(AI::HEROES_SCOUTER) ? "SCOUTER," : "") << (hero.Modes(AI::HEROES_HUNTER) ? "HUNTER," : "") << (hero.Modes(Heroes::PATROL) ? "PATROL," : "") << (hero.Modes(AI::HEROES_WAITING) ? "WAITING," : "") << (hero.Modes(AI::HEROES_STUPID) ? "STUPID" : "")); Interface::StatusWindow & status = Interface::Basic::Get().GetStatusWindow(); while(hero.MayStillMove() && !hero.Modes(AI::HEROES_WAITING|AI::HEROES_STUPID)) { // turn indicator status.RedrawTurnProgress(3); // get task for heroes AI::HeroesGetTask(hero); // turn indicator status.RedrawTurnProgress(5); // heroes AI turn AI::HeroesMove(hero); // turn indicator status.RedrawTurnProgress(7); } DEBUG(DBG_AI, DBG_TRACE, hero.GetName() << ", end"); } bool AIHeroesScheduledVisit(const Kingdom & kingdom, s32 index) { for(KingdomHeroes::const_iterator it = kingdom.GetHeroes().begin(); it != kingdom.GetHeroes().end(); ++it) { AIHero & ai_hero = AIHeroes::Get(**it); Queue & task = ai_hero.sheduled_visit; if(task.isPresent(index)) return true; } return false; } bool IsPriorityAndNotVisitAndNotPresent(const std::pair<s32, int> & indexObj, const Heroes* hero) { AIHero & ai_hero = AIHeroes::Get(*hero); Queue & task = ai_hero.sheduled_visit; return AIHeroesPriorityObject(*hero, indexObj.first) && ! AIHeroesScheduledVisit(hero->GetKingdom(), indexObj.first) && ! task.isPresent(indexObj.first); } void AIHeroesEnd(Heroes* hero) { if(hero) { AIHero & ai_hero = AIHeroes::Get(*hero); AIKingdom & ai_kingdom = AIKingdoms::Get(hero->GetColor()); Queue & task = ai_hero.sheduled_visit; IndexObjectMap & ai_objects = ai_kingdom.scans; if(hero->Modes(AI::HEROES_WAITING|AI::HEROES_STUPID)) { ai_hero.Reset(); hero->ResetModes(AI::HEROES_WAITING|AI::HEROES_STUPID); } IndexObjectMap::iterator it; while(true) { for(it = ai_objects.begin(); it != ai_objects.end(); ++it) if(IsPriorityAndNotVisitAndNotPresent(*it, hero)) break; if(ai_objects.end() == it) break; DEBUG(DBG_AI, DBG_TRACE, hero->GetName() << ", added priority object: " << MP2::StringObject((*it).second) << ", index: " << (*it).first); task.push_front((*it).first); ai_objects.erase((*it).first); } } } void AIHeroesSetHunterWithTarget(Heroes* hero, s32 dst) { if(hero) { AIHero & ai_hero = AIHeroes::Get(*hero); hero->SetModes(AI::HEROES_HUNTER); if(0 > ai_hero.primary_target) { ai_hero.primary_target = dst; } } } void AIHeroesCaptureNearestTown(Heroes* hero) { if(hero) { AIHero & ai_hero = AIHeroes::Get(*hero); if(0 > ai_hero.primary_target) { const Maps::Indexes & castles = Maps::GetObjectPositions(hero->GetIndex(), MP2::OBJ_CASTLE, true); for(MapsIndexes::const_iterator it = castles.begin(); it != castles.end(); ++it) { const Castle* castle = world.GetCastle(Maps::GetPoint(*it)); if(castle) DEBUG(DBG_AI, DBG_TRACE, hero->GetName() << ", to castle: " << castle->GetName()); if(castle && Army::TroopsStrongerEnemyTroops(hero->GetArmy(), castle->GetArmy())) { ai_hero.primary_target = *it; DEBUG(DBG_AI, DBG_INFO, Color::String(hero->GetColor()) << ", Hero " << hero->GetName() << " set primary target: " << *it); break; } } } } }
ai_kingdom.cpp - Код: Выделить всё
/******************************************************************************** * Copyright (C) 2010 by Andrey Afletdinov <fheroes2@gmail.com> * * All rights reserved. * * * * Part of the Free Heroes2 Engine: * * http://sourceforge.net/projects/fheroes2 * * * * Redistribution and use in source and binary forms, with or without * * modification, are permitted provided that the following conditions * * are met: * * - Redistributions may not be sold, nor may they be used in a * * commercial product or activity. * * - Redistributions of source code and/or in binary form must reproduce * * the above copyright notice, this list of conditions and the * * following disclaimer in the documentation and/or other materials * * provided with the distribution. * * * * THIS SOFTWARE IS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, * * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS * * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE * * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, * * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * *******************************************************************************/ #include <functional> #include <algorithm> #include "game.h" #include "cursor.h" #include "engine.h" #include "game_interface.h" #include "interface_gamearea.h" #include "heroes.h" #include "castle.h" #include "world.h" #include "settings.h" #include "kingdom.h" #include "agg.h" #include "ai.h" #include "ai_simple.h" void AICastleTurn(Castle*); void AIHeroesTurn(Heroes*); void AIHeroesEnd(Heroes*); void AIHeroesCaptureNearestTown(Heroes*); void AIHeroesSetHunterWithTarget(Heroes*, s32); AIKingdoms & AIKingdoms::Get(void) { static AIKingdoms ai_kingdoms; return ai_kingdoms; } AIKingdom & AIKingdoms::Get(int color) { return AIKingdoms::Get().at(Color::GetIndex(color)); } void AIKingdoms::Reset(void) { AIKingdoms & ai = AIKingdoms::Get(); std::for_each(ai.begin(), ai.end(), std::mem_fun_ref(&AIKingdom::Reset)); } void AIKingdom::Reset(void) { capital = NULL; scans.clear(); } void IndexObjectMap::DumpObjects(const IndexDistance & id) { IndexObjectMap::const_iterator it = find(id.first); if(it != end()) DEBUG(DBG_AI, DBG_TRACE, MP2::StringObject((*it).second) \ << ", maps index: " << id.first << ", dist: " << id.second); } void WorldStoreObjects(int color, IndexObjectMap & store) { for(s32 it = 0; it < world.w() * world.h(); ++it) { const Maps::Tiles & tile = world.GetTiles(it); if(tile.isFog(color)) continue; if(MP2::isGroundObject(tile.GetObject()) || MP2::isWaterObject(tile.GetObject()) || MP2::OBJ_HEROES == tile.GetObject()) { // if quantity object is empty if(MP2::isQuantityObject(tile.GetObject()) && ! MP2::isPickupObject(tile.GetObject()) && ! tile.QuantityIsValid()) continue; // skip captured obj if(MP2::isCaptureObject(tile.GetObject()) && Players::isFriends(color, tile.QuantityColor())) continue; // skip for meeting heroes if(MP2::OBJ_HEROES == tile.GetObject()) { const Heroes* hero = tile.GetHeroes(); if(hero && color == hero->GetColor()) continue; } // check: is visited objects switch(tile.GetObject()) { case MP2::OBJ_MAGELLANMAPS: case MP2::OBJ_OBSERVATIONTOWER: if(world.GetKingdom(color).isVisited(tile)) continue; break; default: break; } store[it] = tile.GetObject(); } } } void AI::KingdomTurn(Kingdom & kingdom) { KingdomHeroes & heroes = kingdom.GetHeroes(); KingdomCastles & castles = kingdom.GetCastles(); const int color = kingdom.GetColor(); if(kingdom.isLoss() || color == Color::NONE) { kingdom.LossPostActions(); return; } DEBUG(DBG_AI, DBG_INFO, Color::String(kingdom.GetColor()) << " funds: " << kingdom.GetFunds().String()); if(! Settings::Get().MusicMIDI()) AGG::PlayMusic(MUS::COMPUTER); Interface::StatusWindow & status = Interface::Basic::Get().GetStatusWindow(); AIKingdom & ai = AIKingdoms::Get(color); // turn indicator status.RedrawTurnProgress(0); // scan map ai.scans.clear(); WorldStoreObjects(color, ai.scans); DEBUG(DBG_AI, DBG_INFO, Color::String(color) << ", size cache objects: " << ai.scans.size()); // set capital if(NULL == ai.capital && castles.size()) { KingdomCastles::iterator it = std::find_if(castles.begin(), castles.end(), Castle::PredicateIsCastle); if(castles.end() != it) { if(*it) { ai.capital = *it; ai.capital->SetModes(Castle::CAPITAL); } } else // first town { ai.capital = castles.front(); ai.capital->SetModes(Castle::CAPITAL); } } // turn indicator status.RedrawTurnProgress(1); // castles AI turn std::for_each(castles.begin(), castles.end(), AICastleTurn); // need capture town? if(castles.empty()) std::for_each(heroes.begin(), heroes.end(), AIHeroesCaptureNearestTown); // buy hero in capital if(ai.capital && ai.capital->isCastle()) { u32 modes = 0; const u32 maxhero = Maps::XLARGE > world.w() ? (Maps::LARGE > world.w() ? 3 : 2) : 4; if(heroes.empty()) modes = AI::HEROES_HUNTER|AI::HEROES_SCOUTER; else if(heroes.size() < maxhero || 0 == std::count_if(heroes.begin(), heroes.end(), std::bind2nd(std::mem_fun(&Heroes::Modes), AI::HEROES_SCOUTER))) modes = AI::HEROES_SCOUTER; if(modes && heroes.size() < Kingdom::GetMaxHeroes()) { Recruits & rec = kingdom.GetRecruits(); Heroes* hero = ai.capital->GetHeroes().Guest(); if(!hero) { if(rec.GetHero1() && rec.GetHero2()) hero = ai.capital->RecruitHero(rec.GetHero1()->GetLevel() >= rec.GetHero2()->GetLevel() ? rec.GetHero1() : rec.GetHero2()); else if(rec.GetHero1()) hero = ai.capital->RecruitHero(rec.GetHero1()); else if(rec.GetHero2()) hero = ai.capital->RecruitHero(rec.GetHero2()); if(hero) hero->SetModes(modes); } } } // set hunters if(ai.capital) { const size_t hunters = std::count_if(heroes.begin(), heroes.end(), std::bind2nd(std::mem_fun(&Heroes::Modes), AI::HEROES_HUNTER)); // every time if(0 == hunters && heroes.size()) { KingdomHeroes::iterator it = std::find_if(heroes.begin(), heroes.end(), std::not1(std::bind2nd(std::mem_fun(&Heroes::Modes), Heroes::PATROL))); if(it != heroes.end() && ! ai.capital->GetHeroes().Guest()) AIHeroesSetHunterWithTarget((*it), ai.capital->GetIndex()); } else // each month if(world.BeginMonth() && 1 < world.CountDay()) { KingdomHeroes::iterator it = std::find_if(heroes.begin(), heroes.end(), std::bind2nd(std::mem_fun(&Heroes::Modes), AI::HEROES_HUNTER)); if(it != heroes.end() && ! ai.capital->GetHeroes().Guest()) AIHeroesSetHunterWithTarget(*it, ai.capital->GetIndex()); } } // update roles { std::for_each(heroes.begin(), heroes.end(), std::bind2nd(std::mem_fun(&Heroes::ResetModes), AI::HEROES_STUPID|AI::HEROES_WAITING)); // init roles if(heroes.end() != std::find_if(heroes.begin(), heroes.end(), std::not1(std::bind2nd(std::mem_fun(&Heroes::Modes), AI::HEROES_SCOUTER|AI::HEROES_HUNTER)))) { KingdomHeroes::iterator ith, first = heroes.end(); while(heroes.end() != (ith = std::find_if(heroes.begin(), heroes.end(), std::not1(std::bind2nd(std::mem_fun(&Heroes::Modes), // also skip patrol AI::HEROES_HUNTER|AI::HEROES_SCOUTER|Heroes::PATROL))))) { if(first == heroes.end()) { first = ith; if(*ith) (*ith)->SetModes(AI::HEROES_HUNTER|AI::HEROES_SCOUTER); } else if(*ith) (*ith)->SetModes(AI::HEROES_SCOUTER); } } } // turn indicator status.RedrawTurnProgress(2); // heroes turns std::for_each(heroes.begin(), heroes.end(), std::ptr_fun(&AIHeroesTurn)); //std::for_each(heroes.begin(), heroes.end(), std::bind2nd(std::mem_fun(&Heroes::ResetModes), AI::HEROES_STUPID|AI::HEROES_WAITING)); std::for_each(heroes.begin(), heroes.end(), std::ptr_fun(&AIHeroesTurn)); std::for_each(heroes.begin(), heroes.end(), std::ptr_fun(&AIHeroesEnd)); // turn indicator status.RedrawTurnProgress(9); DEBUG(DBG_AI, DBG_INFO, Color::String(color) << " moved"); }
Надо наверное придумать и прописать ему какие-нибудь дополнительные правила чтобы компьютер их выполнял и дейстовавал более умно.
|