mirror of
https://github.com/vcmi/vcmi.git
synced 2024-12-24 22:14:36 +02:00
Merge pull request #93 from ArseniyShestakov/feature/pathfindingTeleports
Okay let's do this!
This commit is contained in:
commit
ec879046ca
@ -30,6 +30,11 @@ void CEmptyAI::showBlockingDialog(const std::string &text, const std::vector<Com
|
|||||||
cb->selectionMade(0, askID);
|
cb->selectionMade(0, askID);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CEmptyAI::showTeleportDialog(TeleportChannelID channel, std::vector<ObjectInstanceID> exits, bool impassable, QueryID askID)
|
||||||
|
{
|
||||||
|
cb->selectionMade(0, askID);
|
||||||
|
}
|
||||||
|
|
||||||
void CEmptyAI::showGarrisonDialog(const CArmedInstance *up, const CGHeroInstance *down, bool removableUnits, QueryID queryID)
|
void CEmptyAI::showGarrisonDialog(const CArmedInstance *up, const CGHeroInstance *down, bool removableUnits, QueryID queryID)
|
||||||
{
|
{
|
||||||
cb->selectionMade(0, queryID);
|
cb->selectionMade(0, queryID);
|
||||||
|
@ -15,6 +15,7 @@ public:
|
|||||||
void heroGotLevel(const CGHeroInstance *hero, PrimarySkill::PrimarySkill pskill, std::vector<SecondarySkill> &skills, QueryID queryID) override;
|
void heroGotLevel(const CGHeroInstance *hero, PrimarySkill::PrimarySkill pskill, std::vector<SecondarySkill> &skills, QueryID queryID) override;
|
||||||
void commanderGotLevel (const CCommanderInstance * commander, std::vector<ui32> skills, QueryID queryID) override;
|
void commanderGotLevel (const CCommanderInstance * commander, std::vector<ui32> skills, QueryID queryID) override;
|
||||||
void showBlockingDialog(const std::string &text, const std::vector<Component> &components, QueryID askID, const int soundID, bool selection, bool cancel) override;
|
void showBlockingDialog(const std::string &text, const std::vector<Component> &components, QueryID askID, const int soundID, bool selection, bool cancel) override;
|
||||||
|
void showTeleportDialog(TeleportChannelID channel, std::vector<ObjectInstanceID> exits, bool impassable, QueryID askID) override;
|
||||||
void showGarrisonDialog(const CArmedInstance *up, const CGHeroInstance *down, bool removableUnits, QueryID queryID) override;
|
void showGarrisonDialog(const CArmedInstance *up, const CGHeroInstance *down, bool removableUnits, QueryID queryID) override;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -588,11 +588,39 @@ TGoalVec Explore::getAllPossibleSubgoals()
|
|||||||
{
|
{
|
||||||
switch (obj->ID.num)
|
switch (obj->ID.num)
|
||||||
{
|
{
|
||||||
case Obj::REDWOOD_OBSERVATORY:
|
case Obj::REDWOOD_OBSERVATORY:
|
||||||
case Obj::PILLAR_OF_FIRE:
|
case Obj::PILLAR_OF_FIRE:
|
||||||
case Obj::CARTOGRAPHER:
|
case Obj::CARTOGRAPHER:
|
||||||
case Obj::SUBTERRANEAN_GATE: //TODO: check ai->knownSubterraneanGates
|
objs.push_back (obj);
|
||||||
|
break;
|
||||||
|
case Obj::MONOLITH_ONE_WAY_ENTRANCE:
|
||||||
|
case Obj::MONOLITH_TWO_WAY:
|
||||||
|
case Obj::SUBTERRANEAN_GATE:
|
||||||
|
auto tObj = dynamic_cast<const CGTeleport *>(obj);
|
||||||
|
assert(ai->knownTeleportChannels.find(tObj->channel) != ai->knownTeleportChannels.end());
|
||||||
|
if(TeleportChannel::IMPASSABLE != ai->knownTeleportChannels[tObj->channel]->passability)
|
||||||
objs.push_back (obj);
|
objs.push_back (obj);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
switch (obj->ID.num)
|
||||||
|
{
|
||||||
|
case Obj::MONOLITH_TWO_WAY:
|
||||||
|
case Obj::SUBTERRANEAN_GATE:
|
||||||
|
auto tObj = dynamic_cast<const CGTeleport *>(obj);
|
||||||
|
if(TeleportChannel::IMPASSABLE == ai->knownTeleportChannels[tObj->channel]->passability)
|
||||||
|
break;
|
||||||
|
for(auto exit : ai->knownTeleportChannels[tObj->channel]->exits)
|
||||||
|
{
|
||||||
|
if(!cb->getObj(exit))
|
||||||
|
{ // Always attempt to visit two-way teleports if one of channel exits is not visible
|
||||||
|
objs.push_back(obj);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
163
AI/VCAI/VCAI.cpp
163
AI/VCAI/VCAI.cpp
@ -95,6 +95,7 @@ VCAI::VCAI(void)
|
|||||||
{
|
{
|
||||||
LOG_TRACE(logAi);
|
LOG_TRACE(logAi);
|
||||||
makingTurn = nullptr;
|
makingTurn = nullptr;
|
||||||
|
destinationTeleport = ObjectInstanceID();
|
||||||
}
|
}
|
||||||
|
|
||||||
VCAI::~VCAI(void)
|
VCAI::~VCAI(void)
|
||||||
@ -122,11 +123,19 @@ void VCAI::heroMoved(const TryMoveHero & details)
|
|||||||
const CGObjectInstance *o1 = frontOrNull(cb->getVisitableObjs(from)),
|
const CGObjectInstance *o1 = frontOrNull(cb->getVisitableObjs(from)),
|
||||||
*o2 = frontOrNull(cb->getVisitableObjs(to));
|
*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;
|
if(cb->isTeleportChannelBidirectional(t1->channel))
|
||||||
knownSubterraneanGates[o2] = o1;
|
{
|
||||||
logAi->debugStream() << boost::format("Found a pair of subterranean gates between %s and %s!") % from % to;
|
if(o1->ID == Obj::SUBTERRANEAN_GATE && o1->ID == o2->ID) // We need to only add subterranean gates in knownSubterraneanGates. Used for features not yet ported to use teleport channels
|
||||||
|
{
|
||||||
|
knownSubterraneanGates[o1] = o2;
|
||||||
|
knownSubterraneanGates[o2] = o1;
|
||||||
|
logAi->debugStream() << boost::format("Found a pair of subterranean gates between %s and %s!") % from % to;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -505,7 +514,7 @@ void VCAI::objectPropertyChanged(const SetObjectProperty * sop)
|
|||||||
auto obj = myCb->getObj(sop->id, false);
|
auto obj = myCb->getObj(sop->id, false);
|
||||||
if (obj)
|
if (obj)
|
||||||
{
|
{
|
||||||
visitableObjs.insert(obj);
|
addVisitableObj(obj);
|
||||||
erase_if_present(alreadyVisited, obj);
|
erase_if_present(alreadyVisited, obj);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -550,7 +559,7 @@ void VCAI::init(shared_ptr<CCallback> CB)
|
|||||||
if(!fh)
|
if(!fh)
|
||||||
fh = new FuzzyHelper();
|
fh = new FuzzyHelper();
|
||||||
|
|
||||||
retreiveVisitableObjs(visitableObjs);
|
retreiveVisitableObjs();
|
||||||
}
|
}
|
||||||
|
|
||||||
void VCAI::yourTurn()
|
void VCAI::yourTurn()
|
||||||
@ -597,6 +606,36 @@ 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::copy_if(exits, vstd::set_inserter(teleportChannelProbingList), [&](ObjectInstanceID id) -> bool
|
||||||
|
{
|
||||||
|
return !(vstd::contains(visitableObjs, cb->getObj(id)) || id == choosenExit);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
requestActionASAP([=]()
|
||||||
|
{
|
||||||
|
answerQuery(askID, choosenExit.getNum());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
void VCAI::showGarrisonDialog(const CArmedInstance *up, const CGHeroInstance *down, bool removableUnits, QueryID queryID)
|
void VCAI::showGarrisonDialog(const CArmedInstance *up, const CGHeroInstance *down, bool removableUnits, QueryID queryID)
|
||||||
{
|
{
|
||||||
LOG_TRACE_PARAMS(logAi, "removableUnits '%i', queryID '%i'", removableUnits % queryID);
|
LOG_TRACE_PARAMS(logAi, "removableUnits '%i', queryID '%i'", removableUnits % queryID);
|
||||||
@ -674,7 +713,7 @@ void VCAI::makeTurn()
|
|||||||
{
|
{
|
||||||
if (isWeeklyRevisitable(obj))
|
if (isWeeklyRevisitable(obj))
|
||||||
{
|
{
|
||||||
visitableObjs.insert(obj); //set doesn't need duplicate check
|
addVisitableObj(obj);
|
||||||
erase_if_present (alreadyVisited, obj);
|
erase_if_present (alreadyVisited, obj);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1559,14 +1598,15 @@ void VCAI::retreiveVisitableObjs(std::vector<const CGObjectInstance *> &out, boo
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
void VCAI::retreiveVisitableObjs(std::set<const CGObjectInstance *> &out, bool includeOwned /*= false*/) const
|
|
||||||
|
void VCAI::retreiveVisitableObjs()
|
||||||
{
|
{
|
||||||
foreach_tile_pos([&](const int3 &pos)
|
foreach_tile_pos([&](const int3 &pos)
|
||||||
{
|
{
|
||||||
for(const CGObjectInstance *obj : myCb->getVisitableObjs(pos, false))
|
for(const CGObjectInstance *obj : myCb->getVisitableObjs(pos, false))
|
||||||
{
|
{
|
||||||
if(includeOwned || obj->tempOwner != playerID)
|
if(obj->tempOwner != playerID)
|
||||||
out.insert(obj);
|
addVisitableObj(obj);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -1586,6 +1626,11 @@ void VCAI::addVisitableObj(const CGObjectInstance *obj)
|
|||||||
{
|
{
|
||||||
visitableObjs.insert(obj);
|
visitableObjs.insert(obj);
|
||||||
helperObjInfo[obj] = ObjInfo(obj);
|
helperObjInfo[obj] = ObjInfo(obj);
|
||||||
|
|
||||||
|
// All teleport objects seen automatically assigned to appropriate channels
|
||||||
|
auto teleportObj = dynamic_cast<const CGTeleport *>(obj);
|
||||||
|
if(teleportObj)
|
||||||
|
CGTeleport::addToChannel(knownTeleportChannels, teleportObj);
|
||||||
}
|
}
|
||||||
|
|
||||||
const CGObjectInstance * VCAI::lookForArt(int aid) const
|
const CGObjectInstance * VCAI::lookForArt(int aid) const
|
||||||
@ -1650,6 +1695,19 @@ bool VCAI::isAccessibleForHero(const int3 & pos, HeroPtr h, bool includeAllies /
|
|||||||
|
|
||||||
bool VCAI::moveHeroToTile(int3 dst, HeroPtr h)
|
bool VCAI::moveHeroToTile(int3 dst, HeroPtr h)
|
||||||
{
|
{
|
||||||
|
auto afterMovementCheck = [&]() -> void
|
||||||
|
{
|
||||||
|
waitTillFree(); //movement may cause battle or blocking dialog
|
||||||
|
if(!h)
|
||||||
|
{
|
||||||
|
lostHero(h);
|
||||||
|
teleportChannelProbingList.clear();
|
||||||
|
if (status.channelProbing()) // if hero lost during channel probing we need to switch this mode off
|
||||||
|
status.setChannelProbing(false);
|
||||||
|
throw cannotFulfillGoalException("Hero was lost!");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
logAi->debugStream() << boost::format("Moving hero %s to tile %s") % h->name % dst;
|
logAi->debugStream() << boost::format("Moving hero %s to tile %s") % h->name % dst;
|
||||||
int3 startHpos = h->visitablePos();
|
int3 startHpos = h->visitablePos();
|
||||||
bool ret = false;
|
bool ret = false;
|
||||||
@ -1658,12 +1716,8 @@ bool VCAI::moveHeroToTile(int3 dst, HeroPtr h)
|
|||||||
//FIXME: this assertion fails also if AI moves onto defeated guarded object
|
//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
|
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
|
afterMovementCheck();// TODO: is it feasible to hero get killed there if game work properly?
|
||||||
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.
|
||||||
{ // 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!");
|
|
||||||
}
|
|
||||||
ret = true;
|
ret = true;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -1676,9 +1730,54 @@ bool VCAI::moveHeroToTile(int3 dst, HeroPtr h)
|
|||||||
throw goalFulfilledException (sptr(Goals::VisitTile(dst).sethero(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;
|
int i=path.nodes.size()-1;
|
||||||
for(; i>0; i--)
|
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)
|
//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)
|
if(path.nodes[i-1].turns)
|
||||||
{
|
{
|
||||||
@ -1690,16 +1789,19 @@ bool VCAI::moveHeroToTile(int3 dst, HeroPtr h)
|
|||||||
if(endpos == h->visitablePos())
|
if(endpos == h->visitablePos())
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
cb->moveHero(*h, CGHeroInstance::convertPosition(endpos, true));
|
if((i-2 >= 0) // Check there is node after next one; otherwise transit is pointless
|
||||||
waitTillFree(); //movement may cause battle or blocking dialog
|
&& (CGTeleport::isConnected(nextObject, getObj(path.nodes[i-2].coord))
|
||||||
boost::this_thread::interruption_point();
|
|| CGTeleport::isTeleport(nextObject)))
|
||||||
if(!h) //we lost hero - remove all tasks assigned to him/her
|
{ // Hero should be able to go through object if it's allow transit
|
||||||
{
|
doMovement(endpos, true);
|
||||||
lostHero(h);
|
|
||||||
//we need to throw, otherwise hero will be assigned to sth again
|
|
||||||
throw std::runtime_error("Hero was lost!");
|
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
doMovement(endpos);
|
||||||
|
|
||||||
|
afterMovementCheck();
|
||||||
|
|
||||||
|
if(teleportChannelProbingList.size())
|
||||||
|
doChannelProbing();
|
||||||
}
|
}
|
||||||
ret = !i;
|
ret = !i;
|
||||||
}
|
}
|
||||||
@ -2531,6 +2633,7 @@ AIStatus::AIStatus()
|
|||||||
battle = NO_BATTLE;
|
battle = NO_BATTLE;
|
||||||
havingTurn = false;
|
havingTurn = false;
|
||||||
ongoingHeroMovement = false;
|
ongoingHeroMovement = false;
|
||||||
|
ongoingChannelProbing = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
AIStatus::~AIStatus()
|
AIStatus::~AIStatus()
|
||||||
@ -2663,6 +2766,18 @@ void AIStatus::setMove(bool ongoing)
|
|||||||
cv.notify_all();
|
cv.notify_all();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void AIStatus::setChannelProbing(bool ongoing)
|
||||||
|
{
|
||||||
|
boost::unique_lock<boost::mutex> lock(mx);
|
||||||
|
ongoingHeroMovement = ongoing;
|
||||||
|
cv.notify_all();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AIStatus::channelProbing()
|
||||||
|
{
|
||||||
|
return ongoingChannelProbing;
|
||||||
|
}
|
||||||
|
|
||||||
SectorMap::SectorMap()
|
SectorMap::SectorMap()
|
||||||
{
|
{
|
||||||
update();
|
update();
|
||||||
|
@ -41,6 +41,7 @@ class AIStatus
|
|||||||
std::map<int, QueryID> requestToQueryID; //IDs of answer-requests sent to server => query ids (so we can match answer confirmation from server to the query)
|
std::map<int, QueryID> requestToQueryID; //IDs of answer-requests sent to server => query ids (so we can match answer confirmation from server to the query)
|
||||||
std::vector<const CGObjectInstance*> objectsBeingVisited;
|
std::vector<const CGObjectInstance*> objectsBeingVisited;
|
||||||
bool ongoingHeroMovement;
|
bool ongoingHeroMovement;
|
||||||
|
bool ongoingChannelProbing; // true if AI currently explore bidirectional teleport channel exits
|
||||||
|
|
||||||
bool havingTurn;
|
bool havingTurn;
|
||||||
|
|
||||||
@ -49,6 +50,8 @@ public:
|
|||||||
~AIStatus();
|
~AIStatus();
|
||||||
void setBattle(BattleState BS);
|
void setBattle(BattleState BS);
|
||||||
void setMove(bool ongoing);
|
void setMove(bool ongoing);
|
||||||
|
void setChannelProbing(bool ongoing);
|
||||||
|
bool channelProbing();
|
||||||
BattleState getBattle();
|
BattleState getBattle();
|
||||||
void addQuery(QueryID ID, std::string description);
|
void addQuery(QueryID ID, std::string description);
|
||||||
void removeQuery(QueryID ID);
|
void removeQuery(QueryID ID);
|
||||||
@ -138,7 +141,10 @@ public:
|
|||||||
|
|
||||||
friend class FuzzyHelper;
|
friend class FuzzyHelper;
|
||||||
|
|
||||||
|
std::map<TeleportChannelID, shared_ptr<TeleportChannel> > knownTeleportChannels;
|
||||||
std::map<const CGObjectInstance *, const CGObjectInstance *> knownSubterraneanGates;
|
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::vector<const CGObjectInstance *> visitedThisWeek; //only OPWs
|
||||||
std::map<HeroPtr, std::set<const CGTownInstance *> > townVisitsThisWeek;
|
std::map<HeroPtr, std::set<const CGTownInstance *> > townVisitsThisWeek;
|
||||||
|
|
||||||
@ -190,6 +196,7 @@ public:
|
|||||||
virtual void commanderGotLevel (const CCommanderInstance * commander, std::vector<ui32> skills, QueryID queryID) override; //TODO
|
virtual void commanderGotLevel (const CCommanderInstance * commander, std::vector<ui32> skills, QueryID queryID) override; //TODO
|
||||||
virtual void showBlockingDialog(const std::string &text, const std::vector<Component> &components, QueryID askID, const int soundID, bool selection, bool cancel) override; //Show a dialog, player must take decision. If selection then he has to choose between one of given components, if cancel he is allowed to not choose. After making choice, CCallback::selectionMade should be called with number of selected component (1 - n) or 0 for cancel (if allowed) and askID.
|
virtual void showBlockingDialog(const std::string &text, const std::vector<Component> &components, QueryID askID, const int soundID, bool selection, bool cancel) override; //Show a dialog, player must take decision. If selection then he has to choose between one of given components, if cancel he is allowed to not choose. After making choice, CCallback::selectionMade should be called with number of selected component (1 - n) or 0 for cancel (if allowed) and askID.
|
||||||
virtual void showGarrisonDialog(const CArmedInstance *up, const CGHeroInstance *down, bool removableUnits, QueryID queryID) override; //all stacks operations between these objects become allowed, interface has to call onEnd when done
|
virtual void showGarrisonDialog(const CArmedInstance *up, const CGHeroInstance *down, bool removableUnits, QueryID queryID) override; //all stacks operations between these objects become allowed, interface has to call onEnd when done
|
||||||
|
virtual void showTeleportDialog(TeleportChannelID channel, std::vector<ObjectInstanceID> exits, bool impassable, QueryID askID) override;
|
||||||
virtual void saveGame(COSer & h, const int version) override; //saving
|
virtual void saveGame(COSer & h, const int version) override; //saving
|
||||||
virtual void loadGame(CISer & h, const int version) override; //loading
|
virtual void loadGame(CISer & h, const int version) override; //loading
|
||||||
virtual void finish() override;
|
virtual void finish() override;
|
||||||
@ -290,7 +297,7 @@ public:
|
|||||||
void validateObject(ObjectIdRef obj); //checks if object is still visible and if not, removes references to it
|
void validateObject(ObjectIdRef obj); //checks if object is still visible and if not, removes references to it
|
||||||
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;
|
||||||
void retreiveVisitableObjs(std::set<const CGObjectInstance *> &out, bool includeOwned = false) const;
|
void retreiveVisitableObjs();
|
||||||
std::vector<const CGObjectInstance *> getFlaggedObjects() const;
|
std::vector<const CGObjectInstance *> getFlaggedObjects() const;
|
||||||
|
|
||||||
const CGObjectInstance *lookForArt(int aid) const;
|
const CGObjectInstance *lookForArt(int aid) const;
|
||||||
@ -343,7 +350,8 @@ public:
|
|||||||
|
|
||||||
template <typename Handler> void serializeInternal(Handler &h, const int version)
|
template <typename Handler> void serializeInternal(Handler &h, const int version)
|
||||||
{
|
{
|
||||||
h & knownSubterraneanGates & townVisitsThisWeek & lockedHeroes & reservedHeroesMap; //FIXME: cannot instantiate abstract class
|
h & knownTeleportChannels & knownSubterraneanGates & destinationTeleport;
|
||||||
|
h & townVisitsThisWeek & lockedHeroes & reservedHeroesMap; //FIXME: cannot instantiate abstract class
|
||||||
h & visitableObjs & alreadyVisited & reservedObjs;
|
h & visitableObjs & alreadyVisited & reservedObjs;
|
||||||
h & saving & status & battlename;
|
h & saving & status & battlename;
|
||||||
h & heroesUnableToExplore;
|
h & heroesUnableToExplore;
|
||||||
|
3
AUTHORS
3
AUTHORS
@ -51,3 +51,6 @@ Alexey aka Macron1Robot, <>
|
|||||||
|
|
||||||
Alexander Shishkin aka alexvins,
|
Alexander Shishkin aka alexvins,
|
||||||
* MinGW platform support, modding related programming
|
* MinGW platform support, modding related programming
|
||||||
|
|
||||||
|
Arseniy Shestakov aka SXX,
|
||||||
|
* pathfinding improvements, programming
|
||||||
|
@ -48,9 +48,9 @@ bool CCallback::teleportHero(const CGHeroInstance *who, const CGTownInstance *wh
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CCallback::moveHero(const CGHeroInstance *h, int3 dst)
|
bool CCallback::moveHero(const CGHeroInstance *h, int3 dst, bool transit)
|
||||||
{
|
{
|
||||||
MoveHero pack(dst,h->id);
|
MoveHero pack(dst,h->id,transit);
|
||||||
sendRequest(&pack);
|
sendRequest(&pack);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -46,7 +46,7 @@ class IGameActionCallback
|
|||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
//hero
|
//hero
|
||||||
virtual bool moveHero(const CGHeroInstance *h, int3 dst) =0; //dst must be free, neighbouring tile (this function can move hero only by one tile)
|
virtual bool moveHero(const CGHeroInstance *h, int3 dst, bool transit) =0; //dst must be free, neighbouring tile (this function can move hero only by one tile)
|
||||||
virtual bool dismissHero(const CGHeroInstance * hero)=0; //dismisses given hero; true - successfuly, false - not successfuly
|
virtual bool dismissHero(const CGHeroInstance * hero)=0; //dismisses given hero; true - successfuly, false - not successfuly
|
||||||
virtual void dig(const CGObjectInstance *hero)=0;
|
virtual void dig(const CGObjectInstance *hero)=0;
|
||||||
virtual void castSpell(const CGHeroInstance *hero, SpellID spellID, const int3 &pos = int3(-1, -1, -1))=0; //cast adventure map spell
|
virtual void castSpell(const CGHeroInstance *hero, SpellID spellID, const int3 &pos = int3(-1, -1, -1))=0; //cast adventure map spell
|
||||||
@ -119,7 +119,7 @@ public:
|
|||||||
void unregisterAllInterfaces(); //stops delivering information about game events to player interfaces -> can be called ONLY after victory/loss
|
void unregisterAllInterfaces(); //stops delivering information about game events to player interfaces -> can be called ONLY after victory/loss
|
||||||
|
|
||||||
//commands
|
//commands
|
||||||
bool moveHero(const CGHeroInstance *h, int3 dst); //dst must be free, neighbouring tile (this function can move hero only by one tile)
|
bool moveHero(const CGHeroInstance *h, int3 dst, bool transit = false); //dst must be free, neighbouring tile (this function can move hero only by one tile)
|
||||||
bool teleportHero(const CGHeroInstance *who, const CGTownInstance *where);
|
bool teleportHero(const CGHeroInstance *who, const CGTownInstance *where);
|
||||||
int selectionMade(int selection, QueryID queryID);
|
int selectionMade(int selection, QueryID queryID);
|
||||||
int swapCreatures(const CArmedInstance *s1, const CArmedInstance *s2, SlotID p1, SlotID p2);
|
int swapCreatures(const CArmedInstance *s1, const CArmedInstance *s2, SlotID p1, SlotID p2);
|
||||||
|
@ -1,4 +1,11 @@
|
|||||||
0.97 -> 0.98
|
0.97 -> 0.98
|
||||||
|
GENERAL:
|
||||||
|
* Pathfinder can now find way using monoliths and whirlpools (only used if hero have protection)
|
||||||
|
|
||||||
|
ADVENTURE AI:
|
||||||
|
* AI will try to use Monolith entrances for exploration
|
||||||
|
* AI will now always revisit each exit of two way monolith if exit no longer visible
|
||||||
|
|
||||||
ADVENTURE MAP:
|
ADVENTURE MAP:
|
||||||
* Implemented world veiw
|
* Implemented world veiw
|
||||||
|
|
||||||
|
10
Global.h
10
Global.h
@ -664,6 +664,16 @@ namespace vstd
|
|||||||
dest.insert(dest.end(), src.begin(), src.end());
|
dest.insert(dest.end(), src.begin(), src.end());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
std::vector<T> intersection(std::vector<T> &v1, std::vector<T> &v2)
|
||||||
|
{
|
||||||
|
std::vector<T> v3;
|
||||||
|
std::sort(v1.begin(), v1.end());
|
||||||
|
std::sort(v2.begin(), v2.end());
|
||||||
|
std::set_intersection(v1.begin(), v1.end(), v2.begin(), v2.end(), std::back_inserter(v3));
|
||||||
|
return v3;
|
||||||
|
}
|
||||||
|
|
||||||
using boost::math::round;
|
using boost::math::round;
|
||||||
}
|
}
|
||||||
using vstd::operator-=;
|
using vstd::operator-=;
|
||||||
|
@ -97,6 +97,7 @@ static bool objectBlitOrderSorter(const TerrainTileObject & a, const TerrainTil
|
|||||||
CPlayerInterface::CPlayerInterface(PlayerColor Player)
|
CPlayerInterface::CPlayerInterface(PlayerColor Player)
|
||||||
{
|
{
|
||||||
logGlobal->traceStream() << "\tHuman player interface for player " << Player << " being constructed";
|
logGlobal->traceStream() << "\tHuman player interface for player " << Player << " being constructed";
|
||||||
|
destinationTeleport = ObjectInstanceID();
|
||||||
observerInDuelMode = false;
|
observerInDuelMode = false;
|
||||||
howManyPeople++;
|
howManyPeople++;
|
||||||
GH.defActionsDef = 0;
|
GH.defActionsDef = 0;
|
||||||
@ -1141,6 +1142,16 @@ void CPlayerInterface::showBlockingDialog( const std::string &text, const std::v
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CPlayerInterface::showTeleportDialog(TeleportChannelID channel, std::vector<ObjectInstanceID> exits, bool impassable, QueryID askID)
|
||||||
|
{
|
||||||
|
EVENT_HANDLER_CALLED_BY_CLIENT;
|
||||||
|
ObjectInstanceID choosenExit;
|
||||||
|
if(destinationTeleport != ObjectInstanceID() && vstd::contains(exits, destinationTeleport))
|
||||||
|
choosenExit = destinationTeleport;
|
||||||
|
|
||||||
|
cb->selectionMade(choosenExit.getNum(), askID);
|
||||||
|
}
|
||||||
|
|
||||||
void CPlayerInterface::tileRevealed(const std::unordered_set<int3, ShashInt3> &pos)
|
void CPlayerInterface::tileRevealed(const std::unordered_set<int3, ShashInt3> &pos)
|
||||||
{
|
{
|
||||||
EVENT_HANDLER_CALLED_BY_CLIENT;
|
EVENT_HANDLER_CALLED_BY_CLIENT;
|
||||||
@ -1389,8 +1400,17 @@ void CPlayerInterface::showArtifactAssemblyDialog (ui32 artifactID, ui32 assembl
|
|||||||
void CPlayerInterface::requestRealized( PackageApplied *pa )
|
void CPlayerInterface::requestRealized( PackageApplied *pa )
|
||||||
{
|
{
|
||||||
EVENT_HANDLER_CALLED_BY_CLIENT;
|
EVENT_HANDLER_CALLED_BY_CLIENT;
|
||||||
if(pa->packType == typeList.getTypeID<MoveHero>() && stillMoveHero.get() == DURING_MOVE)
|
if(pa->packType == typeList.getTypeID<MoveHero>() && stillMoveHero.get() == DURING_MOVE
|
||||||
|
&& destinationTeleport == ObjectInstanceID())
|
||||||
stillMoveHero.setn(CONTINUE_MOVE);
|
stillMoveHero.setn(CONTINUE_MOVE);
|
||||||
|
|
||||||
|
if(destinationTeleport != ObjectInstanceID()
|
||||||
|
&& pa->packType == typeList.getTypeID<QueryReply>()
|
||||||
|
&& stillMoveHero.get() == DURING_MOVE)
|
||||||
|
{ // After teleportation via CGTeleport object is finished
|
||||||
|
destinationTeleport = ObjectInstanceID();
|
||||||
|
stillMoveHero.setn(CONTINUE_MOVE);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void CPlayerInterface::heroExchangeStarted(ObjectInstanceID hero1, ObjectInstanceID hero2, QueryID query)
|
void CPlayerInterface::heroExchangeStarted(ObjectInstanceID hero1, ObjectInstanceID hero2, QueryID query)
|
||||||
@ -2631,30 +2651,47 @@ bool CPlayerInterface::capturedAllEvents()
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CPlayerInterface::doMoveHero(const CGHeroInstance* h, CGPath path)
|
void CPlayerInterface::doMoveHero(const CGHeroInstance * h, CGPath path)
|
||||||
{
|
{
|
||||||
int i = 1;
|
int i = 1;
|
||||||
|
auto getObj = [&](int3 coord, bool ignoreHero = false)
|
||||||
|
{
|
||||||
|
return cb->getTile(CGHeroInstance::convertPosition(coord,false))->topVisitableObj(ignoreHero);
|
||||||
|
};
|
||||||
|
|
||||||
|
boost::unique_lock<boost::mutex> un(stillMoveHero.mx);
|
||||||
|
stillMoveHero.data = CONTINUE_MOVE;
|
||||||
|
auto doMovement = [&](int3 dst, bool transit = false)
|
||||||
|
{
|
||||||
|
stillMoveHero.data = WAITING_MOVE;
|
||||||
|
cb->moveHero(h, dst, transit);
|
||||||
|
while(stillMoveHero.data != STOP_MOVE && stillMoveHero.data != CONTINUE_MOVE)
|
||||||
|
stillMoveHero.cond.wait(un);
|
||||||
|
};
|
||||||
|
|
||||||
{
|
{
|
||||||
path.convert(0);
|
path.convert(0);
|
||||||
boost::unique_lock<boost::mutex> un(stillMoveHero.mx);
|
|
||||||
stillMoveHero.data = CONTINUE_MOVE;
|
|
||||||
|
|
||||||
ETerrainType currentTerrain = ETerrainType::BORDER; // not init yet
|
ETerrainType currentTerrain = ETerrainType::BORDER; // not init yet
|
||||||
ETerrainType newTerrain;
|
ETerrainType newTerrain;
|
||||||
int sh = -1;
|
int sh = -1;
|
||||||
|
|
||||||
const TerrainTile * curTile = cb->getTile(CGHeroInstance::convertPosition(h->pos, false));
|
for(i=path.nodes.size()-1; i>0 && (stillMoveHero.data == CONTINUE_MOVE); i--)
|
||||||
|
|
||||||
for(i=path.nodes.size()-1; i>0 && (stillMoveHero.data == CONTINUE_MOVE || curTile->blocked); i--)
|
|
||||||
{
|
{
|
||||||
//changing z coordinate means we're moving through subterranean gate -> it's done automatically upon the visit, so we don't have to request that move here
|
int3 currentCoord = path.nodes[i].coord;
|
||||||
if(path.nodes[i-1].coord.z != path.nodes[i].coord.z)
|
int3 nextCoord = path.nodes[i-1].coord;
|
||||||
continue;
|
|
||||||
|
|
||||||
//stop sending move requests if the next node can't be reached at the current turn (hero exhausted his move points)
|
auto nextObject = getObj(nextCoord, nextCoord == h->pos);
|
||||||
if(path.nodes[i-1].turns)
|
if(CGTeleport::isConnected(getObj(currentCoord, currentCoord == h->pos), nextObject))
|
||||||
{
|
{
|
||||||
|
CCS->soundh->stopSound(sh);
|
||||||
|
destinationTeleport = nextObject->id;
|
||||||
|
doMovement(h->pos);
|
||||||
|
sh = CCS->soundh->playSound(CCS->soundh->horseSounds[currentTerrain], -1);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(path.nodes[i-1].turns)
|
||||||
|
{ //stop sending move requests if the next node can't be reached at the current turn (hero exhausted his move points)
|
||||||
stillMoveHero.data = STOP_MOVE;
|
stillMoveHero.data = STOP_MOVE;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -2662,13 +2699,12 @@ void CPlayerInterface::doMoveHero(const CGHeroInstance* h, CGPath path)
|
|||||||
// Start a new sound for the hero movement or let the existing one carry on.
|
// Start a new sound for the hero movement or let the existing one carry on.
|
||||||
#if 0
|
#if 0
|
||||||
// TODO
|
// TODO
|
||||||
if (hero is flying && sh == -1)
|
if(hero is flying && sh == -1)
|
||||||
sh = CCS->soundh->playSound(soundBase::horseFlying, -1);
|
sh = CCS->soundh->playSound(soundBase::horseFlying, -1);
|
||||||
#endif
|
#endif
|
||||||
{
|
{
|
||||||
newTerrain = cb->getTile(CGHeroInstance::convertPosition(path.nodes[i].coord, false))->terType;
|
newTerrain = cb->getTile(CGHeroInstance::convertPosition(currentCoord, false))->terType;
|
||||||
|
if(newTerrain != currentTerrain)
|
||||||
if (newTerrain != currentTerrain)
|
|
||||||
{
|
{
|
||||||
CCS->soundh->stopSound(sh);
|
CCS->soundh->stopSound(sh);
|
||||||
sh = CCS->soundh->playSound(CCS->soundh->horseSounds[newTerrain], -1);
|
sh = CCS->soundh->playSound(CCS->soundh->horseSounds[newTerrain], -1);
|
||||||
@ -2676,19 +2712,22 @@ void CPlayerInterface::doMoveHero(const CGHeroInstance* h, CGPath path)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
stillMoveHero.data = WAITING_MOVE;
|
assert(h->pos.z == nextCoord.z); // Z should change only if it's movement via teleporter and in this case this code shouldn't be executed at all
|
||||||
|
int3 endpos(nextCoord.x, nextCoord.y, h->pos.z);
|
||||||
int3 endpos(path.nodes[i-1].coord.x, path.nodes[i-1].coord.y, h->pos.z);
|
|
||||||
bool guarded = CGI->mh->map->isInTheMap(cb->getGuardingCreaturePosition(endpos - int3(1, 0, 0)));
|
|
||||||
|
|
||||||
logGlobal->traceStream() << "Requesting hero movement to " << endpos;
|
logGlobal->traceStream() << "Requesting hero movement to " << endpos;
|
||||||
cb->moveHero(h,endpos);
|
|
||||||
|
|
||||||
while(stillMoveHero.data != STOP_MOVE && stillMoveHero.data != CONTINUE_MOVE)
|
if((i-2 >= 0) // Check there is node after next one; otherwise transit is pointless
|
||||||
stillMoveHero.cond.wait(un);
|
&& (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);
|
||||||
|
|
||||||
logGlobal->traceStream() << "Resuming " << __FUNCTION__;
|
logGlobal->traceStream() << "Resuming " << __FUNCTION__;
|
||||||
if (guarded || showingDialog->get() == true) // Abort movement if a guard was fought or there is a dialog to display (Mantis #1136)
|
bool guarded = cb->isInTheMap(cb->getGuardingCreaturePosition(endpos - int3(1, 0, 0)));
|
||||||
|
if(guarded || showingDialog->get() == true) // Abort movement if a guard was fought or there is a dialog to display (Mantis #1136)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2701,7 +2740,7 @@ void CPlayerInterface::doMoveHero(const CGHeroInstance* h, CGPath path)
|
|||||||
|
|
||||||
|
|
||||||
//todo: this should be in main thread
|
//todo: this should be in main thread
|
||||||
if (adventureInt)
|
if(adventureInt)
|
||||||
{
|
{
|
||||||
// (i == 0) means hero went through all the path
|
// (i == 0) means hero went through all the path
|
||||||
adventureInt->updateMoveHero(h, (i != 0));
|
adventureInt->updateMoveHero(h, (i != 0));
|
||||||
|
@ -89,6 +89,7 @@ class CPlayerInterface : public CGameInterface, public ILockedUpdatable
|
|||||||
const CArmedInstance * currentSelection;
|
const CArmedInstance * currentSelection;
|
||||||
public:
|
public:
|
||||||
bool observerInDuelMode;
|
bool observerInDuelMode;
|
||||||
|
ObjectInstanceID destinationTeleport; //contain -1 or object id if teleportation
|
||||||
|
|
||||||
//minor interfaces
|
//minor interfaces
|
||||||
CondSh<bool> *showingDialog; //indicates if dialog box is displayed
|
CondSh<bool> *showingDialog; //indicates if dialog box is displayed
|
||||||
@ -168,6 +169,7 @@ public:
|
|||||||
void showRecruitmentDialog(const CGDwelling *dwelling, const CArmedInstance *dst, int level) override;
|
void showRecruitmentDialog(const CGDwelling *dwelling, const CArmedInstance *dst, int level) override;
|
||||||
void showShipyardDialog(const IShipyard *obj) override; //obj may be town or shipyard;
|
void showShipyardDialog(const IShipyard *obj) override; //obj may be town or shipyard;
|
||||||
void showBlockingDialog(const std::string &text, const std::vector<Component> &components, QueryID askID, int soundID, bool selection, bool cancel) override; //Show a dialog, player must take decision. If selection then he has to choose between one of given components, if cancel he is allowed to not choose. After making choice, CCallback::selectionMade should be called with number of selected component (1 - n) or 0 for cancel (if allowed) and askID.
|
void showBlockingDialog(const std::string &text, const std::vector<Component> &components, QueryID askID, int soundID, bool selection, bool cancel) override; //Show a dialog, player must take decision. If selection then he has to choose between one of given components, if cancel he is allowed to not choose. After making choice, CCallback::selectionMade should be called with number of selected component (1 - n) or 0 for cancel (if allowed) and askID.
|
||||||
|
void showTeleportDialog(TeleportChannelID channel, std::vector<ObjectInstanceID> exits, bool impassable, QueryID askID) override;
|
||||||
void showGarrisonDialog(const CArmedInstance *up, const CGHeroInstance *down, bool removableUnits, QueryID queryID) override;
|
void showGarrisonDialog(const CArmedInstance *up, const CGHeroInstance *down, bool removableUnits, QueryID queryID) override;
|
||||||
void showPuzzleMap() override;
|
void showPuzzleMap() override;
|
||||||
void viewWorldMap() override;
|
void viewWorldMap() override;
|
||||||
|
@ -179,6 +179,7 @@ public:
|
|||||||
|
|
||||||
void showBlockingDialog(BlockingDialog *iw) override {};
|
void showBlockingDialog(BlockingDialog *iw) override {};
|
||||||
void showGarrisonDialog(ObjectInstanceID upobj, ObjectInstanceID hid, bool removableUnits) override {};
|
void showGarrisonDialog(ObjectInstanceID upobj, ObjectInstanceID hid, bool removableUnits) override {};
|
||||||
|
void showTeleportDialog(TeleportDialog *iw) override {};
|
||||||
void showThievesGuildWindow(PlayerColor player, ObjectInstanceID requestingObjId) override {};
|
void showThievesGuildWindow(PlayerColor player, ObjectInstanceID requestingObjId) override {};
|
||||||
void giveResource(PlayerColor player, Res::ERes which, int val) override {};
|
void giveResource(PlayerColor player, Res::ERes which, int val) override {};
|
||||||
virtual void giveResources(PlayerColor player, TResources resources) override {};
|
virtual void giveResources(PlayerColor player, TResources resources) override {};
|
||||||
@ -212,7 +213,7 @@ public:
|
|||||||
void startBattleI(const CArmedInstance *army1, const CArmedInstance *army2, int3 tile, bool creatureBank = false) override {}; //if any of armies is hero, hero will be used
|
void startBattleI(const CArmedInstance *army1, const CArmedInstance *army2, int3 tile, bool creatureBank = false) override {}; //if any of armies is hero, hero will be used
|
||||||
void startBattleI(const CArmedInstance *army1, const CArmedInstance *army2, bool creatureBank = false) override {}; //if any of armies is hero, hero will be used, visitable tile of second obj is place of battle
|
void startBattleI(const CArmedInstance *army1, const CArmedInstance *army2, bool creatureBank = false) override {}; //if any of armies is hero, hero will be used, visitable tile of second obj is place of battle
|
||||||
void setAmount(ObjectInstanceID objid, ui32 val) override {};
|
void setAmount(ObjectInstanceID objid, ui32 val) override {};
|
||||||
bool moveHero(ObjectInstanceID hid, int3 dst, ui8 teleporting, PlayerColor asker = PlayerColor::NEUTRAL) override {return false;};
|
bool moveHero(ObjectInstanceID hid, int3 dst, ui8 teleporting, bool transit = false, PlayerColor asker = PlayerColor::NEUTRAL) override {return false;};
|
||||||
void giveHeroBonus(GiveBonus * bonus) override {};
|
void giveHeroBonus(GiveBonus * bonus) override {};
|
||||||
void setMovePoints(SetMovePoints * smp) override {};
|
void setMovePoints(SetMovePoints * smp) override {};
|
||||||
void setManaPoints(ObjectInstanceID hid, int val) override {};
|
void setManaPoints(ObjectInstanceID hid, int val) override {};
|
||||||
|
@ -595,6 +595,11 @@ void ExchangeDialog::applyCl(CClient *cl)
|
|||||||
INTERFACE_CALL_IF_PRESENT(heroes[0]->tempOwner, heroExchangeStarted, heroes[0]->id, heroes[1]->id, queryID);
|
INTERFACE_CALL_IF_PRESENT(heroes[0]->tempOwner, heroExchangeStarted, heroes[0]->id, heroes[1]->id, queryID);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void TeleportDialog::applyCl( CClient *cl )
|
||||||
|
{
|
||||||
|
CALL_ONLY_THAT_INTERFACE(hero->tempOwner,showTeleportDialog,channel,exits,impassable,queryID);
|
||||||
|
}
|
||||||
|
|
||||||
void BattleStart::applyFirstCl( CClient *cl )
|
void BattleStart::applyFirstCl( CClient *cl )
|
||||||
{
|
{
|
||||||
//Cannot use the usual macro because curB is not set yet
|
//Cannot use the usual macro because curB is not set yet
|
||||||
|
@ -153,7 +153,7 @@
|
|||||||
|
|
||||||
"whirlpool" : {
|
"whirlpool" : {
|
||||||
"index" :111,
|
"index" :111,
|
||||||
"handler" : "teleport",
|
"handler" : "whirlpool",
|
||||||
"types" : {
|
"types" : {
|
||||||
"object" : {
|
"object" : {
|
||||||
"index" : 0,
|
"index" : 0,
|
||||||
@ -164,7 +164,7 @@
|
|||||||
},
|
},
|
||||||
"subterraneanGate" : {
|
"subterraneanGate" : {
|
||||||
"index" :103,
|
"index" :103,
|
||||||
"handler" : "teleport",
|
"handler" : "subterraneanGate",
|
||||||
"types" : {
|
"types" : {
|
||||||
"object" : {
|
"object" : {
|
||||||
"index" : 0 },
|
"index" : 0 },
|
||||||
|
@ -200,7 +200,7 @@
|
|||||||
// Subtype: paired monoliths
|
// Subtype: paired monoliths
|
||||||
"monolithOneWayEntrance" : {
|
"monolithOneWayEntrance" : {
|
||||||
"index" :43,
|
"index" :43,
|
||||||
"handler": "teleport",
|
"handler": "monolith",
|
||||||
"types" : {
|
"types" : {
|
||||||
"monolith1" : { "index" : 0 },
|
"monolith1" : { "index" : 0 },
|
||||||
"monolith2" : { "index" : 1 },
|
"monolith2" : { "index" : 1 },
|
||||||
@ -214,7 +214,7 @@
|
|||||||
},
|
},
|
||||||
"monolithOneWayExit" : {
|
"monolithOneWayExit" : {
|
||||||
"index" :44,
|
"index" :44,
|
||||||
"handler": "teleport",
|
"handler": "monolith",
|
||||||
"types" : {
|
"types" : {
|
||||||
"monolith1" : { "index" : 0 },
|
"monolith1" : { "index" : 0 },
|
||||||
"monolith2" : { "index" : 1 },
|
"monolith2" : { "index" : 1 },
|
||||||
@ -228,7 +228,7 @@
|
|||||||
},
|
},
|
||||||
"monolithTwoWay" : {
|
"monolithTwoWay" : {
|
||||||
"index" :45,
|
"index" :45,
|
||||||
"handler": "teleport",
|
"handler": "monolith",
|
||||||
"types" : {
|
"types" : {
|
||||||
"monolith1" : { "index" : 0 },
|
"monolith1" : { "index" : 0 },
|
||||||
"monolith2" : { "index" : 1 },
|
"monolith2" : { "index" : 1 },
|
||||||
|
@ -790,6 +790,65 @@ const CGObjectInstance * CGameInfoCallback::getObjInstance( ObjectInstanceID oid
|
|||||||
return gs->map->objects[oid.num];
|
return gs->map->objects[oid.num];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::vector<ObjectInstanceID> CGameInfoCallback::getVisibleTeleportObjects(std::vector<ObjectInstanceID> ids, PlayerColor player) const
|
||||||
|
{
|
||||||
|
vstd::erase_if(ids, [&](ObjectInstanceID id) -> bool
|
||||||
|
{
|
||||||
|
auto obj = getObj(id);
|
||||||
|
return player != PlayerColor::UNFLAGGABLE && (!obj || !isVisible(obj->pos, player));
|
||||||
|
});
|
||||||
|
return ids;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<ObjectInstanceID> CGameInfoCallback::getTeleportChannelEntraces(TeleportChannelID id, PlayerColor player) const
|
||||||
|
{
|
||||||
|
return getVisibleTeleportObjects(gs->map->teleportChannels[id]->entrances, player);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<ObjectInstanceID> CGameInfoCallback::getTeleportChannelExits(TeleportChannelID id, PlayerColor player) const
|
||||||
|
{
|
||||||
|
return getVisibleTeleportObjects(gs->map->teleportChannels[id]->exits, player);
|
||||||
|
}
|
||||||
|
|
||||||
|
ETeleportChannelType CGameInfoCallback::getTeleportChannelType(TeleportChannelID id, PlayerColor player) const
|
||||||
|
{
|
||||||
|
std::vector<ObjectInstanceID> entrances = getTeleportChannelEntraces(id, player);
|
||||||
|
std::vector<ObjectInstanceID> exits = getTeleportChannelExits(id, player);
|
||||||
|
if((!entrances.size() || !exits.size()) // impassable if exits or entrances list are empty
|
||||||
|
|| (entrances.size() == 1 && entrances == exits)) // impassable if only entrance and only exit is same object. e.g bidirectional monolith
|
||||||
|
{
|
||||||
|
return ETeleportChannelType::IMPASSABLE;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto intersection = vstd::intersection(entrances, exits);
|
||||||
|
if(intersection.size() == entrances.size() && intersection.size() == exits.size())
|
||||||
|
return ETeleportChannelType::BIDIRECTIONAL;
|
||||||
|
else if(!intersection.size())
|
||||||
|
return ETeleportChannelType::UNIDIRECTIONAL;
|
||||||
|
else
|
||||||
|
return ETeleportChannelType::MIXED;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CGameInfoCallback::isTeleportChannelImpassable(TeleportChannelID id, PlayerColor player) const
|
||||||
|
{
|
||||||
|
return ETeleportChannelType::IMPASSABLE == getTeleportChannelType(id, player);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CGameInfoCallback::isTeleportChannelBidirectional(TeleportChannelID id, PlayerColor player) const
|
||||||
|
{
|
||||||
|
return ETeleportChannelType::BIDIRECTIONAL == getTeleportChannelType(id, player);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CGameInfoCallback::isTeleportChannelUnidirectional(TeleportChannelID id, PlayerColor player) const
|
||||||
|
{
|
||||||
|
return ETeleportChannelType::UNIDIRECTIONAL == getTeleportChannelType(id, player);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CGameInfoCallback::isTeleportEntrancePassable(const CGTeleport * obj, PlayerColor player) const
|
||||||
|
{
|
||||||
|
return obj && obj->isEntrance() && !isTeleportChannelImpassable(obj->channel, player);
|
||||||
|
}
|
||||||
|
|
||||||
void IGameEventRealizer::showInfoDialog( InfoWindow *iw )
|
void IGameEventRealizer::showInfoDialog( InfoWindow *iw )
|
||||||
{
|
{
|
||||||
commitPackage(iw);
|
commitPackage(iw);
|
||||||
|
@ -25,6 +25,7 @@ struct InfoAboutTown;
|
|||||||
struct UpgradeInfo;
|
struct UpgradeInfo;
|
||||||
struct SThievesGuildInfo;
|
struct SThievesGuildInfo;
|
||||||
class CGDwelling;
|
class CGDwelling;
|
||||||
|
class CGTeleport;
|
||||||
class CMapHeader;
|
class CMapHeader;
|
||||||
struct TeamState;
|
struct TeamState;
|
||||||
struct QuestInfo;
|
struct QuestInfo;
|
||||||
@ -106,6 +107,16 @@ public:
|
|||||||
const TeamState *getTeam(TeamID teamID) const;
|
const TeamState *getTeam(TeamID teamID) const;
|
||||||
const TeamState *getPlayerTeam(PlayerColor color) const;
|
const TeamState *getPlayerTeam(PlayerColor color) const;
|
||||||
EBuildingState::EBuildingState canBuildStructure(const CGTownInstance *t, BuildingID ID) const;// 0 - no more than one capitol, 1 - lack of water, 2 - forbidden, 3 - Add another level to Mage Guild, 4 - already built, 5 - cannot build, 6 - cannot afford, 7 - build, 8 - lack of requirements
|
EBuildingState::EBuildingState canBuildStructure(const CGTownInstance *t, BuildingID ID) const;// 0 - no more than one capitol, 1 - lack of water, 2 - forbidden, 3 - Add another level to Mage Guild, 4 - already built, 5 - cannot build, 6 - cannot afford, 7 - build, 8 - lack of requirements
|
||||||
|
|
||||||
|
//teleport
|
||||||
|
std::vector<ObjectInstanceID> getVisibleTeleportObjects(std::vector<ObjectInstanceID> ids, PlayerColor player) const;
|
||||||
|
std::vector<ObjectInstanceID> getTeleportChannelEntraces(TeleportChannelID id, PlayerColor Player = PlayerColor::UNFLAGGABLE) const;
|
||||||
|
std::vector<ObjectInstanceID> getTeleportChannelExits(TeleportChannelID id, PlayerColor Player = PlayerColor::UNFLAGGABLE) const;
|
||||||
|
ETeleportChannelType getTeleportChannelType(TeleportChannelID id, PlayerColor player = PlayerColor::UNFLAGGABLE) const;
|
||||||
|
bool isTeleportChannelImpassable(TeleportChannelID id, PlayerColor player = PlayerColor::UNFLAGGABLE) const;
|
||||||
|
bool isTeleportChannelBidirectional(TeleportChannelID id, PlayerColor player = PlayerColor::UNFLAGGABLE) const;
|
||||||
|
bool isTeleportChannelUnidirectional(TeleportChannelID id, PlayerColor player = PlayerColor::UNFLAGGABLE) const;
|
||||||
|
bool isTeleportEntrancePassable(const CGTeleport * obj, PlayerColor player) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
class DLL_LINKAGE CPlayerSpecificInfoCallback : public CGameInfoCallback
|
class DLL_LINKAGE CPlayerSpecificInfoCallback : public CGameInfoCallback
|
||||||
|
@ -94,6 +94,7 @@ public:
|
|||||||
|
|
||||||
// all stacks operations between these objects become allowed, interface has to call onEnd when done
|
// all stacks operations between these objects become allowed, interface has to call onEnd when done
|
||||||
virtual void showGarrisonDialog(const CArmedInstance *up, const CGHeroInstance *down, bool removableUnits, QueryID queryID) = 0;
|
virtual void showGarrisonDialog(const CArmedInstance *up, const CGHeroInstance *down, bool removableUnits, QueryID queryID) = 0;
|
||||||
|
virtual void showTeleportDialog(TeleportChannelID channel, std::vector<ObjectInstanceID> exits, bool impassable, QueryID askID) = 0;
|
||||||
virtual void finish(){}; //if for some reason we want to end
|
virtual void finish(){}; //if for some reason we want to end
|
||||||
|
|
||||||
virtual void showWorldViewEx(const std::vector<ObjectPosInfo> & objectPositions){};
|
virtual void showWorldViewEx(const std::vector<ObjectPosInfo> & objectPositions){};
|
||||||
|
@ -1867,7 +1867,7 @@ void CGameState::initMapObjects()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
CGTeleport::postInit(); //pairing subterranean gates
|
CGSubterraneanGate::postInit(gs); //pairing subterranean gates
|
||||||
|
|
||||||
map->calculateGuardingGreaturePositions(); //calculate once again when all the guards are placed and initialized
|
map->calculateGuardingGreaturePositions(); //calculate once again when all the guards are placed and initialized
|
||||||
}
|
}
|
||||||
@ -3300,9 +3300,6 @@ void CPathfinder::initializeGraph()
|
|||||||
|
|
||||||
void CPathfinder::calculatePaths()
|
void CPathfinder::calculatePaths()
|
||||||
{
|
{
|
||||||
assert(hero);
|
|
||||||
assert(hero == getHero(hero->id));
|
|
||||||
|
|
||||||
bool flying = hero->hasBonusOfType(Bonus::FLYING_MOVEMENT);
|
bool flying = hero->hasBonusOfType(Bonus::FLYING_MOVEMENT);
|
||||||
int maxMovePointsLand = hero->maxMovePoints(true);
|
int maxMovePointsLand = hero->maxMovePoints(true);
|
||||||
int maxMovePointsWater = hero->maxMovePoints(false);
|
int maxMovePointsWater = hero->maxMovePoints(false);
|
||||||
@ -3318,7 +3315,7 @@ void CPathfinder::calculatePaths()
|
|||||||
|
|
||||||
if(!gs->map->isInTheMap(out.hpos)/* || !gs->map->isInTheMap(dest)*/) //check input
|
if(!gs->map->isInTheMap(out.hpos)/* || !gs->map->isInTheMap(dest)*/) //check input
|
||||||
{
|
{
|
||||||
logGlobal->errorStream() << "CGameState::calculatePaths: Hero outside the gs->map? How dare you...";
|
logGlobal->errorStream() << "CGameState::calculatePaths: Hero outside the gs->map? How dare you...";
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3349,68 +3346,73 @@ void CPathfinder::calculatePaths()
|
|||||||
turn++;
|
turn++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
//add accessible neighbouring nodes to the queue
|
//add accessible neighbouring nodes to the queue
|
||||||
neighbours.clear();
|
neighbours.clear();
|
||||||
|
|
||||||
//handling subterranean gate => it's exit is the only neighbour
|
auto sObj = ct->topVisitableObj(cp->coord == CGHeroInstance::convertPosition(hero->pos, false));
|
||||||
bool subterraneanEntry = (ct->topVisitableId() == Obj::SUBTERRANEAN_GATE && useSubterraneanGates);
|
auto cObj = dynamic_cast<const CGTeleport *>(sObj);
|
||||||
if(subterraneanEntry)
|
if(gs->isTeleportEntrancePassable(cObj, hero->tempOwner)
|
||||||
|
&& (addTeleportWhirlpool(dynamic_cast<const CGWhirlpool *>(cObj))
|
||||||
|
|| addTeleportTwoWay(cObj)
|
||||||
|
|| addTeleportOneWay(cObj)
|
||||||
|
|| addTeleportOneWayRandom(cObj)))
|
||||||
{
|
{
|
||||||
//try finding the exit gate
|
for(auto objId : gs->getTeleportChannelExits(cObj->channel, hero->tempOwner))
|
||||||
if(const CGObjectInstance *outGate = getObj(CGTeleport::getMatchingGate(ct->visitableObjects.back()->id), false))
|
|
||||||
{
|
{
|
||||||
const int3 outPos = outGate->visitablePos();
|
auto obj = getObj(objId);
|
||||||
//gs->getNeighbours(*getTile(outPos), outPos, neighbours, boost::logic::indeterminate, !cp->land);
|
if(CGTeleport::isExitPassable(gs, hero, obj))
|
||||||
neighbours.push_back(outPos);
|
neighbours.push_back(obj->visitablePos());
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
//gate with no exit (blocked) -> do nothing with this node
|
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
gs->getNeighbours(*ct, cp->coord, neighbours, boost::logic::indeterminate, !cp->land);
|
std::vector<int3> neighbour_tiles;
|
||||||
|
gs->getNeighbours(*ct, cp->coord, neighbour_tiles, boost::logic::indeterminate, !cp->land);
|
||||||
|
if(sObj)
|
||||||
|
{
|
||||||
|
for(int3 neighbour_tile: neighbour_tiles)
|
||||||
|
{
|
||||||
|
if(canMoveBetween(neighbour_tile, sObj->visitablePos()))
|
||||||
|
neighbours.push_back(neighbour_tile);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
vstd::concatenate(neighbours, neighbour_tiles);
|
||||||
|
|
||||||
for(auto & neighbour : neighbours)
|
for(auto & neighbour : neighbours)
|
||||||
{
|
{
|
||||||
const int3 &n = neighbour; //current neighbor
|
const int3 &n = neighbour; //current neighbor
|
||||||
dp = getNode(n);
|
dp = getNode(n);
|
||||||
dt = &gs->map->getTile(n);
|
dt = &gs->map->getTile(n);
|
||||||
destTopVisObjID = dt->topVisitableId();
|
destTopVisObjID = dt->topVisitableId();
|
||||||
|
|
||||||
useEmbarkCost = 0; //0 - usual movement; 1 - embark; 2 - disembark
|
useEmbarkCost = 0; //0 - usual movement; 1 - embark; 2 - disembark
|
||||||
|
|
||||||
int turnAtNextTile = turn;
|
|
||||||
|
|
||||||
|
|
||||||
const bool destIsGuardian = sourceGuardPosition == n;
|
const bool destIsGuardian = sourceGuardPosition == n;
|
||||||
|
|
||||||
if(!goodForLandSeaTransition())
|
auto dObj = dynamic_cast<const CGTeleport*>(dt->topVisitableObj());
|
||||||
continue;
|
if(!goodForLandSeaTransition()
|
||||||
|
|| (!canMoveBetween(cp->coord, dp->coord) && !CGTeleport::isConnected(cObj, dObj))
|
||||||
if(!canMoveBetween(cp->coord, dp->coord) || dp->accessible == CGPathNode::BLOCKED )
|
|| dp->accessible == CGPathNode::BLOCKED)
|
||||||
|
{
|
||||||
continue;
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
//special case -> hero embarked a boat standing on a guarded tile -> we must allow to move away from that tile
|
//special case -> hero embarked a boat standing on a guarded tile -> we must allow to move away from that tile
|
||||||
if(cp->accessible == CGPathNode::VISITABLE && guardedSource && cp->theNodeBefore->land && ct->topVisitableId() == Obj::BOAT)
|
if(cp->accessible == CGPathNode::VISITABLE && guardedSource && cp->theNodeBefore->land && ct->topVisitableId() == Obj::BOAT)
|
||||||
guardedSource = false;
|
guardedSource = false;
|
||||||
|
|
||||||
int cost = gs->getMovementCost(hero, cp->coord, dp->coord, flying, movement);
|
int cost = gs->getMovementCost(hero, cp->coord, dp->coord, flying, movement);
|
||||||
|
|
||||||
//special case -> moving from src Subterranean gate to dest gate -> it's free
|
//special case -> moving from src Subterranean gate to dest gate -> it's free
|
||||||
if(subterraneanEntry && destTopVisObjID == Obj::SUBTERRANEAN_GATE && cp->coord.z != dp->coord.z)
|
if(CGTeleport::isConnected(cObj, dObj))
|
||||||
cost = 0;
|
cost = 0;
|
||||||
|
|
||||||
int remains = movement - cost;
|
int remains = movement - cost;
|
||||||
|
|
||||||
if(useEmbarkCost)
|
if(useEmbarkCost)
|
||||||
{
|
{
|
||||||
remains = hero->movementPointsAfterEmbark(movement, cost, useEmbarkCost - 1);
|
remains = hero->movementPointsAfterEmbark(movement, cost, useEmbarkCost - 1);
|
||||||
cost = movement - remains;
|
cost = movement - remains;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int turnAtNextTile = turn;
|
||||||
if(remains < 0)
|
if(remains < 0)
|
||||||
{
|
{
|
||||||
//occurs rarely, when hero with low movepoints tries to leave the road
|
//occurs rarely, when hero with low movepoints tries to leave the road
|
||||||
@ -3425,7 +3427,6 @@ void CPathfinder::calculatePaths()
|
|||||||
|| (dp->turns >= turnAtNextTile && dp->moveRemains < remains)) //this route is faster
|
|| (dp->turns >= turnAtNextTile && dp->moveRemains < remains)) //this route is faster
|
||||||
&& (!guardedSource || destIsGuardian)) // Can step into tile of guard
|
&& (!guardedSource || destIsGuardian)) // Can step into tile of guard
|
||||||
{
|
{
|
||||||
|
|
||||||
assert(dp != cp->theNodeBefore); //two tiles can't point to each other
|
assert(dp != cp->theNodeBefore); //two tiles can't point to each other
|
||||||
dp->moveRemains = remains;
|
dp->moveRemains = remains;
|
||||||
dp->turns = turnAtNextTile;
|
dp->turns = turnAtNextTile;
|
||||||
@ -3434,13 +3435,28 @@ void CPathfinder::calculatePaths()
|
|||||||
const bool guardedDst = gs->map->guardingCreaturePositions[dp->coord.x][dp->coord.y][dp->coord.z].valid()
|
const bool guardedDst = gs->map->guardingCreaturePositions[dp->coord.x][dp->coord.y][dp->coord.z].valid()
|
||||||
&& dp->accessible == CGPathNode::BLOCKVIS;
|
&& dp->accessible == CGPathNode::BLOCKVIS;
|
||||||
|
|
||||||
if (dp->accessible == CGPathNode::ACCESSIBLE
|
auto checkDestinationTile = [&]() -> bool
|
||||||
|| (useEmbarkCost && allowEmbarkAndDisembark)
|
|
||||||
|| destTopVisObjID == Obj::SUBTERRANEAN_GATE
|
|
||||||
|| (guardedDst && !guardedSource)) // Can step into a hostile tile once.
|
|
||||||
{
|
{
|
||||||
|
if(dp->accessible == CGPathNode::ACCESSIBLE)
|
||||||
|
return true;
|
||||||
|
if(dp->coord == CGHeroInstance::convertPosition(hero->pos, false))
|
||||||
|
return true; // This one is tricky, we can ignore fact that tile is not ACCESSIBLE in case if it's our hero block it. Though this need investigation
|
||||||
|
if(dp->accessible == CGPathNode::VISITABLE && CGTeleport::isTeleport(dt->topVisitableObj()))
|
||||||
|
return true; // For now we'll walways allos transit for teleports
|
||||||
|
if(useEmbarkCost && allowEmbarkAndDisembark)
|
||||||
|
return true;
|
||||||
|
if(gs->isTeleportEntrancePassable(dObj, hero->tempOwner))
|
||||||
|
return true; // Always add entry teleport with non-dummy channel
|
||||||
|
if(CGTeleport::isConnected(cObj, dObj))
|
||||||
|
return true; // Always add exit points of teleport
|
||||||
|
if(guardedDst && !guardedSource)
|
||||||
|
return true; // Can step into a hostile tile once
|
||||||
|
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
if(checkDestinationTile())
|
||||||
mq.push_back(dp);
|
mq.push_back(dp);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} //neighbours loop
|
} //neighbours loop
|
||||||
} //queue loop
|
} //queue loop
|
||||||
@ -3453,7 +3469,7 @@ CGPathNode *CPathfinder::getNode(const int3 &coord)
|
|||||||
|
|
||||||
bool CPathfinder::canMoveBetween(const int3 &a, const int3 &b) const
|
bool CPathfinder::canMoveBetween(const int3 &a, const int3 &b) const
|
||||||
{
|
{
|
||||||
return gs->checkForVisitableDir(a, b) && gs->checkForVisitableDir(b, a);
|
return gs->checkForVisitableDir(a, b);
|
||||||
}
|
}
|
||||||
|
|
||||||
CGPathNode::EAccessibility CPathfinder::evaluateAccessibility(const TerrainTile *tinfo) const
|
CGPathNode::EAccessibility CPathfinder::evaluateAccessibility(const TerrainTile *tinfo) const
|
||||||
@ -3532,8 +3548,16 @@ bool CPathfinder::goodForLandSeaTransition()
|
|||||||
|
|
||||||
CPathfinder::CPathfinder(CPathsInfo &_out, CGameState *_gs, const CGHeroInstance *_hero) : CGameInfoCallback(_gs, boost::optional<PlayerColor>()), out(_out), hero(_hero), FoW(getPlayerTeam(hero->tempOwner)->fogOfWarMap)
|
CPathfinder::CPathfinder(CPathsInfo &_out, CGameState *_gs, const CGHeroInstance *_hero) : CGameInfoCallback(_gs, boost::optional<PlayerColor>()), out(_out), hero(_hero), FoW(getPlayerTeam(hero->tempOwner)->fogOfWarMap)
|
||||||
{
|
{
|
||||||
useSubterraneanGates = true;
|
assert(hero);
|
||||||
|
assert(hero == getHero(hero->id));
|
||||||
|
|
||||||
allowEmbarkAndDisembark = true;
|
allowEmbarkAndDisembark = true;
|
||||||
|
allowTeleportTwoWay = true;
|
||||||
|
allowTeleportOneWay = true;
|
||||||
|
allowTeleportOneWayRandom = false;
|
||||||
|
allowTeleportWhirlpool = false;
|
||||||
|
if (CGWhirlpool::isProtected(hero))
|
||||||
|
allowTeleportWhirlpool = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
CRandomGenerator & CGameState::getRandomGenerator()
|
CRandomGenerator & CGameState::getRandomGenerator()
|
||||||
@ -3541,3 +3565,35 @@ CRandomGenerator & CGameState::getRandomGenerator()
|
|||||||
//logGlobal->traceStream() << "Fetching CGameState::rand with seed " << rand.nextInt();
|
//logGlobal->traceStream() << "Fetching CGameState::rand with seed " << rand.nextInt();
|
||||||
return rand;
|
return rand;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool CPathfinder::addTeleportTwoWay(const CGTeleport * obj) const
|
||||||
|
{
|
||||||
|
return allowTeleportTwoWay && gs->isTeleportChannelBidirectional(obj->channel, hero->tempOwner);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CPathfinder::addTeleportOneWay(const CGTeleport * obj) const
|
||||||
|
{
|
||||||
|
if(allowTeleportOneWay && isTeleportChannelUnidirectional(obj->channel, hero->tempOwner))
|
||||||
|
{
|
||||||
|
auto passableExits = CGTeleport::getPassableExits(gs, hero, gs->getTeleportChannelExits(obj->channel, hero->tempOwner));
|
||||||
|
if(passableExits.size() == 1)
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CPathfinder::addTeleportOneWayRandom(const CGTeleport * obj) const
|
||||||
|
{
|
||||||
|
if(allowTeleportOneWayRandom && isTeleportChannelUnidirectional(obj->channel, hero->tempOwner))
|
||||||
|
{
|
||||||
|
auto passableExits = CGTeleport::getPassableExits(gs, hero, gs->getTeleportChannelExits(obj->channel, hero->tempOwner));
|
||||||
|
if(passableExits.size() > 1)
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CPathfinder::addTeleportWhirlpool(const CGWhirlpool * obj) const
|
||||||
|
{
|
||||||
|
return allowTeleportWhirlpool && obj && !gs->isTeleportChannelImpassable(obj->channel, hero->tempOwner);
|
||||||
|
}
|
||||||
|
@ -279,8 +279,11 @@ struct DLL_EXPORT DuelParameters
|
|||||||
class CPathfinder : private CGameInfoCallback
|
class CPathfinder : private CGameInfoCallback
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
bool useSubterraneanGates;
|
|
||||||
bool allowEmbarkAndDisembark;
|
bool allowEmbarkAndDisembark;
|
||||||
|
bool allowTeleportTwoWay; // Two-way monoliths and Subterranean Gate
|
||||||
|
bool allowTeleportOneWay; // One-way monoliths with one known exit only
|
||||||
|
bool allowTeleportOneWayRandom; // One-way monoliths with more than one known exit
|
||||||
|
bool allowTeleportWhirlpool; // Force enabled if hero protected or unaffected (have one stack of one creature)
|
||||||
CPathsInfo &out;
|
CPathsInfo &out;
|
||||||
const CGHeroInstance *hero;
|
const CGHeroInstance *hero;
|
||||||
const std::vector<std::vector<std::vector<ui8> > > &FoW;
|
const std::vector<std::vector<std::vector<ui8> > > &FoW;
|
||||||
@ -293,7 +296,7 @@ private:
|
|||||||
CGPathNode *dp; //destination node -> it's a neighbour of cp that we consider
|
CGPathNode *dp; //destination node -> it's a neighbour of cp that we consider
|
||||||
const TerrainTile *ct, *dt; //tile info for both nodes
|
const TerrainTile *ct, *dt; //tile info for both nodes
|
||||||
ui8 useEmbarkCost; //0 - usual movement; 1 - embark; 2 - disembark
|
ui8 useEmbarkCost; //0 - usual movement; 1 - embark; 2 - disembark
|
||||||
int destTopVisObjID;
|
Obj destTopVisObjID;
|
||||||
|
|
||||||
|
|
||||||
CGPathNode *getNode(const int3 &coord);
|
CGPathNode *getNode(const int3 &coord);
|
||||||
@ -303,6 +306,11 @@ private:
|
|||||||
CGPathNode::EAccessibility evaluateAccessibility(const TerrainTile *tinfo) const;
|
CGPathNode::EAccessibility evaluateAccessibility(const TerrainTile *tinfo) const;
|
||||||
bool canMoveBetween(const int3 &a, const int3 &b) const; //checks only for visitable objects that may make moving between tiles impossible, not other conditions (like tiles itself accessibility)
|
bool canMoveBetween(const int3 &a, const int3 &b) const; //checks only for visitable objects that may make moving between tiles impossible, not other conditions (like tiles itself accessibility)
|
||||||
|
|
||||||
|
bool addTeleportTwoWay(const CGTeleport * obj) const;
|
||||||
|
bool addTeleportOneWay(const CGTeleport * obj) const;
|
||||||
|
bool addTeleportOneWayRandom(const CGTeleport * obj) const;
|
||||||
|
bool addTeleportWhirlpool(const CGWhirlpool * obj) const;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
CPathfinder(CPathsInfo &_out, CGameState *_gs, const CGHeroInstance *_hero);
|
CPathfinder(CPathsInfo &_out, CGameState *_gs, const CGHeroInstance *_hero);
|
||||||
void calculatePaths(); //calculates possible paths for hero, uses current hero position and movement left; returns pointer to newly allocated CPath or nullptr if path does not exists
|
void calculatePaths(); //calculates possible paths for hero, uses current hero position and movement left; returns pointer to newly allocated CPath or nullptr if path does not exists
|
||||||
|
@ -252,6 +252,14 @@ class TeamID : public BaseForID<TeamID, ui8>
|
|||||||
friend class CNonConstInfoCallback;
|
friend class CNonConstInfoCallback;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class TeleportChannelID : public BaseForID<TeleportChannelID, si32>
|
||||||
|
{
|
||||||
|
INSTID_LIKE_CLASS_COMMON(TeleportChannelID, si32)
|
||||||
|
|
||||||
|
friend class CGameInfoCallback;
|
||||||
|
friend class CNonConstInfoCallback;
|
||||||
|
};
|
||||||
|
|
||||||
// #ifndef INSTANTIATE_BASE_FOR_ID_HERE
|
// #ifndef INSTANTIATE_BASE_FOR_ID_HERE
|
||||||
// extern template std::ostream & operator << <ArtifactInstanceID>(std::ostream & os, BaseForID<ArtifactInstanceID> id);
|
// extern template std::ostream & operator << <ArtifactInstanceID>(std::ostream & os, BaseForID<ArtifactInstanceID> id);
|
||||||
// extern template std::ostream & operator << <ObjectInstanceID>(std::ostream & os, BaseForID<ObjectInstanceID> id);
|
// extern template std::ostream & operator << <ObjectInstanceID>(std::ostream & os, BaseForID<ObjectInstanceID> id);
|
||||||
@ -466,6 +474,14 @@ namespace ETileType
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum class ETeleportChannelType
|
||||||
|
{
|
||||||
|
IMPASSABLE,
|
||||||
|
BIDIRECTIONAL,
|
||||||
|
UNIDIRECTIONAL,
|
||||||
|
MIXED
|
||||||
|
};
|
||||||
|
|
||||||
class Obj
|
class Obj
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
@ -15,6 +15,7 @@
|
|||||||
struct SetMovePoints;
|
struct SetMovePoints;
|
||||||
struct GiveBonus;
|
struct GiveBonus;
|
||||||
struct BlockingDialog;
|
struct BlockingDialog;
|
||||||
|
struct TeleportDialog;
|
||||||
struct MetaString;
|
struct MetaString;
|
||||||
struct ShowInInfobox;
|
struct ShowInInfobox;
|
||||||
struct StackLocation;
|
struct StackLocation;
|
||||||
@ -52,6 +53,7 @@ public:
|
|||||||
virtual void changeSecSkill(const CGHeroInstance * hero, SecondarySkill which, int val, bool abs=false)=0;
|
virtual void changeSecSkill(const CGHeroInstance * hero, SecondarySkill which, int val, bool abs=false)=0;
|
||||||
virtual void showBlockingDialog(BlockingDialog *iw) =0;
|
virtual void showBlockingDialog(BlockingDialog *iw) =0;
|
||||||
virtual void showGarrisonDialog(ObjectInstanceID upobj, ObjectInstanceID hid, bool removableUnits) =0; //cb will be called when player closes garrison window
|
virtual void showGarrisonDialog(ObjectInstanceID upobj, ObjectInstanceID hid, bool removableUnits) =0; //cb will be called when player closes garrison window
|
||||||
|
virtual void showTeleportDialog(TeleportDialog *iw) =0;
|
||||||
virtual void showThievesGuildWindow(PlayerColor player, ObjectInstanceID requestingObjId) =0;
|
virtual void showThievesGuildWindow(PlayerColor player, ObjectInstanceID requestingObjId) =0;
|
||||||
virtual void giveResource(PlayerColor player, Res::ERes which, int val)=0;
|
virtual void giveResource(PlayerColor player, Res::ERes which, int val)=0;
|
||||||
virtual void giveResources(PlayerColor player, TResources resources)=0;
|
virtual void giveResources(PlayerColor player, TResources resources)=0;
|
||||||
@ -83,7 +85,7 @@ public:
|
|||||||
virtual void startBattleI(const CArmedInstance *army1, const CArmedInstance *army2, int3 tile, bool creatureBank = false)=0; //if any of armies is hero, hero will be used
|
virtual void startBattleI(const CArmedInstance *army1, const CArmedInstance *army2, int3 tile, bool creatureBank = false)=0; //if any of armies is hero, hero will be used
|
||||||
virtual void startBattleI(const CArmedInstance *army1, const CArmedInstance *army2, bool creatureBank = false)=0; //if any of armies is hero, hero will be used, visitable tile of second obj is place of battle
|
virtual void startBattleI(const CArmedInstance *army1, const CArmedInstance *army2, bool creatureBank = false)=0; //if any of armies is hero, hero will be used, visitable tile of second obj is place of battle
|
||||||
virtual void setAmount(ObjectInstanceID objid, ui32 val)=0;
|
virtual void setAmount(ObjectInstanceID objid, ui32 val)=0;
|
||||||
virtual bool moveHero(ObjectInstanceID hid, int3 dst, ui8 teleporting, PlayerColor asker = PlayerColor::NEUTRAL)=0;
|
virtual bool moveHero(ObjectInstanceID hid, int3 dst, ui8 teleporting, bool transit = false, PlayerColor asker = PlayerColor::NEUTRAL)=0;
|
||||||
virtual void giveHeroBonus(GiveBonus * bonus)=0;
|
virtual void giveHeroBonus(GiveBonus * bonus)=0;
|
||||||
virtual void setMovePoints(SetMovePoints * smp)=0;
|
virtual void setMovePoints(SetMovePoints * smp)=0;
|
||||||
virtual void setManaPoints(ObjectInstanceID hid, int val)=0;
|
virtual void setManaPoints(ObjectInstanceID hid, int val)=0;
|
||||||
|
@ -1208,6 +1208,28 @@ struct ExchangeDialog : public Query//2005
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct TeleportDialog : public Query//2006
|
||||||
|
{
|
||||||
|
TeleportDialog() {type = 2006;}
|
||||||
|
TeleportDialog(const CGHeroInstance *Hero, TeleportChannelID Channel)
|
||||||
|
: hero(Hero), channel(Channel), impassable(false)
|
||||||
|
{
|
||||||
|
type = 2006;
|
||||||
|
}
|
||||||
|
|
||||||
|
void applyCl(CClient *cl);
|
||||||
|
|
||||||
|
const CGHeroInstance *hero;
|
||||||
|
TeleportChannelID channel;
|
||||||
|
std::vector<ObjectInstanceID> exits;
|
||||||
|
bool impassable;
|
||||||
|
|
||||||
|
template <typename Handler> void serialize(Handler &h, const int version)
|
||||||
|
{
|
||||||
|
h & queryID & hero & channel & exits & impassable;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
struct BattleInfo;
|
struct BattleInfo;
|
||||||
struct BattleStart : public CPackForClient//3000
|
struct BattleStart : public CPackForClient//3000
|
||||||
{
|
{
|
||||||
@ -1758,14 +1780,15 @@ struct DismissHero : public CPackForServer
|
|||||||
struct MoveHero : public CPackForServer
|
struct MoveHero : public CPackForServer
|
||||||
{
|
{
|
||||||
MoveHero(){};
|
MoveHero(){};
|
||||||
MoveHero(const int3 &Dest, ObjectInstanceID HID) : dest(Dest), hid(HID){};
|
MoveHero(const int3 &Dest, ObjectInstanceID HID, bool Transit) : dest(Dest), hid(HID), transit(Transit) {};
|
||||||
int3 dest;
|
int3 dest;
|
||||||
ObjectInstanceID hid;
|
ObjectInstanceID hid;
|
||||||
|
bool transit;
|
||||||
|
|
||||||
bool applyGh(CGameHandler *gh);
|
bool applyGh(CGameHandler *gh);
|
||||||
template <typename Handler> void serialize(Handler &h, const int version)
|
template <typename Handler> void serialize(Handler &h, const int version)
|
||||||
{
|
{
|
||||||
h & dest & hid;
|
h & dest & hid & transit;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -83,7 +83,9 @@ CObjectClassesHandler::CObjectClassesHandler()
|
|||||||
SET_HANDLER("shrine", CGShrine);
|
SET_HANDLER("shrine", CGShrine);
|
||||||
SET_HANDLER("sign", CGSignBottle);
|
SET_HANDLER("sign", CGSignBottle);
|
||||||
SET_HANDLER("siren", CGSirens);
|
SET_HANDLER("siren", CGSirens);
|
||||||
SET_HANDLER("teleport", CGTeleport);
|
SET_HANDLER("monolith", CGMonolith);
|
||||||
|
SET_HANDLER("subterraneanGate", CGSubterraneanGate);
|
||||||
|
SET_HANDLER("whirlpool", CGWhirlpool);
|
||||||
SET_HANDLER("university", CGUniversity);
|
SET_HANDLER("university", CGUniversity);
|
||||||
SET_HANDLER("oncePerHero", CGVisitableOPH);
|
SET_HANDLER("oncePerHero", CGVisitableOPH);
|
||||||
SET_HANDLER("oncePerWeek", CGVisitableOPW);
|
SET_HANDLER("oncePerWeek", CGVisitableOPW);
|
||||||
|
@ -21,8 +21,6 @@
|
|||||||
#include "../IGameCallback.h"
|
#include "../IGameCallback.h"
|
||||||
#include "../CGameState.h"
|
#include "../CGameState.h"
|
||||||
|
|
||||||
std::map<Obj, std::map<int, std::vector<ObjectInstanceID> > > CGTeleport::objs;
|
|
||||||
std::vector<std::pair<ObjectInstanceID, ObjectInstanceID> > CGTeleport::gates;
|
|
||||||
std::map <si32, std::vector<ObjectInstanceID> > CGMagi::eyelist;
|
std::map <si32, std::vector<ObjectInstanceID> > CGMagi::eyelist;
|
||||||
ui8 CGObelisk::obeliskCount; //how many obelisks are on map
|
ui8 CGObelisk::obeliskCount; //how many obelisks are on map
|
||||||
std::map<TeamID, ui8> CGObelisk::visited; //map: team_id => how many obelisks has been visited
|
std::map<TeamID, ui8> CGObelisk::visited; //map: team_id => how many obelisks has been visited
|
||||||
@ -777,115 +775,236 @@ void CGResource::blockingDialogAnswered(const CGHeroInstance *hero, ui32 answer)
|
|||||||
cb->startBattleI(hero, this);
|
cb->startBattleI(hero, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CGTeleport::onHeroVisit( const CGHeroInstance * h ) const
|
CGTeleport::CGTeleport() :
|
||||||
|
type(UNKNOWN), channel(TeleportChannelID())
|
||||||
{
|
{
|
||||||
ObjectInstanceID destinationid;
|
}
|
||||||
switch(ID)
|
|
||||||
|
bool CGTeleport::isEntrance() const
|
||||||
|
{
|
||||||
|
return type == BOTH || type == ENTRANCE;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CGTeleport::isExit() const
|
||||||
|
{
|
||||||
|
return type == BOTH || type == EXIT;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CGTeleport::isChannelEntrance(ObjectInstanceID id) const
|
||||||
|
{
|
||||||
|
return vstd::contains(getAllEntrances(), id);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CGTeleport::isChannelExit(ObjectInstanceID id) const
|
||||||
|
{
|
||||||
|
return vstd::contains(getAllExits(), id);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<ObjectInstanceID> CGTeleport::getAllEntrances(bool excludeCurrent) const
|
||||||
|
{
|
||||||
|
auto ret = cb->getTeleportChannelEntraces(channel);
|
||||||
|
if(excludeCurrent)
|
||||||
|
vstd::erase_if_present(ret, id);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<ObjectInstanceID> CGTeleport::getAllExits(bool excludeCurrent) const
|
||||||
|
{
|
||||||
|
auto ret = cb->getTeleportChannelExits(channel);
|
||||||
|
if(excludeCurrent)
|
||||||
|
vstd::erase_if_present(ret, id);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
ObjectInstanceID CGTeleport::getRandomExit(const CGHeroInstance * h) const
|
||||||
|
{
|
||||||
|
auto passableExits = getPassableExits(cb->gameState(), h, getAllExits(true));
|
||||||
|
if(passableExits.size())
|
||||||
|
return *RandomGeneratorUtil::nextItem(passableExits, cb->gameState()->getRandomGenerator());
|
||||||
|
|
||||||
|
return ObjectInstanceID();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CGTeleport::isTeleport(const CGObjectInstance * obj)
|
||||||
|
{
|
||||||
|
return ((dynamic_cast<const CGTeleport *>(obj)));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CGTeleport::isConnected(const CGTeleport * src, const CGTeleport * dst)
|
||||||
|
{
|
||||||
|
return src && dst && src != dst && src->isChannelExit(dst->id);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CGTeleport::isConnected(const CGObjectInstance * src, const CGObjectInstance * dst)
|
||||||
|
{
|
||||||
|
auto srcObj = dynamic_cast<const CGTeleport *>(src);
|
||||||
|
auto dstObj = dynamic_cast<const CGTeleport *>(dst);
|
||||||
|
return isConnected(srcObj, dstObj);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CGTeleport::isExitPassable(CGameState * gs, const CGHeroInstance * h, const CGObjectInstance * obj)
|
||||||
|
{
|
||||||
|
auto objTopVisObj = gs->map->getTile(obj->visitablePos()).topVisitableObj();
|
||||||
|
if(objTopVisObj->ID == Obj::HERO)
|
||||||
{
|
{
|
||||||
case Obj::MONOLITH_ONE_WAY_ENTRANCE: //one way - find corresponding exit monolith
|
if(h->id == objTopVisObj->id) // Just to be sure it's won't happen.
|
||||||
{
|
return false;
|
||||||
if(vstd::contains(objs,Obj::MONOLITH_ONE_WAY_EXIT) && vstd::contains(objs[Obj::MONOLITH_ONE_WAY_EXIT],subID) && objs[Obj::MONOLITH_ONE_WAY_EXIT][subID].size())
|
|
||||||
{
|
|
||||||
destinationid = *RandomGeneratorUtil::nextItem(objs[Obj::MONOLITH_ONE_WAY_EXIT][subID], cb->gameState()->getRandomGenerator());
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
logGlobal->warnStream() << "Cannot find corresponding exit monolith for "<< id;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case Obj::MONOLITH_TWO_WAY://two way monolith - pick any other one
|
|
||||||
case Obj::WHIRLPOOL: //Whirlpool
|
|
||||||
if(vstd::contains(objs,ID) && vstd::contains(objs[ID],subID) && objs[ID][subID].size()>1)
|
|
||||||
{
|
|
||||||
//choose another exit
|
|
||||||
do
|
|
||||||
{
|
|
||||||
destinationid = *RandomGeneratorUtil::nextItem(objs[ID][subID], cb->gameState()->getRandomGenerator());
|
|
||||||
} while(destinationid == id);
|
|
||||||
|
|
||||||
if (ID == Obj::WHIRLPOOL)
|
// Check if it's friendly hero or not
|
||||||
{
|
if(gs->getPlayerRelations(h->tempOwner, objTopVisObj->tempOwner))
|
||||||
if (!h->hasBonusOfType(Bonus::WHIRLPOOL_PROTECTION))
|
|
||||||
{
|
|
||||||
if (h->Slots().size() > 1 || h->Slots().begin()->second->count > 1)
|
|
||||||
{ //we can't remove last unit
|
|
||||||
SlotID targetstack = h->Slots().begin()->first; //slot numbers may vary
|
|
||||||
for(auto i = h->Slots().rbegin(); i != h->Slots().rend(); i++)
|
|
||||||
{
|
|
||||||
if (h->getPower(targetstack) > h->getPower(i->first))
|
|
||||||
{
|
|
||||||
targetstack = (i->first);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
TQuantity countToTake = h->getStackCount(targetstack) * 0.5;
|
|
||||||
vstd::amax(countToTake, 1);
|
|
||||||
|
|
||||||
|
|
||||||
InfoWindow iw;
|
|
||||||
iw.player = h->tempOwner;
|
|
||||||
iw.text.addTxt (MetaString::ADVOB_TXT, 168);
|
|
||||||
iw.components.push_back (Component(CStackBasicDescriptor(h->getCreature(targetstack), countToTake)));
|
|
||||||
cb->showInfoDialog(&iw);
|
|
||||||
cb->changeStackCount(StackLocation(h, targetstack), -countToTake);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
logGlobal->warnStream() << "Cannot find corresponding exit monolith for "<< id;
|
|
||||||
break;
|
|
||||||
case Obj::SUBTERRANEAN_GATE: //find nearest subterranean gate on the other level
|
|
||||||
{
|
{
|
||||||
destinationid = getMatchingGate(id);
|
// Exchange between heroes only possible via subterranean gates
|
||||||
if(destinationid == ObjectInstanceID()) //no exit
|
if(!dynamic_cast<const CGSubterraneanGate *>(obj))
|
||||||
{
|
return false;
|
||||||
showInfoDialog(h,153,0);//Just inside the entrance you find a large pile of rubble blocking the tunnel. You leave discouraged.
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if(destinationid == ObjectInstanceID())
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<ObjectInstanceID> CGTeleport::getPassableExits(CGameState * gs, const CGHeroInstance * h, std::vector<ObjectInstanceID> exits)
|
||||||
|
{
|
||||||
|
vstd::erase_if(exits, [&](ObjectInstanceID exit) -> bool
|
||||||
{
|
{
|
||||||
logGlobal->warnStream() << "Cannot find exit... (obj at " << pos << ") :( ";
|
return !isExitPassable(gs, h, gs->getObj(exit));
|
||||||
return;
|
});
|
||||||
}
|
return exits;
|
||||||
if (ID == Obj::WHIRLPOOL)
|
}
|
||||||
|
|
||||||
|
void CGTeleport::addToChannel(std::map<TeleportChannelID, shared_ptr<TeleportChannel> > &channelsList, const CGTeleport * obj)
|
||||||
|
{
|
||||||
|
shared_ptr<TeleportChannel> tc;
|
||||||
|
if(channelsList.find(obj->channel) == channelsList.end())
|
||||||
{
|
{
|
||||||
std::set<int3> tiles = cb->getObj(destinationid)->getBlockedPos();
|
tc = make_shared<TeleportChannel>();
|
||||||
auto & tile = *RandomGeneratorUtil::nextItem(tiles, cb->gameState()->getRandomGenerator());
|
channelsList.insert(std::make_pair(obj->channel, tc));
|
||||||
cb->moveHero(h->id, tile + int3(1,0,0), true);
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
cb->moveHero (h->id,CGHeroInstance::convertPosition(cb->getObj(destinationid)->pos,true) - getVisitableOffset(), true);
|
tc = channelsList[obj->channel];
|
||||||
|
|
||||||
|
if(obj->isEntrance() && !vstd::contains(tc->entrances, obj->id))
|
||||||
|
tc->entrances.push_back(obj->id);
|
||||||
|
|
||||||
|
if(obj->isExit() && !vstd::contains(tc->exits, obj->id))
|
||||||
|
tc->exits.push_back(obj->id);
|
||||||
|
|
||||||
|
if(tc->entrances.size() && tc->exits.size()
|
||||||
|
&& (tc->entrances.size() != 1 || tc->entrances != tc->exits))
|
||||||
|
{
|
||||||
|
tc->passability = TeleportChannel::PASSABLE;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void CGTeleport::initObj()
|
TeleportChannelID CGMonolith::findMeChannel(std::vector<Obj> IDs, int SubID) const
|
||||||
{
|
{
|
||||||
int si = subID;
|
for(auto obj : cb->gameState()->map->objects)
|
||||||
switch (ID)
|
|
||||||
{
|
{
|
||||||
case Obj::SUBTERRANEAN_GATE://ignore subterranean gates subid
|
auto teleportObj = dynamic_cast<const CGTeleport *>(cb->getObj(obj->id));
|
||||||
case Obj::WHIRLPOOL:
|
if(teleportObj && vstd::contains(IDs, teleportObj->ID) && teleportObj->subID == SubID)
|
||||||
|
return teleportObj->channel;
|
||||||
|
}
|
||||||
|
return TeleportChannelID();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CGMonolith::onHeroVisit( const CGHeroInstance * h ) const
|
||||||
|
{
|
||||||
|
TeleportDialog td(h, channel);
|
||||||
|
if(isEntrance())
|
||||||
|
{
|
||||||
|
if(cb->isTeleportChannelBidirectional(channel) && 1 < cb->getTeleportChannelExits(channel).size())
|
||||||
|
td.exits = cb->getTeleportChannelExits(channel);
|
||||||
|
|
||||||
|
if(cb->isTeleportChannelImpassable(channel))
|
||||||
{
|
{
|
||||||
si = 0;
|
logGlobal->debugStream() << "Cannot find corresponding exit monolith for "<< id << " (obj at " << pos << ") :(";
|
||||||
break;
|
td.impassable = true;
|
||||||
}
|
}
|
||||||
|
else if(getRandomExit(h) == ObjectInstanceID())
|
||||||
|
logGlobal->debugStream() << "All exits blocked for monolith "<< id << " (obj at " << pos << ") :(";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
showInfoDialog(h, 70, 0);
|
||||||
|
|
||||||
|
cb->showTeleportDialog(&td);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CGMonolith::teleportDialogAnswered(const CGHeroInstance *hero, ui32 answer, std::vector<ObjectInstanceID> exits) const
|
||||||
|
{
|
||||||
|
ObjectInstanceID objId(answer);
|
||||||
|
auto realExits = getAllExits(true);
|
||||||
|
if(!isEntrance() // Do nothing if hero visited exit only object
|
||||||
|
|| (!exits.size() && !realExits.size()) // Do nothing if there no exits on this channel
|
||||||
|
|| (!exits.size() && ObjectInstanceID() == getRandomExit(hero))) // Do nothing if all exits are blocked by friendly hero and it's not subterranean gate
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else if(objId == ObjectInstanceID())
|
||||||
|
objId = getRandomExit(hero);
|
||||||
|
else
|
||||||
|
assert(vstd::contains(exits, objId)); // Likely cheating attempt: not random teleporter choosen, but it's not from provided list
|
||||||
|
|
||||||
|
auto obj = cb->getObj(objId);
|
||||||
|
if(obj)
|
||||||
|
cb->moveHero(hero->id,CGHeroInstance::convertPosition(obj->pos,true) - getVisitableOffset(), true);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CGMonolith::initObj()
|
||||||
|
{
|
||||||
|
std::vector<Obj> IDs;
|
||||||
|
IDs.push_back(ID);
|
||||||
|
switch(ID)
|
||||||
|
{
|
||||||
|
case Obj::MONOLITH_ONE_WAY_ENTRANCE:
|
||||||
|
type = ENTRANCE;
|
||||||
|
IDs.push_back(Obj::MONOLITH_ONE_WAY_EXIT);
|
||||||
|
break;
|
||||||
|
case Obj::MONOLITH_ONE_WAY_EXIT:
|
||||||
|
type = EXIT;
|
||||||
|
IDs.push_back(Obj::MONOLITH_ONE_WAY_ENTRANCE);
|
||||||
|
break;
|
||||||
|
case Obj::MONOLITH_TWO_WAY:
|
||||||
default:
|
default:
|
||||||
|
type = BOTH;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
objs[ID][si].push_back(id);
|
|
||||||
|
channel = findMeChannel(IDs, subID);
|
||||||
|
if(channel == TeleportChannelID())
|
||||||
|
channel = TeleportChannelID(cb->gameState()->map->teleportChannels.size());
|
||||||
|
|
||||||
|
addToChannel(cb->gameState()->map->teleportChannels, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CGTeleport::postInit() //matches subterranean gates into pairs
|
void CGSubterraneanGate::onHeroVisit( const CGHeroInstance * h ) const
|
||||||
|
{
|
||||||
|
TeleportDialog td(h, channel);
|
||||||
|
if(cb->isTeleportChannelImpassable(channel))
|
||||||
|
{
|
||||||
|
showInfoDialog(h,153,0);//Just inside the entrance you find a large pile of rubble blocking the tunnel. You leave discouraged.
|
||||||
|
logGlobal->debugStream() << "Cannot find exit subterranean gate for "<< id << " (obj at " << pos << ") :(";
|
||||||
|
td.impassable = true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
td.exits.push_back(getRandomExit(h));
|
||||||
|
|
||||||
|
cb->showTeleportDialog(&td);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CGSubterraneanGate::initObj()
|
||||||
|
{
|
||||||
|
type = BOTH;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CGSubterraneanGate::postInit( CGameState * gs ) //matches subterranean gates into pairs
|
||||||
{
|
{
|
||||||
//split on underground and surface gates
|
//split on underground and surface gates
|
||||||
std::vector<const CGObjectInstance *> gatesSplit[2]; //surface and underground gates
|
std::vector<CGSubterraneanGate *> gatesSplit[2]; //surface and underground gates
|
||||||
for(auto & elem : objs[Obj::SUBTERRANEAN_GATE][0])
|
for(auto & obj : cb->gameState()->map->objects)
|
||||||
{
|
{
|
||||||
const CGObjectInstance *hlp = cb->getObj(elem);
|
auto hlp = dynamic_cast<CGSubterraneanGate *>(gs->getObjInstance(obj->id));
|
||||||
gatesSplit[hlp->pos.z].push_back(hlp);
|
if(hlp)
|
||||||
|
gatesSplit[hlp->pos.z].push_back(hlp);
|
||||||
}
|
}
|
||||||
|
|
||||||
//sort by position
|
//sort by position
|
||||||
@ -896,16 +1015,16 @@ void CGTeleport::postInit() //matches subterranean gates into pairs
|
|||||||
|
|
||||||
for(size_t i = 0; i < gatesSplit[0].size(); i++)
|
for(size_t i = 0; i < gatesSplit[0].size(); i++)
|
||||||
{
|
{
|
||||||
const CGObjectInstance *cur = gatesSplit[0][i];
|
CGSubterraneanGate * objCurrent = gatesSplit[0][i];
|
||||||
|
|
||||||
//find nearest underground exit
|
//find nearest underground exit
|
||||||
std::pair<int, si32> best(-1, std::numeric_limits<si32>::max()); //pair<pos_in_vector, distance^2>
|
std::pair<int, si32> best(-1, std::numeric_limits<si32>::max()); //pair<pos_in_vector, distance^2>
|
||||||
for(int j = 0; j < gatesSplit[1].size(); j++)
|
for(int j = 0; j < gatesSplit[1].size(); j++)
|
||||||
{
|
{
|
||||||
const CGObjectInstance *checked = gatesSplit[1][j];
|
CGSubterraneanGate *checked = gatesSplit[1][j];
|
||||||
if(!checked)
|
if(!checked)
|
||||||
continue;
|
continue;
|
||||||
si32 hlp = checked->pos.dist2dSQ(cur->pos);
|
si32 hlp = checked->pos.dist2dSQ(objCurrent->pos);
|
||||||
if(hlp < best.second)
|
if(hlp < best.second)
|
||||||
{
|
{
|
||||||
best.first = j;
|
best.first = j;
|
||||||
@ -913,28 +1032,86 @@ void CGTeleport::postInit() //matches subterranean gates into pairs
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(objCurrent->channel == TeleportChannelID())
|
||||||
|
{ // if object not linked to channel then create new channel
|
||||||
|
objCurrent->channel = TeleportChannelID(gs->map->teleportChannels.size());
|
||||||
|
addToChannel(cb->gameState()->map->teleportChannels, objCurrent);
|
||||||
|
}
|
||||||
|
|
||||||
if(best.first >= 0) //found pair
|
if(best.first >= 0) //found pair
|
||||||
{
|
{
|
||||||
gates.push_back(std::make_pair(cur->id, gatesSplit[1][best.first]->id));
|
gatesSplit[1][best.first]->channel = objCurrent->channel;
|
||||||
gatesSplit[1][best.first] = nullptr;
|
addToChannel(cb->gameState()->map->teleportChannels, gatesSplit[1][best.first]);
|
||||||
}
|
}
|
||||||
else
|
|
||||||
gates.push_back(std::make_pair(cur->id, ObjectInstanceID()));
|
|
||||||
}
|
}
|
||||||
objs.erase(Obj::SUBTERRANEAN_GATE);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ObjectInstanceID CGTeleport::getMatchingGate(ObjectInstanceID id)
|
void CGWhirlpool::onHeroVisit( const CGHeroInstance * h ) const
|
||||||
{
|
{
|
||||||
for(auto & gate : gates)
|
TeleportDialog td(h, channel);
|
||||||
|
if(cb->isTeleportChannelImpassable(channel))
|
||||||
{
|
{
|
||||||
if(gate.first == id)
|
logGlobal->debugStream() << "Cannot find exit whirlpool for "<< id << " (obj at " << pos << ") :(";
|
||||||
return gate.second;
|
td.impassable = true;
|
||||||
if(gate.second == id)
|
|
||||||
return gate.first;
|
|
||||||
}
|
}
|
||||||
|
else if(getRandomExit(h) == ObjectInstanceID())
|
||||||
|
logGlobal->debugStream() << "All exits are blocked for whirlpool "<< id << " (obj at " << pos << ") :(";
|
||||||
|
|
||||||
return ObjectInstanceID();
|
if(!isProtected(h))
|
||||||
|
{
|
||||||
|
SlotID targetstack = h->Slots().begin()->first; //slot numbers may vary
|
||||||
|
for(auto i = h->Slots().rbegin(); i != h->Slots().rend(); i++)
|
||||||
|
{
|
||||||
|
if(h->getPower(targetstack) > h->getPower(i->first))
|
||||||
|
targetstack = (i->first);
|
||||||
|
}
|
||||||
|
|
||||||
|
TQuantity countToTake = h->getStackCount(targetstack) * 0.5;
|
||||||
|
vstd::amax(countToTake, 1);
|
||||||
|
|
||||||
|
InfoWindow iw;
|
||||||
|
iw.player = h->tempOwner;
|
||||||
|
iw.text.addTxt(MetaString::ADVOB_TXT, 168);
|
||||||
|
iw.components.push_back(Component(CStackBasicDescriptor(h->getCreature(targetstack), countToTake)));
|
||||||
|
cb->showInfoDialog(&iw);
|
||||||
|
cb->changeStackCount(StackLocation(h, targetstack), -countToTake);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
td.exits = getAllExits(true);
|
||||||
|
|
||||||
|
cb->showTeleportDialog(&td);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CGWhirlpool::teleportDialogAnswered(const CGHeroInstance *hero, ui32 answer, std::vector<ObjectInstanceID> exits) const
|
||||||
|
{
|
||||||
|
ObjectInstanceID objId(answer);
|
||||||
|
auto realExits = getAllExits();
|
||||||
|
if(!exits.size() && !realExits.size())
|
||||||
|
return;
|
||||||
|
else if(objId == ObjectInstanceID())
|
||||||
|
objId = getRandomExit(hero);
|
||||||
|
else
|
||||||
|
assert(vstd::contains(exits, objId)); // Likely cheating attempt: not random teleporter choosen, but it's not from provided list
|
||||||
|
|
||||||
|
auto obj = cb->getObj(objId);
|
||||||
|
if(obj)
|
||||||
|
{
|
||||||
|
std::set<int3> tiles = obj->getBlockedPos();
|
||||||
|
auto & tile = *RandomGeneratorUtil::nextItem(tiles, cb->gameState()->getRandomGenerator());
|
||||||
|
cb->moveHero(hero->id, tile + int3(1,0,0), true);
|
||||||
|
|
||||||
|
cb->moveHero(hero->id,CGHeroInstance::convertPosition(obj->pos,true) - getVisitableOffset(), true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CGWhirlpool::isProtected( const CGHeroInstance * h )
|
||||||
|
{
|
||||||
|
if(h->hasBonusOfType(Bonus::WHIRLPOOL_PROTECTION)
|
||||||
|
|| (h->Slots().size() == 1 && h->Slots().begin()->second->count == 1)) //we can't remove last unit
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CGArtifact::initObj()
|
void CGArtifact::initObj()
|
||||||
|
@ -247,19 +247,92 @@ public:
|
|||||||
ui32 defaultResProduction();
|
ui32 defaultResProduction();
|
||||||
};
|
};
|
||||||
|
|
||||||
class DLL_LINKAGE CGTeleport : public CGObjectInstance //teleports and subterranean gates
|
struct DLL_LINKAGE TeleportChannel
|
||||||
{
|
{
|
||||||
public:
|
enum EPassability {UNKNOWN, IMPASSABLE, PASSABLE};
|
||||||
static std::map<Obj, std::map<int, std::vector<ObjectInstanceID> > > objs; //teleports: map[ID][subID] => vector of ids
|
|
||||||
static std::vector<std::pair<ObjectInstanceID, ObjectInstanceID> > gates; //subterranean gates: pairs of ids
|
TeleportChannel() : passability(UNKNOWN) {}
|
||||||
void onHeroVisit(const CGHeroInstance * h) const override;
|
|
||||||
void initObj() override;
|
std::vector<ObjectInstanceID> entrances;
|
||||||
static void postInit();
|
std::vector<ObjectInstanceID> exits;
|
||||||
static ObjectInstanceID getMatchingGate(ObjectInstanceID id); //receives id of one subterranean gate and returns id of the paired one, -1 if none
|
EPassability passability;
|
||||||
|
|
||||||
template <typename Handler> void serialize(Handler &h, const int version)
|
template <typename Handler> void serialize(Handler &h, const int version)
|
||||||
{
|
{
|
||||||
h & static_cast<CGObjectInstance&>(*this);
|
h & entrances & exits & passability;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class DLL_LINKAGE CGTeleport : public CGObjectInstance
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
enum EType {UNKNOWN, ENTRANCE, EXIT, BOTH};
|
||||||
|
|
||||||
|
EType type;
|
||||||
|
TeleportChannelID channel;
|
||||||
|
|
||||||
|
CGTeleport();
|
||||||
|
bool isEntrance() const;
|
||||||
|
bool isExit() const;
|
||||||
|
bool isChannelEntrance(ObjectInstanceID id) const;
|
||||||
|
bool isChannelExit(ObjectInstanceID id) const;
|
||||||
|
std::vector<ObjectInstanceID> getAllEntrances(bool excludeCurrent = false) const;
|
||||||
|
std::vector<ObjectInstanceID> getAllExits(bool excludeCurrent = false) const;
|
||||||
|
ObjectInstanceID getRandomExit(const CGHeroInstance * h) const;
|
||||||
|
|
||||||
|
virtual void teleportDialogAnswered(const CGHeroInstance *hero, ui32 answer, std::vector<ObjectInstanceID> exits) const = 0;
|
||||||
|
|
||||||
|
static bool isTeleport(const CGObjectInstance * dst);
|
||||||
|
static bool isConnected(const CGTeleport * src, const CGTeleport * dst);
|
||||||
|
static bool isConnected(const CGObjectInstance * src, const CGObjectInstance * dst);
|
||||||
|
static bool isExitPassable(CGameState * gs, const CGHeroInstance * h, const CGObjectInstance * obj);
|
||||||
|
static std::vector<ObjectInstanceID> getPassableExits(CGameState * gs, const CGHeroInstance * h, std::vector<ObjectInstanceID> exits);
|
||||||
|
static void addToChannel(std::map<TeleportChannelID, shared_ptr<TeleportChannel> > &channelsList, const CGTeleport * obj);
|
||||||
|
|
||||||
|
template <typename Handler> void serialize(Handler &h, const int version)
|
||||||
|
{
|
||||||
|
h & type & channel & static_cast<CGObjectInstance&>(*this);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class DLL_LINKAGE CGMonolith : public CGTeleport
|
||||||
|
{
|
||||||
|
TeleportChannelID findMeChannel(std::vector<Obj> IDs, int SubID) const;
|
||||||
|
|
||||||
|
public:
|
||||||
|
void onHeroVisit(const CGHeroInstance * h) const override;
|
||||||
|
void teleportDialogAnswered(const CGHeroInstance *hero, ui32 answer, std::vector<ObjectInstanceID> exits) const override;
|
||||||
|
void initObj() override;
|
||||||
|
|
||||||
|
template <typename Handler> void serialize(Handler &h, const int version)
|
||||||
|
{
|
||||||
|
h & static_cast<CGTeleport&>(*this);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class DLL_LINKAGE CGSubterraneanGate : public CGMonolith
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
void onHeroVisit(const CGHeroInstance * h) const override;
|
||||||
|
void initObj() override;
|
||||||
|
static void postInit(CGameState * gs);
|
||||||
|
|
||||||
|
template <typename Handler> void serialize(Handler &h, const int version)
|
||||||
|
{
|
||||||
|
h & static_cast<CGMonolith&>(*this);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class DLL_LINKAGE CGWhirlpool : public CGMonolith
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
void onHeroVisit(const CGHeroInstance * h) const override;
|
||||||
|
void teleportDialogAnswered(const CGHeroInstance *hero, ui32 answer, std::vector<ObjectInstanceID> exits) const override;
|
||||||
|
static bool isProtected( const CGHeroInstance * h );
|
||||||
|
|
||||||
|
template <typename Handler> void serialize(Handler &h, const int version)
|
||||||
|
{
|
||||||
|
h & static_cast<CGMonolith&>(*this);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -125,14 +125,18 @@ bool TerrainTile::isClear(const TerrainTile *from /*= nullptr*/) const
|
|||||||
return entrableTerrain(from) && !blocked;
|
return entrableTerrain(from) && !blocked;
|
||||||
}
|
}
|
||||||
|
|
||||||
int TerrainTile::topVisitableId() const
|
Obj TerrainTile::topVisitableId(bool excludeTop) const
|
||||||
{
|
{
|
||||||
return visitableObjects.size() ? visitableObjects.back()->ID : -1;
|
return topVisitableObj(excludeTop) ? topVisitableObj(excludeTop)->ID : Obj(Obj::NO_OBJ);
|
||||||
}
|
}
|
||||||
|
|
||||||
CGObjectInstance * TerrainTile::topVisitableObj() const
|
CGObjectInstance * TerrainTile::topVisitableObj(bool excludeTop) const
|
||||||
{
|
{
|
||||||
return visitableObjects.size() ? visitableObjects.back() : nullptr;
|
auto visitableObj = visitableObjects;
|
||||||
|
if(excludeTop && visitableObj.size())
|
||||||
|
visitableObj.pop_back();
|
||||||
|
|
||||||
|
return visitableObj.size() ? visitableObj.back() : nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool TerrainTile::isCoastal() const
|
bool TerrainTile::isCoastal() const
|
||||||
|
@ -289,8 +289,8 @@ struct DLL_LINKAGE TerrainTile
|
|||||||
/// Checks for blocking objects and terraint type (water / land).
|
/// Checks for blocking objects and terraint type (water / land).
|
||||||
bool isClear(const TerrainTile * from = nullptr) const;
|
bool isClear(const TerrainTile * from = nullptr) const;
|
||||||
/// Gets the ID of the top visitable object or -1 if there is none.
|
/// Gets the ID of the top visitable object or -1 if there is none.
|
||||||
int topVisitableId() const;
|
Obj topVisitableId(bool excludeTop = false) const;
|
||||||
CGObjectInstance * topVisitableObj() const;
|
CGObjectInstance * topVisitableObj(bool excludeTop = false) const;
|
||||||
bool isWater() const;
|
bool isWater() const;
|
||||||
bool isCoastal() const;
|
bool isCoastal() const;
|
||||||
bool hasFavourableWinds() const;
|
bool hasFavourableWinds() const;
|
||||||
@ -432,6 +432,7 @@ public:
|
|||||||
|
|
||||||
//Helper lists
|
//Helper lists
|
||||||
std::vector< ConstTransitivePtr<CGHeroInstance> > heroesOnMap;
|
std::vector< ConstTransitivePtr<CGHeroInstance> > heroesOnMap;
|
||||||
|
std::map<TeleportChannelID, shared_ptr<TeleportChannel> > teleportChannels;
|
||||||
|
|
||||||
/// associative list to identify which hero/creature id belongs to which object id(index for objects)
|
/// associative list to identify which hero/creature id belongs to which object id(index for objects)
|
||||||
std::map<si32, ObjectInstanceID> questIdentifierToId;
|
std::map<si32, ObjectInstanceID> questIdentifierToId;
|
||||||
@ -499,11 +500,9 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
h & objects;
|
h & objects;
|
||||||
h & heroesOnMap & towns & artInstances;
|
h & heroesOnMap & teleportChannels & towns & artInstances;
|
||||||
|
|
||||||
// static members
|
// static members
|
||||||
h & CGTeleport::objs;
|
|
||||||
h & CGTeleport::gates;
|
|
||||||
h & CGKeys::playerKeyMap;
|
h & CGKeys::playerKeyMap;
|
||||||
h & CGMagi::eyelist;
|
h & CGMagi::eyelist;
|
||||||
h & CGObelisk::obeliskCount & CGObelisk::visited;
|
h & CGObelisk::obeliskCount & CGObelisk::visited;
|
||||||
|
@ -36,6 +36,9 @@ void registerTypesMapObjects1(Serializer &s)
|
|||||||
|
|
||||||
// Non-armed objects
|
// Non-armed objects
|
||||||
s.template registerType<CGObjectInstance, CGTeleport>();
|
s.template registerType<CGObjectInstance, CGTeleport>();
|
||||||
|
s.template registerType<CGTeleport, CGMonolith>();
|
||||||
|
s.template registerType<CGMonolith, CGSubterraneanGate>();
|
||||||
|
s.template registerType<CGMonolith, CGWhirlpool>();
|
||||||
s.template registerType<CGObjectInstance, CGSignBottle>();
|
s.template registerType<CGObjectInstance, CGSignBottle>();
|
||||||
s.template registerType<CGObjectInstance, CGScholar>();
|
s.template registerType<CGObjectInstance, CGScholar>();
|
||||||
s.template registerType<CGObjectInstance, CGMagicWell>();
|
s.template registerType<CGObjectInstance, CGMagicWell>();
|
||||||
@ -121,7 +124,9 @@ void registerTypesMapObjectTypes(Serializer &s)
|
|||||||
REGISTER_GENERIC_HANDLER(CGShrine);
|
REGISTER_GENERIC_HANDLER(CGShrine);
|
||||||
REGISTER_GENERIC_HANDLER(CGSignBottle);
|
REGISTER_GENERIC_HANDLER(CGSignBottle);
|
||||||
REGISTER_GENERIC_HANDLER(CGSirens);
|
REGISTER_GENERIC_HANDLER(CGSirens);
|
||||||
REGISTER_GENERIC_HANDLER(CGTeleport);
|
REGISTER_GENERIC_HANDLER(CGMonolith);
|
||||||
|
REGISTER_GENERIC_HANDLER(CGSubterraneanGate);
|
||||||
|
REGISTER_GENERIC_HANDLER(CGWhirlpool);
|
||||||
REGISTER_GENERIC_HANDLER(CGTownInstance);
|
REGISTER_GENERIC_HANDLER(CGTownInstance);
|
||||||
REGISTER_GENERIC_HANDLER(CGUniversity);
|
REGISTER_GENERIC_HANDLER(CGUniversity);
|
||||||
REGISTER_GENERIC_HANDLER(CGVisitableOPH);
|
REGISTER_GENERIC_HANDLER(CGVisitableOPH);
|
||||||
@ -282,6 +287,7 @@ void registerTypesClientPacks2(Serializer &s)
|
|||||||
s.template registerType<Query, BlockingDialog>();
|
s.template registerType<Query, BlockingDialog>();
|
||||||
s.template registerType<Query, GarrisonDialog>();
|
s.template registerType<Query, GarrisonDialog>();
|
||||||
s.template registerType<Query, ExchangeDialog>();
|
s.template registerType<Query, ExchangeDialog>();
|
||||||
|
s.template registerType<Query, TeleportDialog>();
|
||||||
|
|
||||||
s.template registerType<CPackForClient, CGarrisonOperationPack>();
|
s.template registerType<CPackForClient, CGarrisonOperationPack>();
|
||||||
s.template registerType<CGarrisonOperationPack, ChangeStackCount>();
|
s.template registerType<CGarrisonOperationPack, ChangeStackCount>();
|
||||||
|
@ -381,11 +381,11 @@ void CMapGenerator::createConnections()
|
|||||||
|
|
||||||
if (withinZone)
|
if (withinZone)
|
||||||
{
|
{
|
||||||
auto gate1 = new CGTeleport;
|
auto gate1 = new CGSubterraneanGate;
|
||||||
gate1->ID = Obj::SUBTERRANEAN_GATE;
|
gate1->ID = Obj::SUBTERRANEAN_GATE;
|
||||||
gate1->subID = 0;
|
gate1->subID = 0;
|
||||||
zoneA->placeAndGuardObject(this, gate1, tile, connection.getGuardStrength());
|
zoneA->placeAndGuardObject(this, gate1, tile, connection.getGuardStrength());
|
||||||
auto gate2 = new CGTeleport(*gate1);
|
auto gate2 = new CGSubterraneanGate(*gate1);
|
||||||
zoneB->placeAndGuardObject(this, gate2, otherTile, connection.getGuardStrength());
|
zoneB->placeAndGuardObject(this, gate2, otherTile, connection.getGuardStrength());
|
||||||
|
|
||||||
stop = true; //we are done, go to next connection
|
stop = true; //we are done, go to next connection
|
||||||
@ -398,11 +398,11 @@ void CMapGenerator::createConnections()
|
|||||||
}
|
}
|
||||||
if (!guardPos.valid())
|
if (!guardPos.valid())
|
||||||
{
|
{
|
||||||
auto teleport1 = new CGTeleport;
|
auto teleport1 = new CGMonolith;
|
||||||
teleport1->ID = Obj::MONOLITH_TWO_WAY;
|
teleport1->ID = Obj::MONOLITH_TWO_WAY;
|
||||||
teleport1->subID = getNextMonlithIndex();
|
teleport1->subID = getNextMonlithIndex();
|
||||||
|
|
||||||
auto teleport2 = new CGTeleport(*teleport1);
|
auto teleport2 = new CGMonolith(*teleport1);
|
||||||
|
|
||||||
zoneA->addRequiredObject (teleport1, connection.getGuardStrength());
|
zoneA->addRequiredObject (teleport1, connection.getGuardStrength());
|
||||||
zoneB->addRequiredObject (teleport2, connection.getGuardStrength());
|
zoneB->addRequiredObject (teleport2, connection.getGuardStrength());
|
||||||
|
@ -1665,7 +1665,7 @@ void CGameHandler::setAmount(ObjectInstanceID objid, ui32 val)
|
|||||||
sendAndApply(&sop);
|
sendAndApply(&sop);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CGameHandler::moveHero( ObjectInstanceID hid, int3 dst, ui8 teleporting, PlayerColor asker /*= 255*/ )
|
bool CGameHandler::moveHero( ObjectInstanceID hid, int3 dst, ui8 teleporting, bool transit, PlayerColor asker /*= 255*/ )
|
||||||
{
|
{
|
||||||
const CGHeroInstance *h = getHero(hid);
|
const CGHeroInstance *h = getHero(hid);
|
||||||
|
|
||||||
@ -1765,7 +1765,8 @@ bool CGameHandler::moveHero( ObjectInstanceID hid, int3 dst, ui8 teleporting, Pl
|
|||||||
}
|
}
|
||||||
else if(visitDest == VISIT_DEST)
|
else if(visitDest == VISIT_DEST)
|
||||||
{
|
{
|
||||||
visitObjectOnTile(t, h);
|
if(!transit || !CGTeleport::isTeleport(t.topVisitableObj()))
|
||||||
|
visitObjectOnTile(t, h);
|
||||||
}
|
}
|
||||||
|
|
||||||
queries.popIfTop(moveQuery);
|
queries.popIfTop(moveQuery);
|
||||||
@ -1900,6 +1901,14 @@ void CGameHandler::showBlockingDialog( BlockingDialog *iw )
|
|||||||
sendToAllClients(iw);
|
sendToAllClients(iw);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CGameHandler::showTeleportDialog( TeleportDialog *iw )
|
||||||
|
{
|
||||||
|
auto dialogQuery = make_shared<CTeleportDialogQuery>(*iw);
|
||||||
|
queries.addQuery(dialogQuery);
|
||||||
|
iw->queryID = dialogQuery->queryID;
|
||||||
|
sendToAllClients(iw);
|
||||||
|
}
|
||||||
|
|
||||||
void CGameHandler::giveResource(PlayerColor player, Res::ERes which, int val) //TODO: cap according to Bersy's suggestion
|
void CGameHandler::giveResource(PlayerColor player, Res::ERes which, int val) //TODO: cap according to Bersy's suggestion
|
||||||
{
|
{
|
||||||
if(!val) return; //don't waste time on empty call
|
if(!val) return; //don't waste time on empty call
|
||||||
|
@ -134,6 +134,7 @@ public:
|
|||||||
//void showInfoDialog(InfoWindow *iw) override;
|
//void showInfoDialog(InfoWindow *iw) override;
|
||||||
|
|
||||||
void showBlockingDialog(BlockingDialog *iw) override;
|
void showBlockingDialog(BlockingDialog *iw) override;
|
||||||
|
void showTeleportDialog(TeleportDialog *iw) override;
|
||||||
void showGarrisonDialog(ObjectInstanceID upobj, ObjectInstanceID hid, bool removableUnits) override;
|
void showGarrisonDialog(ObjectInstanceID upobj, ObjectInstanceID hid, bool removableUnits) override;
|
||||||
void showThievesGuildWindow(PlayerColor player, ObjectInstanceID requestingObjId) override;
|
void showThievesGuildWindow(PlayerColor player, ObjectInstanceID requestingObjId) override;
|
||||||
void giveResource(PlayerColor player, Res::ERes which, int val) override;
|
void giveResource(PlayerColor player, Res::ERes which, int val) override;
|
||||||
@ -167,7 +168,7 @@ public:
|
|||||||
void startBattleI(const CArmedInstance *army1, const CArmedInstance *army2, int3 tile, bool creatureBank = false) override; //if any of armies is hero, hero will be used
|
void startBattleI(const CArmedInstance *army1, const CArmedInstance *army2, int3 tile, bool creatureBank = false) override; //if any of armies is hero, hero will be used
|
||||||
void startBattleI(const CArmedInstance *army1, const CArmedInstance *army2, bool creatureBank = false) override; //if any of armies is hero, hero will be used, visitable tile of second obj is place of battle//void startBattleI(int heroID, CCreatureSet army, int3 tile, std::function<void(BattleResult*)> cb) override; //for hero<=>neutral army
|
void startBattleI(const CArmedInstance *army1, const CArmedInstance *army2, bool creatureBank = false) override; //if any of armies is hero, hero will be used, visitable tile of second obj is place of battle//void startBattleI(int heroID, CCreatureSet army, int3 tile, std::function<void(BattleResult*)> cb) override; //for hero<=>neutral army
|
||||||
void setAmount(ObjectInstanceID objid, ui32 val) override;
|
void setAmount(ObjectInstanceID objid, ui32 val) override;
|
||||||
bool moveHero(ObjectInstanceID hid, int3 dst, ui8 teleporting, PlayerColor asker = PlayerColor::NEUTRAL) override;
|
bool moveHero(ObjectInstanceID hid, int3 dst, ui8 teleporting, bool transit = false, PlayerColor asker = PlayerColor::NEUTRAL) override;
|
||||||
void giveHeroBonus(GiveBonus * bonus) override;
|
void giveHeroBonus(GiveBonus * bonus) override;
|
||||||
void setMovePoints(SetMovePoints * smp) override;
|
void setMovePoints(SetMovePoints * smp) override;
|
||||||
void setManaPoints(ObjectInstanceID hid, int val) override;
|
void setManaPoints(ObjectInstanceID hid, int val) override;
|
||||||
|
@ -313,6 +313,18 @@ CBlockingDialogQuery::CBlockingDialogQuery(const BlockingDialog &bd)
|
|||||||
addPlayer(bd.player);
|
addPlayer(bd.player);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CTeleportDialogQuery::notifyObjectAboutRemoval(const CObjectVisitQuery &objectVisit) const
|
||||||
|
{
|
||||||
|
auto obj = dynamic_cast<const CGTeleport *>(objectVisit.visitedObject);
|
||||||
|
obj->teleportDialogAnswered(objectVisit.visitingHero, *answer, td.exits);
|
||||||
|
}
|
||||||
|
|
||||||
|
CTeleportDialogQuery::CTeleportDialogQuery(const TeleportDialog &td)
|
||||||
|
{
|
||||||
|
this->td = td;
|
||||||
|
addPlayer(td.hero->tempOwner);
|
||||||
|
}
|
||||||
|
|
||||||
CHeroLevelUpDialogQuery::CHeroLevelUpDialogQuery(const HeroLevelUp &Hlu)
|
CHeroLevelUpDialogQuery::CHeroLevelUpDialogQuery(const HeroLevelUp &Hlu)
|
||||||
{
|
{
|
||||||
hlu = Hlu;
|
hlu = Hlu;
|
||||||
|
@ -130,6 +130,16 @@ public:
|
|||||||
virtual void notifyObjectAboutRemoval(const CObjectVisitQuery &objectVisit) const override;
|
virtual void notifyObjectAboutRemoval(const CObjectVisitQuery &objectVisit) const override;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class CTeleportDialogQuery : public CDialogQuery
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
TeleportDialog td; //copy of pack... debug purposes
|
||||||
|
|
||||||
|
CTeleportDialogQuery(const TeleportDialog &td);
|
||||||
|
|
||||||
|
virtual void notifyObjectAboutRemoval(const CObjectVisitQuery &objectVisit) const override;
|
||||||
|
};
|
||||||
|
|
||||||
class CHeroLevelUpDialogQuery : public CDialogQuery
|
class CHeroLevelUpDialogQuery : public CDialogQuery
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
@ -83,7 +83,7 @@ bool DismissHero::applyGh( CGameHandler *gh )
|
|||||||
bool MoveHero::applyGh( CGameHandler *gh )
|
bool MoveHero::applyGh( CGameHandler *gh )
|
||||||
{
|
{
|
||||||
ERROR_IF_NOT_OWNS(hid);
|
ERROR_IF_NOT_OWNS(hid);
|
||||||
return gh->moveHero(hid,dest,0,gh->getPlayerAt(c));
|
return gh->moveHero(hid,dest,0,transit,gh->getPlayerAt(c));
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CastleTeleportHero::applyGh( CGameHandler *gh )
|
bool CastleTeleportHero::applyGh( CGameHandler *gh )
|
||||||
|
Loading…
Reference in New Issue
Block a user