1
0
mirror of https://github.com/vcmi/vcmi.git synced 2025-08-13 19:54:17 +02:00

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.
This commit is contained in:
Michał W. Urbańczyk
2012-06-30 23:48:40 +00:00
parent 613172fa45
commit fcdad0d323
4 changed files with 201 additions and 89 deletions

View File

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

View File

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

View File

@@ -1,7 +1,6 @@
#include "StdInc.h" #include "StdInc.h"
#include "VCAI.h" #include "VCAI.h"
#include "../../lib/UnlockGuard.h" #include "../../lib/UnlockGuard.h"
#include "Fuzzy.h"
#include "../../lib/CObjectHandler.h" #include "../../lib/CObjectHandler.h"
#define I_AM_ELEMENTAR return CGoal(*this).setisElementar(true) #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<CCallback> cb;
boost::thread_specific_ptr<VCAI> ai; boost::thread_specific_ptr<VCAI> ai;
//std::map<int, std::map<int, int> > HeroView::infosCount;
// CCallback *cb; // CCallback *cb;
// VCAI *ai; // 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(); return h1->getTotalStrength() < h2->getTotalStrength();
} }
bool compareArmyStrength(const CArmedInstance *a1, const CArmedInstance *a2) bool compareArmyStrength(const CArmedInstance *a1, const CArmedInstance *a2)
{ {
return a1->getArmyStrength() < a2->getArmyStrength(); return a1->getArmyStrength() < a2->getArmyStrength();
@@ -259,7 +261,7 @@ bool isReachable(const CGObjectInstance *obj)
return cb->getPathInfo(obj->visitablePos())->turns < 255; 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; ui64 ret = 0;
int freeHeroSlots = GameConstants::ARMY_SIZE - h->stacksCount(); int freeHeroSlots = GameConstants::ARMY_SIZE - h->stacksCount();
@@ -306,7 +308,7 @@ bool isCloser(const CGObjectInstance *lhs, const CGObjectInstance *rhs)
return (ln->moveRemains > rn->moveRemains); return (ln->moveRemains > rn->moveRemains);
}; };
bool compareMovement(const CGHeroInstance *lhs, const CGHeroInstance *rhs) bool compareMovement(HeroPtr lhs, HeroPtr rhs)
{ {
return lhs->movement > rhs->movement; return lhs->movement > rhs->movement;
}; };
@@ -665,6 +667,7 @@ void VCAI::objectRemoved(const CGObjectInstance *obj)
{ {
NET_EVENT_HANDLER; NET_EVENT_HANDLER;
LOG_ENTRY; LOG_ENTRY;
if(remove_if_present(visitableObjs, obj)) if(remove_if_present(visitableObjs, obj))
assert(obj->isVisitable()); assert(obj->isVisitable());
@@ -673,6 +676,12 @@ void VCAI::objectRemoved(const CGObjectInstance *obj)
//TODO //TODO
//there are other places where CGObjectinstance ptrs are stored... //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) void VCAI::showHillFortWindow(const CGObjectInstance *object, const CGHeroInstance *visitor)
@@ -923,8 +932,7 @@ void VCAI::makeTurn()
break; break;
case 7: //reconsider strategy case 7: //reconsider strategy
{ {
const CGHeroInstance * h = primaryHero(); if(auto h = primaryHero()) //check if our primary hero can handle danger
if (h) //check if our primary hero can handle danger
{ {
ui64 totalDanger = 0; ui64 totalDanger = 0;
int dangerousObjects = 0; int dangerousObjects = 0;
@@ -934,7 +942,7 @@ void VCAI::makeTurn()
{ {
if (evaluateDanger(obj)) //potentilaly dnagerous if (evaluateDanger(obj)) //potentilaly dnagerous
{ {
totalDanger += evaluateDanger (obj->visitablePos(), h); totalDanger += evaluateDanger(obj->visitablePos(), *h);
++dangerousObjects; ++dangerousObjects;
} }
} }
@@ -968,26 +976,25 @@ void VCAI::makeTurnInternal()
//Pick objects reserved in previous turn - we expect only nerby objects there //Pick objects reserved in previous turn - we expect only nerby objects there
BOOST_FOREACH (auto hero, reservedHeroesMap) BOOST_FOREACH (auto hero, reservedHeroesMap)
{ {
cb->setSelection(hero.first); cb->setSelection(hero.first.get());
boost::sort (hero.second, isCloser); boost::sort (hero.second, isCloser);
BOOST_FOREACH (auto obj, hero.second) BOOST_FOREACH (auto obj, hero.second)
{ {
const CGHeroInstance * h = hero.first; striveToGoal (CGoal(VISIT_TILE).sethero(hero.first).settile(obj->visitablePos()));
striveToGoal (CGoal(VISIT_TILE).sethero(h).settile(obj->visitablePos()));
} }
} }
//now try to win //now try to win
striveToGoal(CGoal(WIN)); striveToGoal(CGoal(WIN));
//finally, continue our abstract long-temr goals //finally, continue our abstract long-term 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);
}
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); return compareMovement (h1.first, h2.first);
}; };
@@ -998,7 +1005,7 @@ void VCAI::makeTurnInternal()
auto it = safeCopy.begin(); auto it = safeCopy.begin();
if (it->first && it->first->tempOwner == playerID && vstd::contains(lockedHeroes, it->first)) //make sure hero still has his goal 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); striveToGoal (it->second);
} }
safeCopy.erase(it); safeCopy.erase(it);
@@ -1019,14 +1026,14 @@ void VCAI::makeTurnInternal()
endTurn(); endTurn();
} }
bool VCAI::goVisitObj(const CGObjectInstance * obj, const CGHeroInstance * h) bool VCAI::goVisitObj(const CGObjectInstance * obj, HeroPtr h)
{ {
int3 dst = obj->visitablePos(); int3 dst = obj->visitablePos();
BNLOG("%s will try to visit %s at (%s)", h->name % obj->hoverName % strFromInt3(dst)); BNLOG("%s will try to visit %s at (%s)", h->name % obj->hoverName % strFromInt3(dst));
return moveHeroToTile(dst, h); return moveHeroToTile(dst, h);
} }
void VCAI::performObjectInteraction(const CGObjectInstance * obj, const CGHeroInstance * h) void VCAI::performObjectInteraction(const CGObjectInstance * obj, HeroPtr h)
{ {
switch (obj->ID) switch (obj->ID)
{ {
@@ -1230,10 +1237,10 @@ void VCAI::buildStructure(const CGTownInstance * t)
return; return;
} }
bool isSafeToVisit(const CGHeroInstance *h, crint3 tile) bool isSafeToVisit(HeroPtr h, crint3 tile)
{ {
const ui64 heroStrength = h->getTotalStrength(), const ui64 heroStrength = h->getTotalStrength(),
dangerStrength = evaluateDanger(tile, h); dangerStrength = evaluateDanger(tile, *h);
if(dangerStrength) if(dangerStrength)
{ {
if(heroStrength / SAFE_ATTACK_CONSTANT > dangerStrength) if(heroStrength / SAFE_ATTACK_CONSTANT > dangerStrength)
@@ -1249,7 +1256,7 @@ bool isSafeToVisit(const CGHeroInstance *h, crint3 tile)
return true; //there's no danger return true; //there's no danger
} }
std::vector<const CGObjectInstance *> VCAI::getPossibleDestinations(const CGHeroInstance *h) std::vector<const CGObjectInstance *> VCAI::getPossibleDestinations(HeroPtr h)
{ {
validateVisitableObjs(); validateVisitableObjs();
std::vector<const CGObjectInstance *> possibleDestinations; std::vector<const CGObjectInstance *> possibleDestinations;
@@ -1289,7 +1296,7 @@ std::vector<const CGObjectInstance *> VCAI::getPossibleDestinations(const CGHero
return possibleDestinations; return possibleDestinations;
} }
void VCAI::wander(const CGHeroInstance * h) void VCAI::wander(HeroPtr h)
{ {
while(1) 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? { //TODO: check for presence?
if (goal.goalType == EGoals::INVALID) if (goal.goalType == EGoals::INVALID)
remove_if_present(lockedHeroes, h); 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 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) if (goalType == EGoals::INVALID)
remove_if_present(lockedHeroes, h); remove_if_present(lockedHeroes, h);
@@ -1403,7 +1410,7 @@ void VCAI::setGoal (const CGHeroInstance *h, EGoals goalType)
void VCAI::completeGoal (const CGoal goal) 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); auto it = lockedHeroes.find(h);
if (it != lockedHeroes.end()) if (it != lockedHeroes.end())
@@ -1448,7 +1455,7 @@ void VCAI::markObjectVisited (const CGObjectInstance *obj)
alreadyVisited.push_back(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); reservedObjs.push_back(obj);
reservedHeroesMap[h].push_back(obj); reservedHeroesMap[h].push_back(obj);
@@ -1524,7 +1531,7 @@ bool VCAI::isAccessible(const int3 &pos)
return false; return false;
} }
const CGHeroInstance * VCAI::getHeroWithGrail() const HeroPtr VCAI::getHeroWithGrail() const
{ {
BOOST_FOREACH(const CGHeroInstance *h, cb->getHeroesInfo()) BOOST_FOREACH(const CGHeroInstance *h, cb->getHeroesInfo())
if(h->hasArt(2)) //grail if(h->hasArt(2)) //grail
@@ -1543,9 +1550,9 @@ const CGObjectInstance * VCAI::getUnvisitedObj(const boost::function<bool(const
return NULL; 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) if (!includeAllies)
{ //don't visit tile occupied by allied hero { //don't visit tile occupied by allied hero
BOOST_FOREACH (auto obj, cb->getVisitableObjs(pos)) 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; visitedObject = NULL;
int3 startHpos = h->visitablePos(); int3 startHpos = h->visitablePos();
@@ -1600,7 +1607,7 @@ bool VCAI::moveHeroToTile(int3 dst, const CGHeroInstance * h)
if(startHpos == dst) if(startHpos == dst)
{ {
assert(cb->getVisitableObjs(dst).size() > 1); //there's no point in revisiting tile where there is no visitable object 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 waitTillFree(); //movement may cause battle or blocking dialog
ret = true; 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; //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 waitTillFree(); //movement may cause battle or blocking dialog
boost::this_thread::interruption_point(); boost::this_thread::interruption_point();
if(h->tempOwner != playerID) //we lost hero - remove all tasks assigned to him/her if(h->tempOwner != playerID) //we lost hero - remove all tasks assigned to him/her
{ {
remove_if_present(lockedHeroes, h); lostHero(h);
BOOST_FOREACH (auto obj, reservedHeroesMap[h]) //we need to throw, otherwise hero will be assigned to sth again
{ throw std::runtime_error("Hero was lost!");
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
break; break;
} }
@@ -1738,7 +1740,7 @@ void VCAI::tryRealize(CGoal g)
throw cannotFulfillGoalException("Cannot visit tile: hero is out of MPs!"); throw cannotFulfillGoalException("Cannot visit tile: hero is out of MPs!");
if(!g.isBlockedBorderGate(g.tile)) if(!g.isBlockedBorderGate(g.tile))
{ {
if (ai->moveHeroToTile(g.tile, g.hero)) if (ai->moveHeroToTile(g.tile, g.hero.get()))
{ {
throw goalFulfilledException(""); throw goalFulfilledException("");
} }
@@ -1781,8 +1783,8 @@ void VCAI::tryRealize(CGoal g)
assert(g.hero->visitablePos() == g.tile); assert(g.hero->visitablePos() == g.tile);
if (g.hero->diggingStatus() == CGHeroInstance::CAN_DIG) if (g.hero->diggingStatus() == CGHeroInstance::CAN_DIG)
{ {
cb->dig (g.hero); cb->dig(g.hero.get());
setGoal (g.hero, INVALID); // finished digging setGoal(g.hero, INVALID); // finished digging
} }
else else
{ {
@@ -1846,9 +1848,11 @@ const CGTownInstance * VCAI::findTownWithTavern() const
return NULL; 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) BOOST_FOREACH(auto h, lockedHeroes)
{ {
if (!h.second.invalid()) //we can use heroes without valid goal if (!h.second.invalid()) //we can use heroes without valid goal
@@ -1857,7 +1861,7 @@ std::vector<const CGHeroInstance *> VCAI::getUnblockedHeroes() const
return ret; return ret;
} }
const CGHeroInstance * VCAI::primaryHero() const HeroPtr VCAI::primaryHero() const
{ {
auto hs = cb->getHeroesInfo(); auto hs = cb->getHeroesInfo();
boost::sort(hs, compareHeroStrength); boost::sort(hs, compareHeroStrength);
@@ -1921,11 +1925,11 @@ void VCAI::striveToGoal(const CGoal &ultimateGoal)
{ {
if (maxGoals) if (maxGoals)
{ {
setGoal (goal.hero, goal); setGoal(goal.hero, goal);
} }
else 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); BNLOG("Looking into %s, MP=%d", h->name.c_str() % h->movement);
INDENT; INDENT;
makePossibleUpgrades(h); makePossibleUpgrades(*h);
cb->setSelection(h); cb->setSelection(*h);
try try
{ {
wander(h); wander(h);
@@ -2051,7 +2055,7 @@ void VCAI::buildArmyIn(const CGTownInstance * t)
moveCreaturesToHero(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"); TimeCheck tc("looking for best exploration neighbour");
std::map<int3, int> dstToRevealedTiles; 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!"); 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"); TimeCheck tc("looking for new exploration point");
PNLOG("Looking for an another place for exploration..."); PNLOG("Looking for an another place for exploration...");
@@ -2187,6 +2191,18 @@ void VCAI::requestActionASAP(boost::function<void()> whatToDo)
b.wait(); 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() AIStatus::AIStatus()
{ {
battle = NO_BATTLE; battle = NO_BATTLE;
@@ -2264,10 +2280,10 @@ bool AIStatus::haveTurn()
return havingTurn; return havingTurn;
} }
int3 whereToExplore(const CGHeroInstance *h) int3 whereToExplore(HeroPtr h)
{ {
//TODO it's stupid and ineffective, write sth better //TODO it's stupid and ineffective, write sth better
cb->setSelection(h); cb->setSelection(*h);
int radius = h->getSightRadious(); int radius = h->getSightRadious();
int3 hpos = h->visitablePos(); int3 hpos = h->visitablePos();
@@ -2348,7 +2364,7 @@ TSubgoal CGoal::whatToDoToAchieve()
break; break;
case EVictoryConditionType::BUILDGRAIL: case EVictoryConditionType::BUILDGRAIL:
{ {
if(const CGHeroInstance *h = ai->getHeroWithGrail()) if(auto h = ai->getHeroWithGrail())
{ {
//hero is in a town that can host Grail //hero is in a town that can host Grail
if(h->visitedTown && !vstd::contains(h->visitedTown->forbiddenBuildings, EBuilding::GRAIL)) if(h->visitedTown && !vstd::contains(h->visitedTown->forbiddenBuildings, EBuilding::GRAIL))
@@ -2441,11 +2457,11 @@ TSubgoal CGoal::whatToDoToAchieve()
return CGoal(EXPLORE); return CGoal(EXPLORE);
} }
const CGHeroInstance *h = hero ? hero : ai->primaryHero(); HeroPtr h = hero ? hero : ai->primaryHero();
if(!h) if(!h)
return CGoal(RECRUIT_HERO); return CGoal(RECRUIT_HERO);
cb->setSelection(h); cb->setSelection(*h);
SectorMap sm; SectorMap sm;
bool dropToFile = false; bool dropToFile = false;
@@ -2724,18 +2740,18 @@ TSubgoal CGoal::whatToDoToAchieve()
case GATHER_ARMY: case GATHER_ARMY:
{ {
//TODO: find hero if none set //TODO: find hero if none set
assert(hero);
const CGHeroInstance *h = hero; cb->setSelection(*hero);
cb->setSelection(h); auto compareReinforcements = [this](const CGTownInstance *lhs, const CGTownInstance *rhs) -> bool
auto compareReinforcements = [h](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; std::vector<const CGTownInstance *> townsReachable;
BOOST_FOREACH(const CGTownInstance *t, cb->getTownsInfo()) BOOST_FOREACH(const CGTownInstance *t, cb->getTownsInfo())
{ {
if(!t->visitingHero && howManyReinforcementsCanGet(h,t)) if(!t->visitingHero && howManyReinforcementsCanGet(hero,t))
{ {
if(isReachable(t)) if(isReachable(t))
townsReachable.push_back(t); townsReachable.push_back(t);
@@ -2934,7 +2950,7 @@ bool isWeeklyRevisitable (const CGObjectInstance * obj)
return false; return false;
} }
bool shouldVisit (const CGHeroInstance * h, const CGObjectInstance * obj) bool shouldVisit(HeroPtr h, const CGObjectInstance * obj)
{ {
switch (obj->ID) switch (obj->ID)
{ {
@@ -2986,7 +3002,7 @@ bool shouldVisit (const CGHeroInstance * h, const CGObjectInstance * obj)
return true; return true;
} }
int3 SectorMap::firstTileToGet(const CGHeroInstance *h, crint3 dst) int3 SectorMap::firstTileToGet(HeroPtr h, crint3 dst)
{ {
int sourceSector = retreiveTile(h->visitablePos()), int sourceSector = retreiveTile(h->visitablePos()),
destinationSector = retreiveTile(dst); destinationSector = retreiveTile(dst);
@@ -3210,3 +3226,67 @@ bool ObjectIdRef::operator<(const ObjectIdRef &rhs) const
{ {
return id < rhs.id; 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();
}

View File

@@ -2,6 +2,34 @@
typedef const int3& crint3; typedef const int3& crint3;
typedef const std::string& crstring; 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 enum BattleState
{ {
NO_BATTLE, NO_BATTLE,
@@ -87,7 +115,6 @@ struct CGoal
objid = -1; objid = -1;
aid = -1; aid = -1;
tile = int3(-1, -1, -1); tile = int3(-1, -1, -1);
hero = NULL;
town = NULL; town = NULL;
} }
@@ -102,7 +129,7 @@ struct CGoal
int objid; SETTER(int, objid) int objid; SETTER(int, objid)
int aid; SETTER(int, aid) int aid; SETTER(int, aid)
int3 tile; SETTER(int3, tile) int3 tile; SETTER(int3, tile)
const CGHeroInstance *hero; SETTER(CGHeroInstance *, hero) HeroPtr hero; SETTER(HeroPtr, hero)
const CGTownInstance *town; SETTER(CGTownInstance *, town) const CGTownInstance *town; SETTER(CGTownInstance *, town)
int bid; SETTER(int, bid) int bid; SETTER(int, bid)
}; };
@@ -141,7 +168,7 @@ struct SectorMap
void makeParentBFS(crint3 source); 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 struct CIssueCommand : CGoal
@@ -188,10 +215,10 @@ public:
std::map<const CGObjectInstance *, const CGObjectInstance *> knownSubterraneanGates; std::map<const CGObjectInstance *, const CGObjectInstance *> knownSubterraneanGates;
std::vector<const CGObjectInstance *> visitedThisWeek; //only OPWs 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<HeroPtr, CGoal> lockedHeroes; //TODO: allow non-elementar objectives
std::map<const CGHeroInstance *, std::vector<const CGObjectInstance *> > reservedHeroesMap; //objects reserved by specific heroes std::map<HeroPtr, std::vector<const CGObjectInstance *> > reservedHeroesMap; //objects reserved by specific heroes
std::vector<const CGObjectInstance *> visitableObjs; std::vector<const CGObjectInstance *> visitableObjs;
std::vector<const CGObjectInstance *> alreadyVisited; std::vector<const CGObjectInstance *> alreadyVisited;
@@ -212,8 +239,8 @@ public:
void tryRealize(CGoal g); void tryRealize(CGoal g);
int3 explorationBestNeighbour(int3 hpos, int radius, const CGHeroInstance * h); int3 explorationBestNeighbour(int3 hpos, int radius, HeroPtr h);
int3 explorationNewPoint(int radius, const CGHeroInstance * h, std::vector<std::vector<int3> > &tiles); int3 explorationNewPoint(int radius, HeroPtr h, std::vector<std::vector<int3> > &tiles);
void recruitHero(); void recruitHero();
virtual void init(CCallback * CB); virtual void init(CCallback * CB);
@@ -284,27 +311,29 @@ public:
void buildArmyIn(const CGTownInstance * t); void buildArmyIn(const CGTownInstance * t);
void striveToGoal(const CGoal &ultimateGoal); void striveToGoal(const CGoal &ultimateGoal);
void endTurn(); void endTurn();
void wander(const CGHeroInstance * h); void wander(HeroPtr h);
void setGoal (const CGHeroInstance *h, const CGoal goal); void setGoal(HeroPtr h, const CGoal goal);
void setGoal (const CGHeroInstance *h, EGoals goalType = INVALID); void setGoal(HeroPtr h, EGoals goalType = INVALID);
void completeGoal (const CGoal goal); //safely removes goal from reserved hero void completeGoal (const CGoal goal); //safely removes goal from reserved hero
void recruitHero(const CGTownInstance * t); 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 buildStructure(const CGTownInstance * t);
//void recruitCreatures(const CGTownInstance * t); //void recruitCreatures(const CGTownInstance * t);
void recruitCreatures(const CGDwelling * d); 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 pickBestCreatures(const CArmedInstance * army, const CArmedInstance * source); //called when we can't find a slot for new stack
void moveCreaturesToHero(const CGTownInstance * t); void moveCreaturesToHero(const CGTownInstance * t);
bool goVisitObj(const CGObjectInstance * obj, const CGHeroInstance * h); bool goVisitObj(const CGObjectInstance * obj, HeroPtr h);
void performObjectInteraction(const CGObjectInstance * obj, const CGHeroInstance * h); void performObjectInteraction(const CGObjectInstance * obj, HeroPtr h);
bool moveHeroToTile(int3 dst, const CGHeroInstance * h); bool moveHeroToTile(int3 dst, HeroPtr h);
void lostHero(HeroPtr h); //should remove all references to hero (assigned tasks and so on)
void waitTillFree(); void waitTillFree();
void addVisitableObj(const CGObjectInstance *obj); void addVisitableObj(const CGObjectInstance *obj);
void markObjectVisited (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 removeVisitableObj(const CGObjectInstance *obj);
void validateVisitableObjs(); void validateVisitableObjs();
void retreiveVisitableObjs(std::vector<const CGObjectInstance *> &out, bool includeOwned = false) const; void retreiveVisitableObjs(std::vector<const CGObjectInstance *> &out, bool includeOwned = false) const;
@@ -312,15 +341,15 @@ public:
const CGObjectInstance *lookForArt(int aid) const; const CGObjectInstance *lookForArt(int aid) const;
bool isAccessible(const int3 &pos); bool isAccessible(const int3 &pos);
const CGHeroInstance *getHeroWithGrail() const; HeroPtr getHeroWithGrail() const;
const CGObjectInstance *getUnvisitedObj(const boost::function<bool(const CGObjectInstance *)> &predicate); 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; const CGTownInstance *findTownWithTavern() const;
std::vector<const CGHeroInstance *> getUnblockedHeroes() const; std::vector<HeroPtr> getUnblockedHeroes() const;
const CGHeroInstance *primaryHero() const; HeroPtr primaryHero() const;
TResources estimateIncome() const; TResources estimateIncome() const;
bool containsSavedRes(const TResources &cost) const; bool containsSavedRes(const TResources &cost) const;
@@ -336,4 +365,4 @@ bool objWithID(const CGObjectInstance *obj)
} }
bool isWeeklyRevisitable (const CGObjectInstance * obj); bool isWeeklyRevisitable (const CGObjectInstance * obj);
bool shouldVisit (const CGHeroInstance * h, const CGObjectInstance * obj); bool shouldVisit (HeroPtr h, const CGObjectInstance * obj);