소스 검색

First attempt to replace rule-of-a-thumb behavior with actual reasoning
- GATHER_ARMY goal replaces wander function
- Heroes will remember their own goals until they are fulfilled
- Goal search handles mutual dependency
- AI will recruit multiple heroes for exploration
- Lots of tweaks

DjWarmonger 13 년 전
부모
커밋
9518474ec7
5개의 변경된 파일543개의 추가작업 그리고 470개의 파일을 삭제
  1. 182 110
      AI/VCAI/VCAI.cpp
  2. 4 4
      AI/VCAI/VCAI.h
  3. 355 355
      Global.h
  4. 1 0
      lib/GameConstants.h
  5. 1 1
      server/CGameHandler.cpp

+ 182 - 110
AI/VCAI/VCAI.cpp

@@ -182,7 +182,7 @@ template <typename Container, typename Item>
 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 <typename V, typename Item, typename Item2>
+bool remove_if_present(std::map<Item,V> & c, const Item2 &item)
+{
+	auto i = c.find(item);
+	if (i != c.end())
+	{
+		c.erase(i);
+		return true;
+	}
+	return false;
+}
+
 template <typename Container, typename Pred>
 void erase(Container &c, Pred pred)
 {
@@ -284,15 +296,11 @@ ui64 evaluateDanger(crint3 tile, const CGHeroInstance *visitor)
 	if(t->visitable)
 	{
 		dangerousObject = dynamic_cast<CArmedInstance*>(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<const CGCreature*>(obj);
 			return cre->getArmyStrength();
 		}
+	case Obj::CREATURE_GENERATOR1:
+		{
+			const CGDwelling *d = dynamic_cast<const CGDwelling*>(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<const CGObjectInstance *> 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<const CGObjectInstance *> 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<const CGObjectInstance *> 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<const CGTownInstance *> townsReachable;
-			std::vector<const CGTownInstance *> 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<const CGTownInstance *> 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<const CGHeroInstance *> VCAI::getUnblockedHeroes() const
 {
 	std::vector<const CGHeroInstance *> 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<const CGHeroInstance *> 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<const CGTownInstance *> 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<const CGObjectInstance *> 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);
 	}

+ 4 - 4
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<const CGObjectInstance *> visitedThisWeek; //only OPWs
 	std::map<const CGHeroInstance *, std::vector<const CGTownInstance *> > townVisitsThisWeek;
 
-	std::set<const CGHeroInstance *> blockedHeroes; //they won't get any new action
+	std::map<const CGHeroInstance *, CGoal> lockedHeroes; //TODO: allow non-elementar objectives
 
 	std::vector<const CGObjectInstance *> visitableObjs;
 	std::vector<const CGObjectInstance *> 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<const CGObjectInstance *> getPossibleDestinations(const CGHeroInstance *h);

+ 355 - 355
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 <cstdio>
-#include <stdio.h>
-#ifdef _WIN32
-#include <tchar.h>
-#else
-#include "tchar_amigaos4.h"
-#endif
-
-#include <cmath>
-#include <cassert>
-#include <assert.h>
-#include <vector>
-#include <string>
-#include <map>
-#include <queue>
-#include <set>
-#include <utility>
-#include <numeric>
-
-#include <iostream>
-#include <fstream>
-#include <sstream>
-#include <iomanip>
-
-#include <algorithm>
-#include <memory>
-#include <cstdlib>
-
-//filesystem version 3 causes problems (and it's default as of boost 1.46)
-#define BOOST_FILESYSTEM_VERSION 2
-
-#include <boost/algorithm/string.hpp>
-#include <boost/assert.hpp>
-#include <boost/assign.hpp>
-#include <boost/bind.hpp>
-#include <boost/cstdint.hpp>
-#include <boost/date_time/posix_time/posix_time.hpp>
-#include <boost/filesystem.hpp>
-#include <boost/foreach.hpp>
-#include <boost/format.hpp>
-#include <boost/function.hpp>
-#include <boost/lexical_cast.hpp>
-#include <boost/logic/tribool.hpp>
-#include <boost/program_options.hpp>
-#include <boost/thread.hpp>
-#include <boost/unordered_set.hpp>
-
-#ifdef ANDROID
-#include <android/log.h>
-#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<typename KeyT, typename ValT>
-class bmap : public std::map<KeyT, ValT>
-{
-public:
-	const ValT & operator[](KeyT key) const
-	{
-		return find(key)->second;
-	}
-	ValT & operator[](KeyT key)
-	{
-		return static_cast<std::map<KeyT, ValT> &>(*this)[key];
-	}
-	template <typename Handler> void serialize(Handler &h, const int version)
-	{
-		h & static_cast<std::map<KeyT, ValT> &>(*this);
-	}
-};
-
-namespace vstd
-{
-	//returns true if container c contains item i
-	template <typename Container, typename Item>
-	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 <typename V, typename Item, typename Item2>
-	bool contains(const std::map<Item,V> & c, const Item2 &i)
-	{
-		return c.find(i)!=c.end();
-	}
-
-	//returns true if bmap c contains item i
-	template <typename V, typename Item, typename Item2>
-	bool contains(const bmap<Item,V> & c, const Item2 &i)
-	{
-		return c.find(i)!=c.end();
-	}
-
-	//returns true if unordered set c contains item i
-	template <typename Item>
-	bool contains(const boost::unordered_set<Item> & 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 <typename T1, typename T2>
-	int find_pos(const std::vector<T1> & 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 <typename T1, typename T2, typename Func>
-	int find_pos(const std::vector<T1> & 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, typename Item>
-	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, typename Item>
-	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, typename Item>
-	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 <typename t1, typename t2>
-	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 <typename t1, typename t2>
-	t1 &amin(t1 &a, const t2 &b)
-	{
-		if(a <= b)
-			return a;
-		else
-		{
-			a = b;
-			return a;
-		}
-	}
-
-	//makes a to fit the range <b, c>
-	template <typename t1, typename t2, typename t3>
-	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 <typename t1, typename t2, typename t3>
-	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 <typename t1, typename t2, typename t3>
-	bool iswithin(const t1 &a, const t2 &b, const t3 &c)
-	{
-		return a >= b && a <= c;
-	}
-
-	template <typename t1, typename t2>
-	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 <typename t1, typename t2>
-	assigner<t1,t2> assigno(t1 &a1, const t2 &a2)
-	{
-		return assigner<t1,t2>(a1,a2);
-	}
-
-	//deleted pointer and sets it to NULL
-	template <typename T>
-	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 <cstdio>
+#include <stdio.h>
+#ifdef _WIN32
+#include <tchar.h>
+#else
+#include "tchar_amigaos4.h"
+#endif
+
+#include <cmath>
+#include <cassert>
+#include <assert.h>
+#include <vector>
+#include <string>
+#include <map>
+#include <queue>
+#include <set>
+#include <utility>
+#include <numeric>
+
+#include <iostream>
+#include <fstream>
+#include <sstream>
+#include <iomanip>
+
+#include <algorithm>
+#include <memory>
+#include <cstdlib>
+
+//filesystem version 3 causes problems (and it's default as of boost 1.46)
+#define BOOST_FILESYSTEM_VERSION 2
+
+#include <boost/algorithm/string.hpp>
+#include <boost/assert.hpp>
+#include <boost/assign.hpp>
+#include <boost/bind.hpp>
+#include <boost/cstdint.hpp>
+#include <boost/date_time/posix_time/posix_time.hpp>
+#include <boost/filesystem.hpp>
+#include <boost/foreach.hpp>
+#include <boost/format.hpp>
+#include <boost/function.hpp>
+#include <boost/lexical_cast.hpp>
+#include <boost/logic/tribool.hpp>
+#include <boost/program_options.hpp>
+#include <boost/thread.hpp>
+#include <boost/unordered_set.hpp>
+
+#ifdef ANDROID
+#include <android/log.h>
+#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<typename KeyT, typename ValT>
+class bmap : public std::map<KeyT, ValT>
+{
+public:
+	const ValT & operator[](KeyT key) const
+	{
+		return find(key)->second;
+	}
+	ValT & operator[](KeyT key)
+	{
+		return static_cast<std::map<KeyT, ValT> &>(*this)[key];
+	}
+	template <typename Handler> void serialize(Handler &h, const int version)
+	{
+		h & static_cast<std::map<KeyT, ValT> &>(*this);
+	}
+};
+
+namespace vstd
+{
+	//returns true if container c contains item i
+	template <typename Container, typename Item>
+	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 <typename V, typename Item, typename Item2>
+	bool contains(const std::map<Item,V> & c, const Item2 &i)
+	{
+		return c.find(i)!=c.end();
+	}
+
+	//returns true if bmap c contains item i
+	template <typename V, typename Item, typename Item2>
+	bool contains(const bmap<Item,V> & c, const Item2 &i)
+	{
+		return c.find(i)!=c.end();
+	}
+
+	//returns true if unordered set c contains item i
+	template <typename Item>
+	bool contains(const boost::unordered_set<Item> & 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 <typename T1, typename T2>
+	int find_pos(const std::vector<T1> & 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 <typename T1, typename T2, typename Func>
+	int find_pos(const std::vector<T1> & 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, typename Item>
+	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, typename Item>
+	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, typename Item>
+	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 <typename t1, typename t2>
+	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 <typename t1, typename t2>
+	t1 &amin(t1 &a, const t2 &b)
+	{
+		if(a <= b)
+			return a;
+		else
+		{
+			a = b;
+			return a;
+		}
+	}
+
+	//makes a to fit the range <b, c>
+	template <typename t1, typename t2, typename t3>
+	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 <typename t1, typename t2, typename t3>
+	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 <typename t1, typename t2, typename t3>
+	bool iswithin(const t1 &a, const t2 &b, const t3 &c)
+	{
+		return a >= b && a <= c;
+	}
+
+	template <typename t1, typename t2>
+	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 <typename t1, typename t2>
+	assigner<t1,t2> assigno(t1 &a1, const t2 &a2)
+	{
+		return assigner<t1,t2>(a1,a2);
+	}
+
+	//deleted pointer and sets it to NULL
+	template <typename T>
+	void clear_pointer(T* &ptr)
+	{
+		delete ptr;
+		ptr = NULL;
+	}
+
 	template<typename T>
 	std::unique_ptr<T> make_unique()
 	{
 		return std::unique_ptr<T>(new T());
-	}
+	}
 	template<typename T, typename Arg1>
 	std::unique_ptr<T> make_unique(Arg1&& arg1)
 	{
 		return std::unique_ptr<T>(new T(std::forward<Arg1>(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<typename T, size_t N> 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<typename T, size_t N> 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"

+ 1 - 0
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;

+ 1 - 1
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