diff --git a/AI/VCAI/VCAI.cpp b/AI/VCAI/VCAI.cpp index 7f31e6bb9..6c8878262 100644 --- a/AI/VCAI/VCAI.cpp +++ b/AI/VCAI/VCAI.cpp @@ -182,7 +182,7 @@ template bool remove_if_present(Container &c, const Item &item) { auto i = std::find(c.begin(), c.end(), item); - if(i != c.end()) + if (i != c.end()) { c.erase(i); return true; @@ -191,6 +191,18 @@ bool remove_if_present(Container &c, const Item &item) return false; } +template +bool remove_if_present(std::map & c, const Item2 &item) +{ + auto i = c.find(item); + if (i != c.end()) + { + c.erase(i); + return true; + } + return false; +} + template void erase(Container &c, Pred pred) { @@ -284,15 +296,11 @@ ui64 evaluateDanger(crint3 tile, const CGHeroInstance *visitor) if(t->visitable) { dangerousObject = dynamic_cast(t->visitableObjects.back()); + objectDanger = evaluateDanger(t->visitableObjects.back()); //unguarded objects can also be dangerous or unhandled if (dangerousObject) { - objectDanger = evaluateDanger(dangerousObject); objectDanger *= fh->getTacticalAdvantage (visitor, dangerousObject); } - else - { - objectDanger = 0; - } } int3 guardPos = cb->guardingCreaturePosition(tile); @@ -331,6 +339,11 @@ ui64 evaluateDanger(const CGObjectInstance *obj) const CGCreature *cre = dynamic_cast(obj); return cre->getArmyStrength(); } + case Obj::CREATURE_GENERATOR1: + { + const CGDwelling *d = dynamic_cast(obj); + return d->getArmyStrength(); + } case Obj::CRYPT: //crypt case Obj::CREATURE_BANK: //crebank case Obj::DRAGON_UTOPIA: @@ -834,20 +847,46 @@ void VCAI::makeTurn() BNLOG("Player %d starting turn", playerID); INDENT; - if(cb->getDate(1) == 1) + switch(cb->getDate(1)) { - townVisitsThisWeek.clear(); - std::vector objs; - retreiveVisitableObjs(objs, true); - BOOST_FOREACH(const CGObjectInstance *obj, objs) + case 1: { - if (isWeeklyRevisitable(obj)) - { - if (!vstd::contains(visitableObjs, obj)) - visitableObjs.push_back(obj); - auto o = std::find (alreadyVisited.begin(), alreadyVisited.end(), obj); - if (o != alreadyVisited.end()) - alreadyVisited.erase(o); + townVisitsThisWeek.clear(); + std::vector objs; + retreiveVisitableObjs(objs, true); + BOOST_FOREACH(const CGObjectInstance *obj, objs) + { + if (isWeeklyRevisitable(obj)) + { + if (!vstd::contains(visitableObjs, obj)) + visitableObjs.push_back(obj); + auto o = std::find (alreadyVisited.begin(), alreadyVisited.end(), obj); + if (o != alreadyVisited.end()) + alreadyVisited.erase(o); + } + } + } + case 7: //reconsider strategy + { + const CGHeroInstance * h = primaryHero(); + if (h) //check if our primary hero can ahndle danger + { + ui64 totalDanger = 0; + int dangerousObjects = 0; + std::vector objs; + retreiveVisitableObjs(objs, false); + BOOST_FOREACH (auto obj, objs) + { + if (evaluateDanger(obj)) //potentilaly dnagerous + { + totalDanger += evaluateDanger (obj->visitablePos(), h); + ++dangerousObjects; + } + } + if (dangerousObjects && totalDanger / dangerousObjects > h->getHeroStrength()) + { + setGoal (h, CGoal(GATHER_ARMY).sethero(h)); + } } } } @@ -862,7 +901,6 @@ void VCAI::makeTurn() void VCAI::makeTurnInternal() { - blockedHeroes.clear(); saving = 0; //it looks messy here, but it's better to have armed heroes before attempting realizing goals @@ -872,8 +910,12 @@ void VCAI::makeTurnInternal() try { striveToGoal(CGoal(WIN)); - striveToGoal(CGoal(BUILD)); - striveToGoal(CGoal(EXPLORE)); //if we have any MPs left, why not use them? + for (auto hg = lockedHeroes.begin(); hg != lockedHeroes.end(); hg++) //continue our goals + { + if (!hg->second.invalid()) + striveToGoal (hg->second); + } + striveToGoal(CGoal(BUILD)); //TODO: smarter building management } catch(boost::thread_interrupted &e) { @@ -1058,75 +1100,11 @@ void VCAI::wander(const CGHeroInstance * h) while(1) { auto dests = getPossibleDestinations(h); - if(!dests.size()) + if(!dests.size()) //TODO: merge with GATHER_ARMY goal { - auto compareReinforcements = [h](const CGTownInstance *lhs, const CGTownInstance *rhs) -> bool - { - return howManyReinforcementsCanGet(h, lhs) < howManyReinforcementsCanGet(h, rhs); - }; - - std::vector townsReachable; - std::vector townsNotReachable; - BOOST_FOREACH(const CGTownInstance *t, cb->getTownsInfo()) - { - if(!t->visitingHero && howManyReinforcementsCanGet(h,t) && !vstd::contains(townVisitsThisWeek[h], t)) - { - if(isReachable(t)) - townsReachable.push_back(t); - else - townsNotReachable.push_back(t); - } - } -// towns.erase(boost::remove_if(towns, [=](const CGTownInstance *t) -> bool -// { -// return !!t->visitingHero || !isReachable(t) || !howManyReinforcementsCanGet(h,t) || vstd::contains(townVisitsThisWeek[h], t); -// }),towns.end()); - - if(townsReachable.size()) - { - boost::sort(townsReachable, compareReinforcements); - dests.push_back(townsReachable.back()); - } - else if(townsNotReachable.size()) - { - boost::sort(townsNotReachable, compareReinforcements); - //TODO pick the truly best - const CGTownInstance *t = townsNotReachable.back(); - BNLOG("%s can't reach any town, we'll try to make our way to %s at %s", h->name % t->name % t->visitablePos()); - int3 pos1 = h->pos; - striveToGoal(CGoal(CLEAR_WAY_TO).settile(t->visitablePos()).sethero(h)); - if(pos1 == h->pos && h == primaryHero()) //hero can't move - { - /*boost::sort(unreachableTowns, compareArmyStrength);*/ - //BOOST_FOREACH(const CGTownInstance *t, unreachableTowns) - if(cb->getResourceAmount(Res::GOLD) >= HERO_GOLD_COST && cb->getHeroesInfo().size() < ALLOWED_ROAMING_HEROES && cb->getAvailableHeroes(t).size()) - recruitHero(t); - - } - - break; - } - else if(cb->getResourceAmount(Res::GOLD) >= HERO_GOLD_COST) - { - std::vector towns = cb->getTownsInfo(); - erase_if(towns, [](const CGTownInstance *t) -> bool - { - BOOST_FOREACH(const CGHeroInstance *h, cb->getHeroesInfo()) - if(!t->getArmyStrength() || howManyReinforcementsCanGet(h, t)) - return true; - - return false; - }); - boost::sort(towns, compareArmyStrength); - if(towns.size()) - recruitHero(towns.back()); - break; - } - else - { - PNLOG("Nowhere more to go...\n"); - break; - } + PNLOG("Nowhere more to go...\n"); + setGoal (h, INVALID); + break; } if(!goVisitObj(dests.front(), h)) @@ -1144,6 +1122,16 @@ void VCAI::wander(const CGHeroInstance * h) } } +void VCAI::setGoal (const CGHeroInstance *h, const CGoal goal) +{ //TODO: check for presence? + lockedHeroes[h] = goal; +} + +void VCAI::setGoal (const CGHeroInstance *h, EGoals goalType) +{ + lockedHeroes[h] = CGoal(goalType); +} + void VCAI::battleStart(const CCreatureSet *army1, const CCreatureSet *army2, int3 tile, const CGHeroInstance *hero1, const CGHeroInstance *hero2, bool side) { assert(playerID > GameConstants::PLAYER_LIMIT || status.getBattle() == UPCOMING_BATTLE); @@ -1313,7 +1301,7 @@ bool VCAI::moveHeroToTile(int3 dst, const CGHeroInstance * h) //stop sending move requests if the next node can't be reached at the current turn (hero exhausted his move points) if(path.nodes[i-1].turns) { - blockedHeroes.insert(h); //to avoid attempts of moving heroes with very little MPs + //blockedHeroes.insert(h); //to avoid attempts of moving heroes with very little MPs break; } @@ -1331,7 +1319,10 @@ bool VCAI::moveHeroToTile(int3 dst, const CGHeroInstance * h) waitTillFree(); //movement may cause battle or blocking dialog boost::this_thread::interruption_point(); if(h->tempOwner != playerID) //we lost hero + { + remove_if_present(lockedHeroes, h); break; + } } ret = !i; @@ -1408,7 +1399,8 @@ void VCAI::tryRealize(CGoal g) throw cannotFulfillGoalException("Cannot visit tile: hero is out of MPs!"); if(!g.isBlockedBorderGate(g.tile)) { - ai->moveHeroToTile(g.tile, g.hero); + if (ai->moveHeroToTile(g.tile, g.hero)); + setGoal (g.hero, INVALID); //this hero reached target and no goal } else throw cannotFulfillGoalException("There's a blocked gate!"); @@ -1446,11 +1438,14 @@ void VCAI::tryRealize(CGoal g) case DIG_AT_TILE: { assert(g.hero->visitablePos() == g.tile); - if(g.hero->diggingStatus() == CGHeroInstance::CAN_DIG) - cb->dig(g.hero); + if (g.hero->diggingStatus() == CGHeroInstance::CAN_DIG) + { + cb->dig (g.hero); + setGoal (g.hero, INVALID); // finished digging + } else { - ai->blockedHeroes.insert(g.hero); + ai->lockedHeroes[g.hero] = g; //hero who tries to dig shouldn't do anything else throw cannotFulfillGoalException("A hero can't dig!\n"); } } @@ -1484,6 +1479,8 @@ void VCAI::tryRealize(CGoal g) } case CONQUER: + case GATHER_ARMY: + case BOOST_HERO: // TODO: conquer?? throw cannotFulfillGoalException("I don't know how to fulfill this!"); @@ -1495,14 +1492,14 @@ void VCAI::tryRealize(CGoal g) throw cannotFulfillGoalException("I don't know how to fulfill this!"); default: - assert(0); + throw cannotFulfillGoalException("Unknown type of goal !"); } } const CGTownInstance * VCAI::findTownWithTavern() const { BOOST_FOREACH(const CGTownInstance *t, cb->getTownsInfo()) - if(vstd::contains(t->builtBuildings, EBuilding::TAVERN)) + if(vstd::contains(t->builtBuildings, EBuilding::TAVERN) && !t->visitingHero) return t; return NULL; @@ -1511,9 +1508,11 @@ const CGTownInstance * VCAI::findTownWithTavern() const std::vector VCAI::getUnblockedHeroes() const { std::vector ret = cb->getHeroesInfo(); - BOOST_FOREACH(const CGHeroInstance *h, blockedHeroes) - remove_if_present(ret, h); - + BOOST_FOREACH(auto h, lockedHeroes) + { + if (!h.second.invalid()) //we can use heroes without valid goal + remove_if_present(ret, h.first); + } return ret; } @@ -1550,7 +1549,8 @@ void VCAI::striveToGoal(const CGoal &ultimateGoal) { CGoal goal = ultimateGoal; BNLOG("Striving to goal of type %d", ultimateGoal.goalType); - while(!goal.isElementar) + int maxGoals = 100; //preventing deadlock for mutually dependent goals + while(!goal.isElementar && maxGoals) { INDENT; BNLOG("Considering goal %d.", goal.goalType); @@ -1558,6 +1558,7 @@ void VCAI::striveToGoal(const CGoal &ultimateGoal) { boost::this_thread::interruption_point(); goal = goal.whatToDoToAchieve(); + --maxGoals; } catch(std::exception &e) { @@ -1569,6 +1570,17 @@ void VCAI::striveToGoal(const CGoal &ultimateGoal) try { boost::this_thread::interruption_point(); + if (goal.hero) //lock this hero to fulfill ultimate goal + { + if (maxGoals) + { + setGoal (goal.hero, goal); + } + else + { + setGoal (goal.hero, INVALID); // we seemingly don't know what to do with hero + } + } tryRealize(goal); boost::this_thread::interruption_point(); } @@ -2026,18 +2038,33 @@ TSubgoal CGoal::whatToDoToAchieve() //return CGoal(EXPLORE); // TODO improve case EXPLORE: { - if(cb->getHeroesInfo().empty()) - return CGoal(RECRUIT_HERO); - auto hs = cb->getHeroesInfo(); - assert(hs.size()); + int howManyHeroes = hs.size(); + erase(hs, [](const CGHeroInstance *h) { - return !h->movement || contains(ai->blockedHeroes, h); //only hero with movement are of interest for us + return contains(ai->lockedHeroes, h); }); - if(hs.empty()) + if(hs.empty()) //all heroes are busy. buy new one { - throw cannotFulfillGoalException("No heroes with remaining MPs for exploring!\n"); + if (howManyHeroes < 3 && ai->findTownWithTavern()) //we may want to recruit second hero. TODO: make it smart finally + return CGoal(RECRUIT_HERO); + else //find mobile hero with weakest army + { + hs = cb->getHeroesInfo(); + erase_if(hs, [](const CGHeroInstance *h) + { + return !h->movement; //only hero with movement are of interest for us + }); + if (hs.empty()) + { + if (howManyHeroes < GameConstants::MAX_HEROES_PER_PLAYER) + return CGoal(RECRUIT_HERO); + else + throw cannotFulfillGoalException("No heroes with remaining MPs for exploring!\n"); + } + boost::sort(hs, compareHeroStrength); + } } const CGHeroInstance *h = hs.front(); @@ -2090,7 +2117,7 @@ TSubgoal CGoal::whatToDoToAchieve() if(isSafeToVisit(hero, tile)) return CGoal(*this).setisElementar(true); else - return CGoal(INVALID); //todo can gather army? + return CGoal(GATHER_ARMY).sethero(hero); } else //inaccessible for all heroes return CGoal(CLEAR_WAY_TO).settile(tile); @@ -2181,7 +2208,7 @@ TSubgoal CGoal::whatToDoToAchieve() std::vector heroes = cb->getHeroesInfo(); erase_if(heroes, [](const CGHeroInstance *h) { - return vstd::contains(ai->blockedHeroes, h) || !h->movement; + return vstd::contains(ai->lockedHeroes, h) || !h->movement; }); boost::sort(heroes, compareHeroStrength); @@ -2217,13 +2244,58 @@ TSubgoal CGoal::whatToDoToAchieve() } return CGoal(EXPLORE); //enemy is inaccessible - ; } break; case BUILD: I_AM_ELEMENTAR; case INVALID: I_AM_ELEMENTAR; + case GATHER_ARMY: + { + const CGHeroInstance *h = hero; + auto compareReinforcements = [h](const CGTownInstance *lhs, const CGTownInstance *rhs) -> bool + { + return howManyReinforcementsCanGet(h, lhs) < howManyReinforcementsCanGet(h, rhs); + }; + + std::vector townsReachable; + BOOST_FOREACH(const CGTownInstance *t, cb->getTownsInfo()) + { + if(!t->visitingHero && howManyReinforcementsCanGet(h,t)) + { + if(isReachable(t)) + townsReachable.push_back(t); + } + } + + if(townsReachable.size()) //try towns first + { + boost::sort(townsReachable, compareReinforcements); + return CGoal(VISIT_TILE).sethero(hero).settile(townsReachable.back()->visitablePos()); + } + else + { + std::vector objs; //here we'll gather all dwellings + ai->retreiveVisitableObjs(objs); + erase_if(objs, [&](const CGObjectInstance *obj) + { + return (obj->ID != Obj::CREATURE_GENERATOR1); //not town/ dwelling + }); + if(objs.empty()) //no possible objects, we did eveyrthing already + return CGoal(EXPLORE).sethero(hero); + //TODO: check if we can recruit any creatures there, evaluate army + + boost::sort(objs, isCloser); + BOOST_FOREACH(const CGObjectInstance *obj, objs) + { //find safe dwelling + if (isSafeToVisit(hero, obj->visitablePos())) //TODO: make use of multiple heroes + return CGoal(VISIT_TILE).sethero(hero).settile(obj->visitablePos()); + } + } + + return CGoal(EXPLORE); //find dwelling + } + break; default: assert(0); } diff --git a/AI/VCAI/VCAI.h b/AI/VCAI/VCAI.h index ad574a105..ac713f789 100644 --- a/AI/VCAI/VCAI.h +++ b/AI/VCAI/VCAI.h @@ -37,7 +37,7 @@ public: enum EGoals { INVALID = -1, - WIN, DO_NOT_LOSE, CONQUER, BUILD, EXPLORE, //GATHER_ARMY,// BOOST_HERO, + WIN, DO_NOT_LOSE, CONQUER, BUILD, EXPLORE, GATHER_ARMY, BOOST_HERO, RECRUIT_HERO, BUILD_STRUCTURE, //if hero set, then in visited town COLLECT_RES, @@ -156,7 +156,7 @@ public: std::vector visitedThisWeek; //only OPWs std::map > townVisitsThisWeek; - std::set blockedHeroes; //they won't get any new action + std::map lockedHeroes; //TODO: allow non-elementar objectives std::vector visitableObjs; std::vector alreadyVisited; @@ -170,8 +170,6 @@ public: VCAI(void); ~VCAI(void); - CGoal currentGoal; - CGObjectInstance * visitedObject; //remember currently viisted object boost::thread *makingTurn; @@ -250,6 +248,8 @@ public: void striveToGoal(const CGoal &ultimateGoal); void endTurn(); void wander(const CGHeroInstance * h); + void setGoal (const CGHeroInstance *h, const CGoal goal); + void setGoal (const CGHeroInstance *h, EGoals goalType = INVALID); void recruitHero(const CGTownInstance * t); std::vector getPossibleDestinations(const CGHeroInstance *h); diff --git a/Global.h b/Global.h index 053a0d633..45dadbbc0 100644 --- a/Global.h +++ b/Global.h @@ -1,363 +1,363 @@ -#pragma once - -// Standard include file -// Contents: -// Includes C/C++ libraries, STL libraries, IOStream and String libraries -// Includes the most important boost headers -// Defines the import + export, override and exception handling macros -// Defines the vstd library -// Includes the logger - -// This file shouldn't be changed, except if there is a important header file missing which is shared among several projects. - -/* - * Global.h, part of VCMI engine - * - * Authors: listed in file AUTHORS in main folder - * - * License: GNU General Public License v2.0 or later - * Full text of license available in license.txt file, in main folder - * - */ - -#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers -#include -#include -#ifdef _WIN32 -#include -#else -#include "tchar_amigaos4.h" -#endif - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include -#include -#include - -//filesystem version 3 causes problems (and it's default as of boost 1.46) -#define BOOST_FILESYSTEM_VERSION 2 - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#ifdef ANDROID -#include -#endif - -// Integral data types -typedef boost::uint64_t ui64; //unsigned int 64 bits (8 bytes) -typedef boost::uint32_t ui32; //unsigned int 32 bits (4 bytes) -typedef boost::uint16_t ui16; //unsigned int 16 bits (2 bytes) -typedef boost::uint8_t ui8; //unsigned int 8 bits (1 byte) -typedef boost::int64_t si64; //signed int 64 bits (8 bytes) -typedef boost::int32_t si32; //signed int 32 bits (4 bytes) -typedef boost::int16_t si16; //signed int 16 bits (2 bytes) -typedef boost::int8_t si8; //signed int 8 bits (1 byte) - -#ifdef __GNUC__ -#define GCC_VERSION (__GNUC__ * 100 + __GNUC_MINOR__ ) -#endif - -// Import + Export macro declarations -#ifdef _WIN32 -#define DLL_EXPORT __declspec(dllexport) -#else -#if defined(__GNUC__) && GCC_VERSION >= 400 -#define DLL_EXPORT __attribute__ ((visibility("default"))) -#else -#define DLL_EXPORT -#endif -#endif - -#ifdef _WIN32 -#define DLL_IMPORT __declspec(dllimport) -#else -#if defined(__GNUC__) && GCC_VERSION >= 400 -#define DLL_IMPORT __attribute__ ((visibility("default"))) -#else -#define DLL_IMPORT -#endif -#endif - -#ifdef VCMI_DLL -#define DLL_LINKAGE DLL_EXPORT -#else -#define DLL_LINKAGE DLL_IMPORT -#endif - -//defining available c++11 features - -//initialization lists - only gcc-4.4 or later -#if defined(__GNUC__) && (GCC_VERSION >= 404) -#define CPP11_USE_INITIALIZERS_LIST -#endif - -//nullptr - only msvc and gcc-4.6 or later, othervice define it as NULL -#if !defined(_MSC_VER) && !(defined(__GNUC__) && (GCC_VERSION >= 406)) -#define nullptr NULL -#endif - -//override keyword - only msvc and gcc-4.7 or later. -#if !defined(_MSC_VER) && !(defined(__GNUC__) && (GCC_VERSION >= 407)) -#define override -#endif - -//workaround to support existing code -#define OVERRIDE override - -//a normal std::map with a const operator[] for sanity -template -class bmap : public std::map -{ -public: - const ValT & operator[](KeyT key) const - { - return find(key)->second; - } - ValT & operator[](KeyT key) - { - return static_cast &>(*this)[key]; - } - template void serialize(Handler &h, const int version) - { - h & static_cast &>(*this); - } -}; - -namespace vstd -{ - //returns true if container c contains item i - template - bool contains(const Container & c, const Item &i) - { - return std::find(c.begin(),c.end(),i) != c.end(); - } - - //returns true if map c contains item i - template - bool contains(const std::map & c, const Item2 &i) - { - return c.find(i)!=c.end(); - } - - //returns true if bmap c contains item i - template - bool contains(const bmap & c, const Item2 &i) - { - return c.find(i)!=c.end(); - } - - //returns true if unordered set c contains item i - template - bool contains(const boost::unordered_set & c, const Item &i) - { - return c.find(i)!=c.end(); - } - - //returns position of first element in vector c equal to s, if there is no such element, -1 is returned - template - int find_pos(const std::vector & c, const T2 &s) - { - for(size_t i=0; i < c.size(); ++i) - if(c[i] == s) - return i; - return -1; - } - - //Func(T1,T2) must say if these elements matches - template - int find_pos(const std::vector & c, const T2 &s, const Func &f) - { - for(size_t i=0; i < c.size(); ++i) - if(f(c[i],s)) - return i; - return -1; - } - - //returns iterator to the given element if present in container, end() if not - template - typename Container::iterator find(Container & c, const Item &i) - { - return std::find(c.begin(),c.end(),i); - } - - //returns const iterator to the given element if present in container, end() if not - template - typename Container::const_iterator find(const Container & c, const Item &i) - { - return std::find(c.begin(),c.end(),i); - } - - //removes element i from container c, returns false if c does not contain i - template - typename Container::size_type operator-=(Container &c, const Item &i) - { - typename Container::iterator itr = find(c,i); - if(itr == c.end()) - return false; - c.erase(itr); - return true; - } - - //assigns greater of (a, b) to a and returns maximum of (a, b) - template - t1 &amax(t1 &a, const t2 &b) - { - if(a >= b) - return a; - else - { - a = b; - return a; - } - } - - //assigns smaller of (a, b) to a and returns minimum of (a, b) - template - t1 &amin(t1 &a, const t2 &b) - { - if(a <= b) - return a; - else - { - a = b; - return a; - } - } - - //makes a to fit the range - template - t1 &abetween(t1 &a, const t2 &b, const t3 &c) - { - amax(a,b); - amin(a,c); - return a; - } - - //checks if a is between b and c - template - bool isbetween(const t1 &a, const t2 &b, const t3 &c) - { - return a > b && a < c; - } - - //checks if a is within b and c - template - bool iswithin(const t1 &a, const t2 &b, const t3 &c) - { - return a >= b && a <= c; - } - - template - struct assigner - { - public: - t1 &op1; - t2 op2; - assigner(t1 &a1, const t2 & a2) - :op1(a1), op2(a2) - {} - void operator()() - { - op1 = op2; - } - }; - - // Assigns value a2 to a1. The point of time of the real operation can be controlled - // with the () operator. - template - assigner assigno(t1 &a1, const t2 &a2) - { - return assigner(a1,a2); - } - - //deleted pointer and sets it to NULL - template - void clear_pointer(T* &ptr) - { - delete ptr; - ptr = NULL; - } - +#pragma once + +// Standard include file +// Contents: +// Includes C/C++ libraries, STL libraries, IOStream and String libraries +// Includes the most important boost headers +// Defines the import + export, override and exception handling macros +// Defines the vstd library +// Includes the logger + +// This file shouldn't be changed, except if there is a important header file missing which is shared among several projects. + +/* + * Global.h, part of VCMI engine + * + * Authors: listed in file AUTHORS in main folder + * + * License: GNU General Public License v2.0 or later + * Full text of license available in license.txt file, in main folder + * + */ + +#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers +#include +#include +#ifdef _WIN32 +#include +#else +#include "tchar_amigaos4.h" +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include + +//filesystem version 3 causes problems (and it's default as of boost 1.46) +#define BOOST_FILESYSTEM_VERSION 2 + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef ANDROID +#include +#endif + +// Integral data types +typedef boost::uint64_t ui64; //unsigned int 64 bits (8 bytes) +typedef boost::uint32_t ui32; //unsigned int 32 bits (4 bytes) +typedef boost::uint16_t ui16; //unsigned int 16 bits (2 bytes) +typedef boost::uint8_t ui8; //unsigned int 8 bits (1 byte) +typedef boost::int64_t si64; //signed int 64 bits (8 bytes) +typedef boost::int32_t si32; //signed int 32 bits (4 bytes) +typedef boost::int16_t si16; //signed int 16 bits (2 bytes) +typedef boost::int8_t si8; //signed int 8 bits (1 byte) + +#ifdef __GNUC__ +#define GCC_VERSION (__GNUC__ * 100 + __GNUC_MINOR__ ) +#endif + +// Import + Export macro declarations +#ifdef _WIN32 +#define DLL_EXPORT __declspec(dllexport) +#else +#if defined(__GNUC__) && GCC_VERSION >= 400 +#define DLL_EXPORT __attribute__ ((visibility("default"))) +#else +#define DLL_EXPORT +#endif +#endif + +#ifdef _WIN32 +#define DLL_IMPORT __declspec(dllimport) +#else +#if defined(__GNUC__) && GCC_VERSION >= 400 +#define DLL_IMPORT __attribute__ ((visibility("default"))) +#else +#define DLL_IMPORT +#endif +#endif + +#ifdef VCMI_DLL +#define DLL_LINKAGE DLL_EXPORT +#else +#define DLL_LINKAGE DLL_IMPORT +#endif + +//defining available c++11 features + +//initialization lists - only gcc-4.4 or later +#if defined(__GNUC__) && (GCC_VERSION >= 404) +#define CPP11_USE_INITIALIZERS_LIST +#endif + +//nullptr - only msvc and gcc-4.6 or later, othervice define it as NULL +#if !defined(_MSC_VER) && !(defined(__GNUC__) && (GCC_VERSION >= 406)) +#define nullptr NULL +#endif + +//override keyword - only msvc and gcc-4.7 or later. +#if !defined(_MSC_VER) && !(defined(__GNUC__) && (GCC_VERSION >= 407)) +#define override +#endif + +//workaround to support existing code +#define OVERRIDE override + +//a normal std::map with a const operator[] for sanity +template +class bmap : public std::map +{ +public: + const ValT & operator[](KeyT key) const + { + return find(key)->second; + } + ValT & operator[](KeyT key) + { + return static_cast &>(*this)[key]; + } + template void serialize(Handler &h, const int version) + { + h & static_cast &>(*this); + } +}; + +namespace vstd +{ + //returns true if container c contains item i + template + bool contains(const Container & c, const Item &i) + { + return std::find(c.begin(),c.end(),i) != c.end(); + } + + //returns true if map c contains item i + template + bool contains(const std::map & c, const Item2 &i) + { + return c.find(i)!=c.end(); + } + + //returns true if bmap c contains item i + template + bool contains(const bmap & c, const Item2 &i) + { + return c.find(i)!=c.end(); + } + + //returns true if unordered set c contains item i + template + bool contains(const boost::unordered_set & c, const Item &i) + { + return c.find(i)!=c.end(); + } + + //returns position of first element in vector c equal to s, if there is no such element, -1 is returned + template + int find_pos(const std::vector & c, const T2 &s) + { + for(size_t i=0; i < c.size(); ++i) + if(c[i] == s) + return i; + return -1; + } + + //Func(T1,T2) must say if these elements matches + template + int find_pos(const std::vector & c, const T2 &s, const Func &f) + { + for(size_t i=0; i < c.size(); ++i) + if(f(c[i],s)) + return i; + return -1; + } + + //returns iterator to the given element if present in container, end() if not + template + typename Container::iterator find(Container & c, const Item &i) + { + return std::find(c.begin(),c.end(),i); + } + + //returns const iterator to the given element if present in container, end() if not + template + typename Container::const_iterator find(const Container & c, const Item &i) + { + return std::find(c.begin(),c.end(),i); + } + + //removes element i from container c, returns false if c does not contain i + template + typename Container::size_type operator-=(Container &c, const Item &i) + { + typename Container::iterator itr = find(c,i); + if(itr == c.end()) + return false; + c.erase(itr); + return true; + } + + //assigns greater of (a, b) to a and returns maximum of (a, b) + template + t1 &amax(t1 &a, const t2 &b) + { + if(a >= b) + return a; + else + { + a = b; + return a; + } + } + + //assigns smaller of (a, b) to a and returns minimum of (a, b) + template + t1 &amin(t1 &a, const t2 &b) + { + if(a <= b) + return a; + else + { + a = b; + return a; + } + } + + //makes a to fit the range + template + t1 &abetween(t1 &a, const t2 &b, const t3 &c) + { + amax(a,b); + amin(a,c); + return a; + } + + //checks if a is between b and c + template + bool isbetween(const t1 &a, const t2 &b, const t3 &c) + { + return a > b && a < c; + } + + //checks if a is within b and c + template + bool iswithin(const t1 &a, const t2 &b, const t3 &c) + { + return a >= b && a <= c; + } + + template + struct assigner + { + public: + t1 &op1; + t2 op2; + assigner(t1 &a1, const t2 & a2) + :op1(a1), op2(a2) + {} + void operator()() + { + op1 = op2; + } + }; + + // Assigns value a2 to a1. The point of time of the real operation can be controlled + // with the () operator. + template + assigner assigno(t1 &a1, const t2 &a2) + { + return assigner(a1,a2); + } + + //deleted pointer and sets it to NULL + template + void clear_pointer(T* &ptr) + { + delete ptr; + ptr = NULL; + } + template std::unique_ptr make_unique() { return std::unique_ptr(new T()); - } + } template std::unique_ptr make_unique(Arg1&& arg1) { return std::unique_ptr(new T(std::forward(arg1))); - } -} - -using std::shared_ptr; -using std::unique_ptr; -using std::make_shared; -using vstd::make_unique; - -using vstd::operator-=; - -// can be used for counting arrays -template char (&_ArrayCountObj(const T (&)[N]))[N]; -#define ARRAY_COUNT(arr) (sizeof(_ArrayCountObj(arr))) - -//XXX pls dont - 'debug macros' are usually more trouble than it's worth -#define HANDLE_EXCEPTION \ - catch (const std::exception& e) { \ - tlog1 << e.what() << std::endl; \ - throw; \ -} \ - catch (const std::exception * e) \ -{ \ - tlog1 << e->what()<< std::endl; \ - throw; \ -} \ - catch (const std::string& e) { \ - tlog1 << e << std::endl; \ - throw; \ -} - -#define HANDLE_EXCEPTIONC(COMMAND) \ - catch (const std::exception& e) { \ - COMMAND; \ - tlog1 << e.what() << std::endl; \ - throw; \ -} \ - catch (const std::string &e) \ -{ \ - COMMAND; \ - tlog1 << e << std::endl; \ - throw; \ -} - - -#include "lib/CLogger.h" + } +} + +using std::shared_ptr; +using std::unique_ptr; +using std::make_shared; +using vstd::make_unique; + +using vstd::operator-=; + +// can be used for counting arrays +template char (&_ArrayCountObj(const T (&)[N]))[N]; +#define ARRAY_COUNT(arr) (sizeof(_ArrayCountObj(arr))) + +//XXX pls dont - 'debug macros' are usually more trouble than it's worth +#define HANDLE_EXCEPTION \ + catch (const std::exception& e) { \ + tlog1 << e.what() << std::endl; \ + throw; \ +} \ + catch (const std::exception * e) \ +{ \ + tlog1 << e->what()<< std::endl; \ + throw; \ +} \ + catch (const std::string& e) { \ + tlog1 << e << std::endl; \ + throw; \ +} + +#define HANDLE_EXCEPTIONC(COMMAND) \ + catch (const std::exception& e) { \ + COMMAND; \ + tlog1 << e.what() << std::endl; \ + throw; \ +} \ + catch (const std::string &e) \ +{ \ + COMMAND; \ + tlog1 << e << std::endl; \ + throw; \ +} + + +#include "lib/CLogger.h" diff --git a/lib/GameConstants.h b/lib/GameConstants.h index 2053f98d2..7f83eefb6 100644 --- a/lib/GameConstants.h +++ b/lib/GameConstants.h @@ -64,6 +64,7 @@ namespace GameConstants const int CRE_LEVELS = 10; const int F_NUMBER = 9; //factions (town types) quantity const int PLAYER_LIMIT = 8; //player limit per map + const int MAX_HEROES_PER_PLAYER = 8; const int ALL_PLAYERS = 255; //bitfield const int HEROES_PER_TYPE=8; //amount of heroes of each type const int SKILL_QUANTITY=28; diff --git a/server/CGameHandler.cpp b/server/CGameHandler.cpp index 742840df1..c54c7a3ad 100644 --- a/server/CGameHandler.cpp +++ b/server/CGameHandler.cpp @@ -2889,7 +2889,7 @@ bool CGameHandler::hireHero(const CGObjectInstance *obj, ui8 hid, ui8 player) //common preconditions if( (p->resources[Res::GOLD]<2500 && complain("Not enough gold for buying hero!")) - || (getHeroCount(player, false) >= 8 && complain("Cannot hire hero, only 8 wandering heroes are allowed!"))) + || (getHeroCount(player, false) >= GameConstants::MAX_HEROES_PER_PLAYER && complain("Cannot hire hero, only 8 wandering heroes are allowed!"))) return false; if(t) //tavern in town