Sfoglia il codice sorgente

Added class HeroPtr to VCAI that should replace CGHeroInstance*. HeroPtr is able to detect when the hero under it becomes unavailable.

Hopefully fixed #954 (and all bugs caused by keeping a reference to hero that got killed: #998, #1002, #1008, #1011, #1018).
If not, assertions should pop up earlier.
Michał W. Urbańczyk 13 anni fa
parent
commit
fcdad0d323
4 ha cambiato i file con 201 aggiunte e 89 eliminazioni
  1. 1 0
      AI/VCAI/Fuzzy.h
  2. 2 0
      AI/VCAI/StdInc.h
  3. 148 68
      AI/VCAI/VCAI.cpp
  4. 50 21
      AI/VCAI/VCAI.h

+ 1 - 0
AI/VCAI/Fuzzy.h

@@ -1,3 +1,4 @@
+#pragma once
 #include "../FuzzyLite/FuzzyLite.h"
 
 /*

+ 2 - 0
AI/VCAI/StdInc.h

@@ -26,6 +26,8 @@
 #include "../../lib/CondSh.h"
 #include "../../lib/CStopWatch.h"
 
+#include "Fuzzy.h"
+
 #include <fstream>
 #include <queue>
 

+ 148 - 68
AI/VCAI/VCAI.cpp

@@ -1,7 +1,6 @@
 #include "StdInc.h"
 #include "VCAI.h"
 #include "../../lib/UnlockGuard.h"
-#include "Fuzzy.h"
 #include "../../lib/CObjectHandler.h"
 
 #define I_AM_ELEMENTAR return CGoal(*this).setisElementar(true)
@@ -20,6 +19,8 @@ using namespace vstd;
 boost::thread_specific_ptr<CCallback> cb;
 boost::thread_specific_ptr<VCAI> ai;
 
+//std::map<int, std::map<int, int> > HeroView::infosCount;
+
 // CCallback *cb;
 // VCAI *ai;
 
@@ -96,10 +97,11 @@ std::string goalName(EGoals goalType)
 	}
 }
 
-bool compareHeroStrength(const CGHeroInstance *h1, const CGHeroInstance *h2)
+bool compareHeroStrength(HeroPtr h1, HeroPtr h2)
 {
 	return h1->getTotalStrength() < h2->getTotalStrength();
 }
+
 bool compareArmyStrength(const CArmedInstance *a1, const CArmedInstance *a2)
 {
 	return a1->getArmyStrength() < a2->getArmyStrength();
@@ -259,7 +261,7 @@ bool isReachable(const CGObjectInstance *obj)
 	return cb->getPathInfo(obj->visitablePos())->turns < 255;
 }
 
-ui64 howManyReinforcementsCanGet(const CGHeroInstance *h, const CGTownInstance *t)
+ui64 howManyReinforcementsCanGet(HeroPtr h, const CGTownInstance *t)
 {
 	ui64 ret = 0;
 	int freeHeroSlots = GameConstants::ARMY_SIZE - h->stacksCount();
@@ -306,7 +308,7 @@ bool isCloser(const CGObjectInstance *lhs, const CGObjectInstance *rhs)
 	return (ln->moveRemains > rn->moveRemains);
 };
 
-bool compareMovement(const CGHeroInstance *lhs, const CGHeroInstance *rhs)
+bool compareMovement(HeroPtr lhs, HeroPtr rhs)
 {
 	return lhs->movement > rhs->movement;
 };
@@ -665,6 +667,7 @@ void VCAI::objectRemoved(const CGObjectInstance *obj)
 {
 	NET_EVENT_HANDLER;
 	LOG_ENTRY;
+
 	if(remove_if_present(visitableObjs, obj))
 		assert(obj->isVisitable());
 
@@ -673,6 +676,12 @@ void VCAI::objectRemoved(const CGObjectInstance *obj)
 
 	//TODO
 	//there are other places where CGObjectinstance ptrs are stored...
+	// 
+
+	if(obj->ID == GameConstants::HEROI_TYPE  &&  obj->tempOwner == playerID)
+	{
+		lostHero(cb->getHero(obj->id)); //we can promote, since objectRemoved is killed just before actual deletion
+	}
 }
 
 void VCAI::showHillFortWindow(const CGObjectInstance *object, const CGHeroInstance *visitor)
@@ -923,8 +932,7 @@ void VCAI::makeTurn()
 			break;
 		case 7: //reconsider strategy
 		{
-			const CGHeroInstance * h = primaryHero();
-			if (h) //check if our primary hero can handle danger
+			if(auto h = primaryHero()) //check if our primary hero can handle danger
 			{
 				ui64 totalDanger = 0;
 				int dangerousObjects = 0;
@@ -934,7 +942,7 @@ void VCAI::makeTurn()
 				{
 					if (evaluateDanger(obj)) //potentilaly dnagerous
 					{
-						totalDanger += evaluateDanger (obj->visitablePos(), h);
+						totalDanger += evaluateDanger(obj->visitablePos(), *h);
 						++dangerousObjects;
 					}
 				}
@@ -968,26 +976,25 @@ void VCAI::makeTurnInternal()
 		//Pick objects reserved in previous turn - we expect only nerby objects there
 		BOOST_FOREACH (auto hero, reservedHeroesMap)
 		{
-			cb->setSelection(hero.first);
+			cb->setSelection(hero.first.get());
 			boost::sort (hero.second, isCloser);
 			BOOST_FOREACH (auto obj, hero.second)
 			{
-				const CGHeroInstance * h = hero.first;
-				striveToGoal (CGoal(VISIT_TILE).sethero(h).settile(obj->visitablePos()));
+				striveToGoal (CGoal(VISIT_TILE).sethero(hero.first).settile(obj->visitablePos()));
 			}
 		}
 
 		//now try to win
 		striveToGoal(CGoal(WIN));
 
-		//finally, continue our abstract long-temr goals
-		std::vector<std::pair<const CGHeroInstance *, CGoal> > safeCopy; //heroes tend to die in the process and loose their goals, unsafe to iterate it
-		BOOST_FOREACH (auto h, lockedHeroes)
-		{
-			safeCopy.push_back(h);
-		}
+		//finally, continue our abstract long-term goals
 
-		auto lockedHeroesSorter = [](std::pair<const CGHeroInstance *, CGoal> h1, std::pair<const CGHeroInstance *, CGoal> h2) -> bool
+		//heroes tend to die in the process and loose their goals, unsafe to iterate it
+		std::vector<std::pair<HeroPtr, CGoal> > safeCopy;
+		boost::copy(lockedHeroes, std::back_inserter(safeCopy));
+
+		typedef decltype(*safeCopy.begin()) TItrType;
+		auto lockedHeroesSorter = [](TItrType h1, TItrType h2) -> bool
 		{
 			return compareMovement (h1.first, h2.first);
 		};
@@ -998,7 +1005,7 @@ void VCAI::makeTurnInternal()
 			auto it = safeCopy.begin();
 			if (it->first && it->first->tempOwner == playerID && vstd::contains(lockedHeroes, it->first)) //make sure hero still has his goal
 			{
-				cb->setSelection(it->first);
+				cb->setSelection(*it->first);
 				striveToGoal (it->second);
 			}
 			safeCopy.erase(it);
@@ -1019,14 +1026,14 @@ void VCAI::makeTurnInternal()
 	endTurn();
 }
 
-bool VCAI::goVisitObj(const CGObjectInstance * obj, const CGHeroInstance * h)
+bool VCAI::goVisitObj(const CGObjectInstance * obj, HeroPtr h)
 {
 	int3 dst = obj->visitablePos();
 	BNLOG("%s will try to visit %s at (%s)", h->name % obj->hoverName % strFromInt3(dst));
 	return moveHeroToTile(dst, h);
 }
 
-void VCAI::performObjectInteraction(const CGObjectInstance * obj, const CGHeroInstance * h)
+void VCAI::performObjectInteraction(const CGObjectInstance * obj, HeroPtr h)
 {
 	switch (obj->ID)
 	{
@@ -1230,10 +1237,10 @@ void VCAI::buildStructure(const CGTownInstance * t)
 		return;
 }
 
-bool isSafeToVisit(const CGHeroInstance *h, crint3 tile)
+bool isSafeToVisit(HeroPtr h, crint3 tile)
 {
 	const ui64 heroStrength = h->getTotalStrength(),
-		dangerStrength = evaluateDanger(tile, h);
+		dangerStrength = evaluateDanger(tile, *h);
 	if(dangerStrength)
 	{
 		if(heroStrength / SAFE_ATTACK_CONSTANT > dangerStrength)
@@ -1249,7 +1256,7 @@ bool isSafeToVisit(const CGHeroInstance *h, crint3 tile)
 	return true; //there's no danger
 }
 
-std::vector<const CGObjectInstance *> VCAI::getPossibleDestinations(const CGHeroInstance *h)
+std::vector<const CGObjectInstance *> VCAI::getPossibleDestinations(HeroPtr h)
 {
 	validateVisitableObjs();
 	std::vector<const CGObjectInstance *> possibleDestinations;
@@ -1289,7 +1296,7 @@ std::vector<const CGObjectInstance *> VCAI::getPossibleDestinations(const CGHero
 	return possibleDestinations;
 }
 
-void VCAI::wander(const CGHeroInstance * h)
+void VCAI::wander(HeroPtr h)
 {
 	while(1)
 	{
@@ -1385,7 +1392,7 @@ void VCAI::wander(const CGHeroInstance * h)
 	}
 }
 
-void VCAI::setGoal (const CGHeroInstance *h, const CGoal goal)
+void VCAI::setGoal(HeroPtr h, const CGoal goal)
 { //TODO: check for presence?
 	if (goal.goalType == EGoals::INVALID)
 		remove_if_present(lockedHeroes, h);
@@ -1393,7 +1400,7 @@ void VCAI::setGoal (const CGHeroInstance *h, const CGoal goal)
 		lockedHeroes[h] = CGoal(goal).setisElementar(false); //always evaluate goals before realizing
 }
 
-void VCAI::setGoal (const CGHeroInstance *h, EGoals goalType)
+void VCAI::setGoal(HeroPtr h, EGoals goalType)
 {
 	if (goalType == EGoals::INVALID)
 		remove_if_present(lockedHeroes, h);
@@ -1403,7 +1410,7 @@ void VCAI::setGoal (const CGHeroInstance *h, EGoals goalType)
 
 void VCAI::completeGoal (const CGoal goal)
 {
-	if (const CGHeroInstance * h = goal.hero)
+	if (const CGHeroInstance * h = goal.hero.get(true))
 	{
 		auto it = lockedHeroes.find(h);
 		if (it != lockedHeroes.end())
@@ -1448,7 +1455,7 @@ void VCAI::markObjectVisited (const CGObjectInstance *obj)
 	alreadyVisited.push_back(obj);
 }
 
-void VCAI::reserveObject (const CGHeroInstance * h, const CGObjectInstance *obj)
+void VCAI::reserveObject(HeroPtr h, const CGObjectInstance *obj)
 {
 	reservedObjs.push_back(obj);
 	reservedHeroesMap[h].push_back(obj);
@@ -1524,7 +1531,7 @@ bool VCAI::isAccessible(const int3 &pos)
 	return false;
 }
 
-const CGHeroInstance * VCAI::getHeroWithGrail() const
+HeroPtr VCAI::getHeroWithGrail() const
 {
 	BOOST_FOREACH(const CGHeroInstance *h, cb->getHeroesInfo())
 		if(h->hasArt(2)) //grail
@@ -1543,9 +1550,9 @@ const CGObjectInstance * VCAI::getUnvisitedObj(const boost::function<bool(const
 	return NULL;
 }
 
-bool VCAI::isAccessibleForHero(const int3 & pos, const CGHeroInstance * h, bool includeAllies) const
+bool VCAI::isAccessibleForHero(const int3 & pos, HeroPtr h, bool includeAllies /*= false*/) const
 {
-	cb->setSelection(h);
+	cb->setSelection(*h);
 	if (!includeAllies)
 	{ //don't visit tile occupied by allied hero
 		BOOST_FOREACH (auto obj, cb->getVisitableObjs(pos))
@@ -1592,7 +1599,7 @@ public:
 	}
 };
 
-bool VCAI::moveHeroToTile(int3 dst, const CGHeroInstance * h)
+bool VCAI::moveHeroToTile(int3 dst, HeroPtr h)
 {
 	visitedObject = NULL;
 	int3 startHpos = h->visitablePos();
@@ -1600,7 +1607,7 @@ bool VCAI::moveHeroToTile(int3 dst, const CGHeroInstance * h)
 	if(startHpos == dst)
 	{
 		assert(cb->getVisitableObjs(dst).size() > 1); //there's no point in revisiting tile where there is no visitable object
-		cb->moveHero(h,CGHeroInstance::convertPosition(dst, true));
+		cb->moveHero(*h, CGHeroInstance::convertPosition(dst, true));
 		waitTillFree(); //movement may cause battle or blocking dialog
 		ret = true;
 	}
@@ -1638,19 +1645,14 @@ bool VCAI::moveHeroToTile(int3 dst, const CGHeroInstance * h)
 //
 // 			}
 			//tlog0 << "Moving " << h->name << " from " << h->getPosition() << " to " << endpos << std::endl;
-			cb->moveHero(h,CGHeroInstance::convertPosition(endpos, true));
+			cb->moveHero(*h, CGHeroInstance::convertPosition(endpos, true));
 			waitTillFree(); //movement may cause battle or blocking dialog
 			boost::this_thread::interruption_point();
 			if(h->tempOwner != playerID) //we lost hero - remove all tasks assigned to him/her
 			{
-				remove_if_present(lockedHeroes, h);
-				BOOST_FOREACH (auto obj, reservedHeroesMap[h])
-				{
-					remove_if_present(reservedObjs, obj); //unreserve all objects for that hero
-				}
-				remove_if_present(reservedHeroesMap, h);
-
-				throw std::runtime_error("Hero was lost!"); //we need to throw, otherwise hero will be assigned to sth again
+				lostHero(h);
+				//we need to throw, otherwise hero will be assigned to sth again
+				throw std::runtime_error("Hero was lost!"); 
 				break;
 			}
 
@@ -1738,7 +1740,7 @@ void VCAI::tryRealize(CGoal g)
 				throw cannotFulfillGoalException("Cannot visit tile: hero is out of MPs!");
 			if(!g.isBlockedBorderGate(g.tile))
 			{
-				if (ai->moveHeroToTile(g.tile, g.hero))
+				if (ai->moveHeroToTile(g.tile, g.hero.get()))
 				{
 					throw goalFulfilledException("");
 				}
@@ -1781,8 +1783,8 @@ void VCAI::tryRealize(CGoal g)
 			assert(g.hero->visitablePos() == g.tile);
 			if (g.hero->diggingStatus() == CGHeroInstance::CAN_DIG)
 			{
-				cb->dig (g.hero);
-				setGoal (g.hero, INVALID); // finished digging
+				cb->dig(g.hero.get());
+				setGoal(g.hero, INVALID); // finished digging
 			}
 			else
 			{
@@ -1846,9 +1848,11 @@ const CGTownInstance * VCAI::findTownWithTavern() const
 	return NULL;
 }
 
-std::vector<const CGHeroInstance *> VCAI::getUnblockedHeroes() const
+std::vector<HeroPtr> VCAI::getUnblockedHeroes() const
 {
-	std::vector<const CGHeroInstance *> ret = cb->getHeroesInfo();
+	std::vector<HeroPtr> ret;
+	boost::copy(cb->getHeroesInfo(), std::back_inserter(ret));
+
 	BOOST_FOREACH(auto h, lockedHeroes)
 	{
 		if (!h.second.invalid()) //we can use heroes without valid goal
@@ -1857,7 +1861,7 @@ std::vector<const CGHeroInstance *> VCAI::getUnblockedHeroes() const
 	return ret;
 }
 
-const CGHeroInstance * VCAI::primaryHero() const
+HeroPtr VCAI::primaryHero() const
 {
 	auto hs = cb->getHeroesInfo();
 	boost::sort(hs, compareHeroStrength);
@@ -1921,11 +1925,11 @@ void VCAI::striveToGoal(const CGoal &ultimateGoal)
 			{
 				if (maxGoals)
 				{
-					setGoal (goal.hero, goal);
+					setGoal(goal.hero, goal);
 				}
 				else
 				{
-					setGoal (goal.hero, INVALID); // we seemingly don't know what to do with hero
+					setGoal(goal.hero, INVALID); // we seemingly don't know what to do with hero
 				}
 			}
 
@@ -2025,12 +2029,12 @@ void VCAI::performTypicalActions()
 		}
 	}
 
-	BOOST_FOREACH(const CGHeroInstance *h, getUnblockedHeroes())
+	BOOST_FOREACH(auto h, getUnblockedHeroes())
 	{
 		BNLOG("Looking into %s, MP=%d", h->name.c_str() % h->movement);
 		INDENT;
-		makePossibleUpgrades(h);
-		cb->setSelection(h);
+		makePossibleUpgrades(*h);
+		cb->setSelection(*h);
 		try
 		{
 			wander(h);
@@ -2051,7 +2055,7 @@ void VCAI::buildArmyIn(const CGTownInstance * t)
 	moveCreaturesToHero(t);
 }
 
-int3 VCAI::explorationBestNeighbour(int3 hpos, int radius, const CGHeroInstance * h)
+int3 VCAI::explorationBestNeighbour(int3 hpos, int radius, HeroPtr h)
 {
 	TimeCheck tc("looking for best exploration neighbour");
 	std::map<int3, int> dstToRevealedTiles;
@@ -2076,7 +2080,7 @@ int3 VCAI::explorationBestNeighbour(int3 hpos, int radius, const CGHeroInstance
 	throw cannotFulfillGoalException("No neighbour will bring new discoveries!");
 }
 
-int3 VCAI::explorationNewPoint(int radius, const CGHeroInstance * h, std::vector<std::vector<int3> > &tiles)
+int3 VCAI::explorationNewPoint(int radius, HeroPtr h, std::vector<std::vector<int3> > &tiles)
 {
 	TimeCheck tc("looking for new exploration point");
 	PNLOG("Looking for an another place for exploration...");
@@ -2187,6 +2191,18 @@ void VCAI::requestActionASAP(boost::function<void()> whatToDo)
 	b.wait();
 }
 
+void VCAI::lostHero(HeroPtr h)
+{
+	BNLOG("I lost my hero %s. It's best to forget and move on.\n", h.name);
+
+	remove_if_present(lockedHeroes, h);
+	BOOST_FOREACH(auto obj, reservedHeroesMap[h])
+	{
+		remove_if_present(reservedObjs, obj); //unreserve all objects for that hero
+	}
+	remove_if_present(reservedHeroesMap, h);
+}
+
 AIStatus::AIStatus()
 {
 	battle = NO_BATTLE;
@@ -2264,10 +2280,10 @@ bool AIStatus::haveTurn()
 	return havingTurn;
 }
 
-int3 whereToExplore(const CGHeroInstance *h)
+int3 whereToExplore(HeroPtr h)
 {
 	//TODO it's stupid and ineffective, write sth better
-	cb->setSelection(h);
+	cb->setSelection(*h);
 	int radius = h->getSightRadious();
 	int3 hpos = h->visitablePos();
 
@@ -2348,7 +2364,7 @@ TSubgoal CGoal::whatToDoToAchieve()
 				break;
 			case EVictoryConditionType::BUILDGRAIL:
 				{
-					if(const CGHeroInstance *h = ai->getHeroWithGrail())
+					if(auto h = ai->getHeroWithGrail())
 					{
 						//hero is in a town that can host Grail
 						if(h->visitedTown && !vstd::contains(h->visitedTown->forbiddenBuildings, EBuilding::GRAIL))
@@ -2441,11 +2457,11 @@ TSubgoal CGoal::whatToDoToAchieve()
 				return CGoal(EXPLORE);
 			}
 
-			const CGHeroInstance *h = hero ? hero : ai->primaryHero();
+			HeroPtr h = hero ? hero : ai->primaryHero();
 			if(!h)
 				return CGoal(RECRUIT_HERO);
 
-			cb->setSelection(h);
+			cb->setSelection(*h);
 
 			SectorMap sm;
 			bool dropToFile = false;
@@ -2724,18 +2740,18 @@ TSubgoal CGoal::whatToDoToAchieve()
 	case GATHER_ARMY:
 		{
 			//TODO: find hero if none set
+			assert(hero);
 
-			const CGHeroInstance *h = hero;
-			cb->setSelection(h);
-			auto compareReinforcements = [h](const CGTownInstance *lhs, const CGTownInstance *rhs) -> bool
+			cb->setSelection(*hero);
+			auto compareReinforcements = [this](const CGTownInstance *lhs, const CGTownInstance *rhs) -> bool
 			{
-				return howManyReinforcementsCanGet(h, lhs) < howManyReinforcementsCanGet(h, rhs);
+				return howManyReinforcementsCanGet(hero, lhs) < howManyReinforcementsCanGet(hero, rhs);
 			};
 
 			std::vector<const CGTownInstance *> townsReachable;
 			BOOST_FOREACH(const CGTownInstance *t, cb->getTownsInfo())
 			{
-				if(!t->visitingHero && howManyReinforcementsCanGet(h,t))
+				if(!t->visitingHero && howManyReinforcementsCanGet(hero,t))
 				{
 					if(isReachable(t))
 						townsReachable.push_back(t);
@@ -2934,7 +2950,7 @@ bool isWeeklyRevisitable (const CGObjectInstance * obj)
 	return false;
 }
 
-bool shouldVisit (const CGHeroInstance * h, const CGObjectInstance * obj)
+bool shouldVisit(HeroPtr h, const CGObjectInstance * obj)
 {
 	switch (obj->ID)
 	{
@@ -2986,7 +3002,7 @@ bool shouldVisit (const CGHeroInstance * h, const CGObjectInstance * obj)
 	return true;
 }
 
-int3 SectorMap::firstTileToGet(const CGHeroInstance *h, crint3 dst)
+int3 SectorMap::firstTileToGet(HeroPtr h, crint3 dst)
 {
 	int sourceSector = retreiveTile(h->visitablePos()),
 		destinationSector = retreiveTile(dst);
@@ -3209,4 +3225,68 @@ ObjectIdRef::ObjectIdRef(const CGObjectInstance *obj) : id(obj->id)
 bool ObjectIdRef::operator<(const ObjectIdRef &rhs) const
 {
 	return id < rhs.id;
-}
+}
+
+HeroPtr::HeroPtr(const CGHeroInstance *H)
+{
+	if(!H)
+	{
+		//init from nullptr should equal to default init
+		*this = HeroPtr();
+		return;
+	}
+
+	h = H;
+	name = h->name;
+
+	hid = H->subID;
+//	infosCount[ai->playerID][hid]++;
+}
+
+HeroPtr::HeroPtr()
+{
+	h = nullptr;
+	hid = -1;
+}
+
+HeroPtr::~HeroPtr()
+{
+// 	if(hid >= 0)
+// 		infosCount[ai->playerID][hid]--;
+}
+
+bool HeroPtr::operator<(const HeroPtr &rhs) const
+{
+	return hid < rhs.hid;
+}
+
+const CGHeroInstance * HeroPtr::get(bool doWeExpectNull /*= false*/) const
+{
+	//TODO? check if these all assertions every time we get info about hero affect efficiency
+	// 
+	//behave terribly when attempting unauthorised access to hero that is not ours (or was lost)
+	assert(doWeExpectNull || h);
+	if(h)
+	{
+		assert(cb->getObj(h->id));
+		assert(h->tempOwner == ai->playerID);
+	}
+
+	return h;
+}
+
+const CGHeroInstance * HeroPtr::operator->() const
+{
+	return get();
+}
+
+bool HeroPtr::validAndSet() const
+{
+	return get(true);
+}
+
+const CGHeroInstance * HeroPtr::operator*() const
+{
+	return get();
+}
+

+ 50 - 21
AI/VCAI/VCAI.h

@@ -2,6 +2,34 @@
 typedef const int3& crint3;
 typedef const std::string& crstring;
 
+//provisional class for AI to store a reference to an owned hero object
+//checks if it's valid on access, should be used in place of const CGHeroInstance*
+struct HeroPtr
+{
+	const CGHeroInstance *h;
+	int hid; //hero id (object subID or type ID)
+
+public:
+	std::string name;
+
+	
+	HeroPtr();
+	HeroPtr(const CGHeroInstance *H);
+	~HeroPtr();
+
+	operator bool() const
+	{
+		return validAndSet();
+	}
+
+	bool operator<(const HeroPtr &rhs) const;
+	const CGHeroInstance *operator->() const;
+	const CGHeroInstance *operator*() const; //not that consistent with -> but all interfaces use CGHeroInstance*, so it's convenient
+
+	const CGHeroInstance *get(bool doWeExpectNull = false) const;
+	bool validAndSet() const;
+};
+
 enum BattleState
 {
 	NO_BATTLE,
@@ -87,7 +115,6 @@ struct CGoal
 		objid = -1;
 		aid = -1;
 		tile = int3(-1, -1, -1);
-		hero = NULL;
 		town = NULL;
 	}
 
@@ -102,7 +129,7 @@ struct CGoal
 	int objid; SETTER(int, objid)
 	int aid; SETTER(int, aid)
 	int3 tile; SETTER(int3, tile)
-	const CGHeroInstance *hero; SETTER(CGHeroInstance *, hero)
+	HeroPtr hero; SETTER(HeroPtr, hero)
 	const CGTownInstance *town; SETTER(CGTownInstance *, town)
 	int bid; SETTER(int, bid)
 };
@@ -141,7 +168,7 @@ struct SectorMap
 
 	void makeParentBFS(crint3 source);
 
-	int3 firstTileToGet(const CGHeroInstance *h, crint3 dst); //if h wants to reach tile dst, which tile he should visit to clear the way?
+	int3 firstTileToGet(HeroPtr h, crint3 dst); //if h wants to reach tile dst, which tile he should visit to clear the way?
 };
 
 struct CIssueCommand : CGoal
@@ -188,10 +215,10 @@ public:
 
 	std::map<const CGObjectInstance *, const CGObjectInstance *> knownSubterraneanGates;
 	std::vector<const CGObjectInstance *> visitedThisWeek; //only OPWs
-	std::map<const CGHeroInstance *, std::vector<const CGTownInstance *> > townVisitsThisWeek;
+	std::map<HeroPtr, std::vector<const CGTownInstance *> > townVisitsThisWeek;
 
-	std::map<const CGHeroInstance *, CGoal> lockedHeroes; //TODO: allow non-elementar objectives
-	std::map<const CGHeroInstance *, std::vector<const CGObjectInstance *> > reservedHeroesMap; //objects reserved by specific heroes
+	std::map<HeroPtr, CGoal> lockedHeroes; //TODO: allow non-elementar objectives
+	std::map<HeroPtr, std::vector<const CGObjectInstance *> > reservedHeroesMap; //objects reserved by specific heroes
 
 	std::vector<const CGObjectInstance *> visitableObjs;
 	std::vector<const CGObjectInstance *> alreadyVisited;
@@ -212,8 +239,8 @@ public:
 
 	void tryRealize(CGoal g);
 
-	int3 explorationBestNeighbour(int3 hpos, int radius, const CGHeroInstance * h);
-	int3 explorationNewPoint(int radius, const CGHeroInstance * h, std::vector<std::vector<int3> > &tiles);
+	int3 explorationBestNeighbour(int3 hpos, int radius, HeroPtr h);
+	int3 explorationNewPoint(int radius, HeroPtr h, std::vector<std::vector<int3> > &tiles);
 	void recruitHero();
 
 	virtual void init(CCallback * CB);
@@ -284,27 +311,29 @@ public:
 	void buildArmyIn(const CGTownInstance * t);
 	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 wander(HeroPtr h);
+	void setGoal(HeroPtr h, const CGoal goal);
+	void setGoal(HeroPtr h, EGoals goalType = INVALID);
 	void completeGoal (const CGoal goal); //safely removes goal from reserved hero
 
 	void recruitHero(const CGTownInstance * t);
-	std::vector<const CGObjectInstance *> getPossibleDestinations(const CGHeroInstance *h);
+	std::vector<const CGObjectInstance *> getPossibleDestinations(HeroPtr h);
 	void buildStructure(const CGTownInstance * t);
 	//void recruitCreatures(const CGTownInstance * t);
 	void recruitCreatures(const CGDwelling * d);
 	void pickBestCreatures(const CArmedInstance * army, const CArmedInstance * source); //called when we can't find a slot for new stack
 	void moveCreaturesToHero(const CGTownInstance * t);
-	bool goVisitObj(const CGObjectInstance * obj, const CGHeroInstance * h);
-	void performObjectInteraction(const CGObjectInstance * obj, const CGHeroInstance * h);
+	bool goVisitObj(const CGObjectInstance * obj, HeroPtr h);
+	void performObjectInteraction(const CGObjectInstance * obj, HeroPtr h);
+
+	bool moveHeroToTile(int3 dst, HeroPtr h);
 
-	bool moveHeroToTile(int3 dst, const CGHeroInstance * h);
+	void lostHero(HeroPtr h); //should remove all references to hero (assigned tasks and so on)
 	void waitTillFree();
 
 	void addVisitableObj(const CGObjectInstance *obj);
 	void markObjectVisited (const CGObjectInstance *obj);
-	void reserveObject (const CGHeroInstance * h, const CGObjectInstance *obj);
+	void reserveObject (HeroPtr h, const CGObjectInstance *obj);
 	//void removeVisitableObj(const CGObjectInstance *obj);
 	void validateVisitableObjs();
 	void retreiveVisitableObjs(std::vector<const CGObjectInstance *> &out, bool includeOwned = false) const;
@@ -312,15 +341,15 @@ public:
 
 	const CGObjectInstance *lookForArt(int aid) const;
 	bool isAccessible(const int3 &pos);
-	const CGHeroInstance *getHeroWithGrail() const;
+	HeroPtr getHeroWithGrail() const;
 
 	const CGObjectInstance *getUnvisitedObj(const boost::function<bool(const CGObjectInstance *)> &predicate);
-	bool isAccessibleForHero(const int3 & pos, const CGHeroInstance * h, bool includeAllies = false) const;
+	bool isAccessibleForHero(const int3 & pos, HeroPtr h, bool includeAllies = false) const;
 
 	const CGTownInstance *findTownWithTavern() const;
 
-	std::vector<const CGHeroInstance *> getUnblockedHeroes() const;
-	const CGHeroInstance *primaryHero() const;
+	std::vector<HeroPtr> getUnblockedHeroes() const;
+	HeroPtr primaryHero() const;
 	TResources estimateIncome() const;
 	bool containsSavedRes(const TResources &cost) const;
 
@@ -336,4 +365,4 @@ bool objWithID(const CGObjectInstance *obj)
 }
 
 bool isWeeklyRevisitable (const CGObjectInstance * obj);
-bool shouldVisit (const CGHeroInstance * h, const CGObjectInstance * obj);
+bool shouldVisit (HeroPtr h, const CGObjectInstance * obj);