mirror of
https://github.com/vcmi/vcmi.git
synced 2024-11-28 08:48:48 +02:00
18535db0ef
TeleportDialog is based off BlockingDialog and it's needed for server to ask client what teleport hero should be teleported to. It's also contain list of possible exits, identifier of currently used channel and also impassable option. If impassable set to true then client will remember that current teleport channel is lack of exit point.
419 lines
11 KiB
C++
419 lines
11 KiB
C++
#include "StdInc.h"
|
|
#include "CQuery.h"
|
|
#include "CGameHandler.h"
|
|
#include "../lib/BattleState.h"
|
|
|
|
boost::mutex Queries::mx;
|
|
|
|
template <typename Container>
|
|
std::string formatContainer(const Container &c, std::string delimeter=", ", std::string opener="(", std::string closer=")")
|
|
{
|
|
std::string ret = opener;
|
|
auto itr = std::begin(c);
|
|
if(itr != std::end(c))
|
|
{
|
|
ret += boost::lexical_cast<std::string>(*itr);
|
|
while(++itr != std::end(c))
|
|
{
|
|
ret += delimeter;
|
|
ret += boost::lexical_cast<std::string>(*itr);
|
|
}
|
|
}
|
|
ret += closer;
|
|
return ret;
|
|
}
|
|
|
|
std::ostream & operator<<(std::ostream &out, const CQuery &query)
|
|
{
|
|
return out << query.toString();
|
|
}
|
|
|
|
std::ostream & operator<<(std::ostream &out, QueryPtr query)
|
|
{
|
|
return out << "[" << query.get() << "] " << query->toString();
|
|
}
|
|
|
|
CQuery::CQuery(void)
|
|
{
|
|
boost::unique_lock<boost::mutex> l(Queries::mx);
|
|
|
|
static QueryID QID = QueryID(0);
|
|
|
|
queryID = ++QID;
|
|
logGlobal->traceStream() << "Created a new query with id " << queryID;
|
|
}
|
|
|
|
|
|
CQuery::~CQuery(void)
|
|
{
|
|
logGlobal->traceStream() << "Destructed the query with id " << queryID;
|
|
}
|
|
|
|
void CQuery::addPlayer(PlayerColor color)
|
|
{
|
|
if(color.isValidPlayer())
|
|
{
|
|
players.push_back(color);
|
|
}
|
|
}
|
|
|
|
std::string CQuery::toString() const
|
|
{
|
|
std::string ret = boost::str(boost::format("A query of type %s and qid=%d affecting players %s") % typeid(*this).name() % queryID % formatContainer(players));
|
|
return ret;
|
|
}
|
|
|
|
bool CQuery::endsByPlayerAnswer() const
|
|
{
|
|
return false;
|
|
}
|
|
|
|
void CQuery::onRemoval(CGameHandler *gh, PlayerColor color)
|
|
{
|
|
}
|
|
|
|
bool CQuery::blocksPack(const CPack *pack) const
|
|
{
|
|
return false;
|
|
}
|
|
|
|
void CQuery::notifyObjectAboutRemoval(const CObjectVisitQuery &objectVisit) const
|
|
{
|
|
}
|
|
|
|
void CQuery::onExposure(CGameHandler *gh, QueryPtr topQuery)
|
|
{
|
|
gh->queries.popQuery(*this);
|
|
}
|
|
|
|
void CQuery::onAdding(CGameHandler *gh, PlayerColor color)
|
|
{
|
|
|
|
}
|
|
|
|
CObjectVisitQuery::CObjectVisitQuery(const CGObjectInstance *Obj, const CGHeroInstance *Hero, int3 Tile)
|
|
: visitedObject(Obj), visitingHero(Hero), tile(Tile), removeObjectAfterVisit(false)
|
|
{
|
|
addPlayer(Hero->tempOwner);
|
|
}
|
|
|
|
bool CObjectVisitQuery::blocksPack(const CPack *pack) const
|
|
{
|
|
//During the visit itself ALL actions are blocked.
|
|
//(However, the visit may trigger a query above that'll pass some.)
|
|
return true;
|
|
}
|
|
|
|
void CObjectVisitQuery::onRemoval(CGameHandler *gh, PlayerColor color)
|
|
{
|
|
gh->objectVisitEnded(*this);
|
|
|
|
//TODO or should it be destructor?
|
|
//Can object visit affect 2 players and what would be desired behavior?
|
|
if(removeObjectAfterVisit)
|
|
gh->removeObject(visitedObject);
|
|
}
|
|
|
|
void CObjectVisitQuery::onExposure(CGameHandler *gh, QueryPtr topQuery)
|
|
{
|
|
//Object may have been removed and deleted.
|
|
if(gh->isValidObject(visitedObject))
|
|
topQuery->notifyObjectAboutRemoval(*this);
|
|
|
|
gh->queries.popQuery(*this);
|
|
}
|
|
|
|
void Queries::popQuery(PlayerColor player, QueryPtr query)
|
|
{
|
|
LOG_TRACE_PARAMS(logGlobal, "player='%s', query='%s'", player % query);
|
|
if(topQuery(player) != query)
|
|
{
|
|
logGlobal->traceStream() << "Cannot remove, not a top!";
|
|
return;
|
|
}
|
|
|
|
queries[player] -= query;
|
|
auto nextQuery = topQuery(player);
|
|
|
|
query->onRemoval(gh, player);
|
|
|
|
//Exposure on query below happens only if removal didn't trigger any new query
|
|
if(nextQuery && nextQuery == topQuery(player))
|
|
{
|
|
nextQuery->onExposure(gh, query);
|
|
}
|
|
}
|
|
|
|
void Queries::popQuery(const CQuery &query)
|
|
{
|
|
LOG_TRACE_PARAMS(logGlobal, "query='%s'", query);
|
|
|
|
assert(query.players.size());
|
|
for(auto player : query.players)
|
|
{
|
|
auto top = topQuery(player);
|
|
if(top.get() == &query)
|
|
popQuery(top);
|
|
else
|
|
logGlobal->traceStream() << "Cannot remove query " << query;
|
|
}
|
|
}
|
|
|
|
void Queries::popQuery(QueryPtr query)
|
|
{
|
|
for(auto player : query->players)
|
|
popQuery(player, query);
|
|
}
|
|
|
|
void Queries::addQuery(QueryPtr query)
|
|
{
|
|
for(auto player : query->players)
|
|
addQuery(player, query);
|
|
}
|
|
|
|
void Queries::addQuery(PlayerColor player, QueryPtr query)
|
|
{
|
|
LOG_TRACE_PARAMS(logGlobal, "player='%s', query='%s'", player % query);
|
|
query->onAdding(gh, player);
|
|
queries[player].push_back(query);
|
|
}
|
|
|
|
QueryPtr Queries::topQuery(PlayerColor player)
|
|
{
|
|
return vstd::backOrNull(queries[player]);
|
|
}
|
|
|
|
void Queries::popIfTop(QueryPtr query)
|
|
{
|
|
LOG_TRACE_PARAMS(logGlobal, "query='%d'", query);
|
|
if(!query)
|
|
logGlobal->errorStream() << "The query is nullptr! Ignoring.";
|
|
|
|
popIfTop(*query);
|
|
}
|
|
|
|
void Queries::popIfTop(const CQuery &query)
|
|
{
|
|
for(PlayerColor color : query.players)
|
|
if(topQuery(color).get() == &query)
|
|
popQuery(color, topQuery(color));
|
|
}
|
|
|
|
std::vector<shared_ptr<const CQuery>> Queries::allQueries() const
|
|
{
|
|
std::vector<shared_ptr<const CQuery>> ret;
|
|
for(auto &playerQueries : queries)
|
|
for(auto &query : playerQueries.second)
|
|
ret.push_back(query);
|
|
|
|
return ret;
|
|
}
|
|
|
|
std::vector<shared_ptr<CQuery>> Queries::allQueries()
|
|
{
|
|
//TODO code duplication with const function :(
|
|
std::vector<shared_ptr<CQuery>> ret;
|
|
for(auto &playerQueries : queries)
|
|
for(auto &query : playerQueries.second)
|
|
ret.push_back(query);
|
|
|
|
return ret;
|
|
}
|
|
|
|
void CBattleQuery::notifyObjectAboutRemoval(const CObjectVisitQuery &objectVisit) const
|
|
{
|
|
assert(result);
|
|
objectVisit.visitedObject->battleFinished(objectVisit.visitingHero, *result);
|
|
}
|
|
|
|
CBattleQuery::CBattleQuery(const BattleInfo *Bi)
|
|
{
|
|
belligerents[0] = Bi->sides[0].armyObject;
|
|
belligerents[1] = Bi->sides[1].armyObject;
|
|
|
|
bi = Bi;
|
|
|
|
for(auto &side : bi->sides)
|
|
addPlayer(side.color);
|
|
}
|
|
|
|
CBattleQuery::CBattleQuery()
|
|
{
|
|
|
|
}
|
|
|
|
bool CBattleQuery::blocksPack(const CPack *pack) const
|
|
{
|
|
return !dynamic_cast<const MakeAction*>(pack) && !dynamic_cast<const MakeCustomAction*>(pack);
|
|
}
|
|
|
|
void CBattleQuery::onRemoval(CGameHandler *gh, PlayerColor color)
|
|
{
|
|
gh->battleAfterLevelUp(*result);
|
|
}
|
|
|
|
void CGarrisonDialogQuery::notifyObjectAboutRemoval(const CObjectVisitQuery &objectVisit) const
|
|
{
|
|
objectVisit.visitedObject->garrisonDialogClosed(objectVisit.visitingHero);
|
|
}
|
|
|
|
CGarrisonDialogQuery::CGarrisonDialogQuery(const CArmedInstance *up, const CArmedInstance *down)
|
|
{
|
|
exchangingArmies[0] = up;
|
|
exchangingArmies[1] = down;
|
|
|
|
addPlayer(up->tempOwner);
|
|
addPlayer(down->tempOwner);
|
|
}
|
|
|
|
bool CGarrisonDialogQuery::blocksPack(const CPack *pack) const
|
|
{
|
|
std::set<ObjectInstanceID> ourIds;
|
|
ourIds.insert(this->exchangingArmies[0]->id);
|
|
ourIds.insert(this->exchangingArmies[1]->id);
|
|
|
|
|
|
if (auto stacks = dynamic_cast<const ArrangeStacks*>(pack))
|
|
{
|
|
return !vstd::contains(ourIds, stacks->id1) || !vstd::contains(ourIds, stacks->id2);
|
|
}
|
|
if (auto arts = dynamic_cast<const ExchangeArtifacts*>(pack))
|
|
{
|
|
if(auto id1 = boost::apply_visitor(GetEngagedHeroIds(), arts->src.artHolder))
|
|
if(!vstd::contains(ourIds, *id1))
|
|
return true;
|
|
|
|
if(auto id2 = boost::apply_visitor(GetEngagedHeroIds(), arts->dst.artHolder))
|
|
if(!vstd::contains(ourIds, *id2))
|
|
return true;
|
|
return false;
|
|
}
|
|
if (auto dismiss = dynamic_cast<const DisbandCreature*>(pack))
|
|
{
|
|
return !vstd::contains(ourIds, dismiss->id);
|
|
}
|
|
|
|
if (auto dismiss = dynamic_cast<const AssembleArtifacts*>(pack))
|
|
{
|
|
return !vstd::contains(ourIds, dismiss->heroID);
|
|
}
|
|
|
|
return CDialogQuery::blocksPack(pack);
|
|
}
|
|
|
|
void CBlockingDialogQuery::notifyObjectAboutRemoval(const CObjectVisitQuery &objectVisit) const
|
|
{
|
|
assert(answer);
|
|
objectVisit.visitedObject->blockingDialogAnswered(objectVisit.visitingHero, *answer);
|
|
}
|
|
|
|
CBlockingDialogQuery::CBlockingDialogQuery(const BlockingDialog &bd)
|
|
{
|
|
this->bd = bd;
|
|
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)
|
|
{
|
|
hlu = Hlu;
|
|
addPlayer(hlu.hero->tempOwner);
|
|
}
|
|
|
|
void CHeroLevelUpDialogQuery::onRemoval(CGameHandler *gh, PlayerColor color)
|
|
{
|
|
assert(answer);
|
|
logGlobal->traceStream() << "Completing hero level-up query. " << hlu.hero->getObjectName() << " gains skill " << *answer;
|
|
gh->levelUpHero(hlu.hero, hlu.skills[*answer]);
|
|
}
|
|
|
|
void CHeroLevelUpDialogQuery::notifyObjectAboutRemoval(const CObjectVisitQuery &objectVisit) const
|
|
{
|
|
objectVisit.visitedObject->heroLevelUpDone(objectVisit.visitingHero);
|
|
}
|
|
|
|
CCommanderLevelUpDialogQuery::CCommanderLevelUpDialogQuery(const CommanderLevelUp &Clu)
|
|
{
|
|
clu = Clu;
|
|
addPlayer(clu.hero->tempOwner);
|
|
}
|
|
|
|
void CCommanderLevelUpDialogQuery::onRemoval(CGameHandler *gh, PlayerColor color)
|
|
{
|
|
assert(answer);
|
|
logGlobal->traceStream() << "Completing commander level-up query. Commander of hero " << clu.hero->getObjectName() << " gains skill " << *answer;
|
|
gh->levelUpCommander(clu.hero->commander, clu.skills[*answer]);
|
|
}
|
|
|
|
void CCommanderLevelUpDialogQuery::notifyObjectAboutRemoval(const CObjectVisitQuery &objectVisit) const
|
|
{
|
|
objectVisit.visitedObject->heroLevelUpDone(objectVisit.visitingHero);
|
|
}
|
|
|
|
bool CDialogQuery::endsByPlayerAnswer() const
|
|
{
|
|
return true;
|
|
}
|
|
|
|
bool CDialogQuery::blocksPack(const CPack *pack) const
|
|
{
|
|
//We accept only query replies from correct player
|
|
if(auto reply = dynamic_cast<const QueryReply *>(pack))
|
|
{
|
|
return !vstd::contains(players, reply->player);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
CHeroMovementQuery::CHeroMovementQuery(const TryMoveHero &Tmh, const CGHeroInstance *Hero, bool VisitDestAfterVictory)
|
|
: tmh(Tmh), visitDestAfterVictory(VisitDestAfterVictory), hero(Hero)
|
|
{
|
|
players.push_back(hero->tempOwner);
|
|
}
|
|
|
|
void CHeroMovementQuery::onExposure(CGameHandler *gh, QueryPtr topQuery)
|
|
{
|
|
assert(players.size() == 1);
|
|
|
|
if(visitDestAfterVictory && hero->tempOwner == players[0]) //hero still alive, so he won with the guard
|
|
//TODO what if there were H4-like escape? we should also check pos
|
|
{
|
|
logGlobal->traceStream() << "Hero " << hero->name << " after victory over guard finishes visit to " << tmh.end;
|
|
//finish movement
|
|
visitDestAfterVictory = false;
|
|
gh->visitObjectOnTile(*gh->getTile(CGHeroInstance::convertPosition(tmh.end, false)), hero);
|
|
}
|
|
|
|
gh->queries.popIfTop(*this);
|
|
}
|
|
|
|
void CHeroMovementQuery::onRemoval(CGameHandler *gh, PlayerColor color)
|
|
{
|
|
PlayerBlocked pb;
|
|
pb.player = color;
|
|
pb.reason = PlayerBlocked::ONGOING_MOVEMENT;
|
|
pb.startOrEnd = PlayerBlocked::BLOCKADE_ENDED;
|
|
gh->sendAndApply(&pb);
|
|
}
|
|
|
|
void CHeroMovementQuery::onAdding(CGameHandler *gh, PlayerColor color)
|
|
{
|
|
PlayerBlocked pb;
|
|
pb.player = color;
|
|
pb.reason = PlayerBlocked::ONGOING_MOVEMENT;
|
|
pb.startOrEnd = PlayerBlocked::BLOCKADE_STARTED;
|
|
gh->sendAndApply(&pb);
|
|
}
|