1
0
mirror of https://github.com/vcmi/vcmi.git synced 2024-12-26 22:57:00 +02:00

VCAI: add all new movement code include teleports and transit support

This commit is contained in:
ArseniyShestakov 2015-03-08 17:47:58 +03:00
parent 665712c196
commit 12cf883740
2 changed files with 116 additions and 18 deletions

View File

@ -95,6 +95,7 @@ VCAI::VCAI(void)
{
LOG_TRACE(logAi);
makingTurn = nullptr;
destinationTeleport = ObjectInstanceID();
}
VCAI::~VCAI(void)
@ -122,11 +123,19 @@ void VCAI::heroMoved(const TryMoveHero & details)
const CGObjectInstance *o1 = frontOrNull(cb->getVisitableObjs(from)),
*o2 = frontOrNull(cb->getVisitableObjs(to));
if(o1 && o2 && o1->ID == Obj::SUBTERRANEAN_GATE && o2->ID == Obj::SUBTERRANEAN_GATE)
auto t1 = dynamic_cast<const CGTeleport *>(o1);
auto t2 = dynamic_cast<const CGTeleport *>(o2);
if(t1 && t2)
{
knownSubterraneanGates[o1] = o2;
knownSubterraneanGates[o2] = o1;
logAi->debugStream() << boost::format("Found a pair of subterranean gates between %s and %s!") % from % to;
if(ETeleportChannelType::BIDIRECTIONAL == cb->getTeleportChannelType(t1->channel))
{
if(o1->ID == Obj::SUBTERRANEAN_GATE)
{
knownSubterraneanGates[o1] = o2;
knownSubterraneanGates[o2] = o1;
logAi->debugStream() << boost::format("Found a pair of subterranean gates between %s and %s!") % from % to;
}
}
}
}
}
@ -590,6 +599,37 @@ void VCAI::showBlockingDialog(const std::string &text, const std::vector<Compone
});
}
void VCAI::showTeleportDialog(TeleportChannelID channel, std::vector<ObjectInstanceID> exits, bool impassable, QueryID askID)
{
LOG_TRACE_PARAMS(logAi, "askID '%i', exits '%s'", askID % exits);
NET_EVENT_HANDLER;
status.addQuery(askID, boost::str(boost::format("Teleport dialog query with %d exits")
% exits.size()));
ObjectInstanceID choosenExit;
if(impassable)
knownTeleportChannels[channel]->passability = TeleportChannel::IMPASSABLE;
else
{
if(destinationTeleport != ObjectInstanceID() && vstd::contains(exits, destinationTeleport))
choosenExit = destinationTeleport;
if(!status.channelProbing())
{
vstd::erase_if(exits, [&](ObjectInstanceID id) -> bool
{
return vstd::contains(visitableObjs, cb->getObj(id)) || id == choosenExit;
});
teleportChannelProbingList = exits;
}
}
requestActionASAP([=]()
{
answerQuery(askID, choosenExit.getNum());
});
}
void VCAI::showGarrisonDialog(const CArmedInstance *up, const CGHeroInstance *down, bool removableUnits, QueryID queryID)
{
LOG_TRACE_PARAMS(logAi, "removableUnits '%i', queryID '%i'", removableUnits % queryID);
@ -1649,6 +1689,18 @@ bool VCAI::isAccessibleForHero(const int3 & pos, HeroPtr h, bool includeAllies /
bool VCAI::moveHeroToTile(int3 dst, HeroPtr h)
{
auto afterMovementCheck = [&]() -> void
{
waitTillFree(); //movement may cause battle or blocking dialog
if(!h)
{
lostHero(h);
if (status.channelProbing()) // if hero lost during channel probing we need to switch this mode off
status.setChannelProbing(false);
throw std::runtime_error("Hero was lost!");
}
};
logAi->debugStream() << boost::format("Moving hero %s to tile %s") % h->name % dst;
int3 startHpos = h->visitablePos();
bool ret = false;
@ -1657,12 +1709,8 @@ bool VCAI::moveHeroToTile(int3 dst, HeroPtr h)
//FIXME: this assertion fails also if AI moves onto defeated guarded 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));
waitTillFree(); //movement may cause battle or blocking dialog
if(!h) // TODO is it feasible to hero get killed there if game work properly?
{ // not sure if AI can currently reconsider to attack bank while staying on it. Check issue 2084 on mantis for more information.
lostHero(h);
throw std::runtime_error("Hero was lost!");
}
afterMovementCheck();// TODO: is it feasible to hero get killed there if game work properly?
// not sure if AI can currently reconsider to attack bank while staying on it. Check issue 2084 on mantis for more information.
ret = true;
}
else
@ -1675,9 +1723,54 @@ bool VCAI::moveHeroToTile(int3 dst, HeroPtr h)
throw goalFulfilledException (sptr(Goals::VisitTile(dst).sethero(h)));
}
auto getObj = [&](int3 coord, bool ignoreHero = false)
{
return cb->getTile(coord,false)->topVisitableObj(ignoreHero);
};
auto doMovement = [&](int3 dst, bool transit = false)
{
cb->moveHero(*h, CGHeroInstance::convertPosition(dst, true), transit);
};
auto doTeleportMovement = [&](int3 dst, ObjectInstanceID exitId)
{
destinationTeleport = exitId;
cb->moveHero(*h, CGHeroInstance::convertPosition(dst, true));
destinationTeleport = ObjectInstanceID();
afterMovementCheck();
};
auto doChannelProbing = [&]() -> void
{
auto currentExit = getObj(CGHeroInstance::convertPosition(h->pos,false));
assert(currentExit);
status.setChannelProbing(true);
for(auto exit : teleportChannelProbingList)
doTeleportMovement(CGHeroInstance::convertPosition(h->pos,false), exit);
teleportChannelProbingList.clear();
doTeleportMovement(CGHeroInstance::convertPosition(h->pos,false), currentExit->id);
status.setChannelProbing(false);
};
int i=path.nodes.size()-1;
for(; i>0; i--)
{
int3 currentCoord = path.nodes[i].coord;
int3 nextCoord = path.nodes[i-1].coord;
auto currentObject = getObj(currentCoord, currentCoord == CGHeroInstance::convertPosition(h->pos,false));
auto nextObject = getObj(nextCoord);
if(CGTeleport::isConnected(currentObject, nextObject))
{ //we use special login if hero standing on teleporter it's mean we need
doTeleportMovement(currentCoord, nextObject->id);
if(teleportChannelProbingList.size())
doChannelProbing();
continue;
}
//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)
{
@ -1689,16 +1782,19 @@ bool VCAI::moveHeroToTile(int3 dst, HeroPtr h)
if(endpos == h->visitablePos())
continue;
cb->moveHero(*h, CGHeroInstance::convertPosition(endpos, true));
waitTillFree(); //movement may cause battle or blocking dialog
boost::this_thread::interruption_point();
if(!h) //we lost hero - remove all tasks assigned to him/her
{
lostHero(h);
//we need to throw, otherwise hero will be assigned to sth again
throw std::runtime_error("Hero was lost!");
if((i-2 >= 0) // Check there is node after next one; otherwise transit is pointless
&& (CGTeleport::isConnected(nextObject, getObj(path.nodes[i-2].coord))
|| CGTeleport::isTeleport(nextObject)))
{ // Hero should be able to go through object if it's allow transit
doMovement(endpos, true);
}
else
doMovement(endpos);
afterMovementCheck();
if(teleportChannelProbingList.size())
doChannelProbing();
}
ret = !i;
}

View File

@ -143,6 +143,8 @@ public:
std::map<TeleportChannelID, shared_ptr<TeleportChannel> > knownTeleportChannels;
std::map<const CGObjectInstance *, const CGObjectInstance *> knownSubterraneanGates;
ObjectInstanceID destinationTeleport;
std::vector<ObjectInstanceID> teleportChannelProbingList; //list of teleport channel exits that not visible and need to be (re-)explored
//std::vector<const CGObjectInstance *> visitedThisWeek; //only OPWs
std::map<HeroPtr, std::set<const CGTownInstance *> > townVisitsThisWeek;