From b5100bee94e28d33dcf9d14835ad895b40f1d57b Mon Sep 17 00:00:00 2001 From: ArseniyShestakov Date: Wed, 2 Dec 2015 17:56:26 +0300 Subject: [PATCH] Teleport: rework code to support exit point selection for whirlpools --- AI/VCAI/VCAI.cpp | 20 +++++++----- AI/VCAI/VCAI.h | 9 ++++++ client/CPlayerInterface.cpp | 14 ++++++--- client/CPlayerInterface.h | 3 +- lib/Connection.h | 2 +- lib/mapObjects/MiscObjects.cpp | 57 +++++++++++++++++++++------------- lib/mapObjects/MiscObjects.h | 8 ++--- 7 files changed, 72 insertions(+), 41 deletions(-) diff --git a/AI/VCAI/VCAI.cpp b/AI/VCAI/VCAI.cpp index 18a1801ec..e84694703 100644 --- a/AI/VCAI/VCAI.cpp +++ b/AI/VCAI/VCAI.cpp @@ -96,6 +96,7 @@ VCAI::VCAI(void) LOG_TRACE(logAi); makingTurn = nullptr; destinationTeleport = ObjectInstanceID(); + destinationTeleportPos = int3(); } VCAI::~VCAI(void) @@ -616,31 +617,32 @@ void VCAI::showBlockingDialog(const std::string &text, const std::vectorpassability = TeleportChannel::IMPASSABLE; else { - if(destinationTeleport != ObjectInstanceID() && vstd::contains(exits, destinationTeleport)) - choosenExit = destinationTeleport; + auto neededExit = std::make_pair(destinationTeleport, destinationTeleportPos); + if(destinationTeleport != ObjectInstanceID() && vstd::contains(exits, neededExit)) + choosenExit = vstd::find_pos(exits, neededExit); - if(!status.channelProbing()) +/* 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()); + answerQuery(askID, choosenExit); }); } @@ -1863,8 +1865,10 @@ bool VCAI::moveHeroToTile(int3 dst, HeroPtr h) auto doTeleportMovement = [&](int3 dst, ObjectInstanceID exitId) { destinationTeleport = exitId; - cb->moveHero(*h, CGHeroInstance::convertPosition(dst, true)); + destinationTeleportPos = CGHeroInstance::convertPosition(dst, true); + cb->moveHero(*h, destinationTeleportPos); destinationTeleport = ObjectInstanceID(); + destinationTeleportPos = int3(); afterMovementCheck(); }; diff --git a/AI/VCAI/VCAI.h b/AI/VCAI/VCAI.h index 15fd7c7d4..aa5eb521b 100644 --- a/AI/VCAI/VCAI.h +++ b/AI/VCAI/VCAI.h @@ -148,6 +148,7 @@ public: std::map > knownTeleportChannels; std::map knownSubterraneanGates; ObjectInstanceID destinationTeleport; + int3 destinationTeleportPos; std::vector teleportChannelProbingList; //list of teleport channel exits that not visible and need to be (re-)explored //std::vector visitedThisWeek; //only OPWs std::map > townVisitsThisWeek; @@ -360,6 +361,14 @@ public: template void serializeInternal(Handler &h, const int version) { h & knownTeleportChannels & knownSubterraneanGates & destinationTeleport; + if(version >= 755) + { + h & destinationTeleportPos; + } + else if(!h.saving) + { + destinationTeleportPos = int3(); + } h & townVisitsThisWeek & lockedHeroes & reservedHeroesMap; //FIXME: cannot instantiate abstract class h & visitableObjs & alreadyVisited & reservedObjs; h & saving & status & battlename; diff --git a/client/CPlayerInterface.cpp b/client/CPlayerInterface.cpp index 3b5bdf7f2..6132d8035 100644 --- a/client/CPlayerInterface.cpp +++ b/client/CPlayerInterface.cpp @@ -97,6 +97,7 @@ CPlayerInterface::CPlayerInterface(PlayerColor Player) { logGlobal->traceStream() << "\tHuman player interface for player " << Player << " being constructed"; destinationTeleport = ObjectInstanceID(); + destinationTeleportPos = int3(); observerInDuelMode = false; howManyPeople++; GH.defActionsDef = 0; @@ -1147,14 +1148,15 @@ void CPlayerInterface::showBlockingDialog( const std::string &text, const std::v } -void CPlayerInterface::showTeleportDialog(TeleportChannelID channel, std::vector exits, bool impassable, QueryID askID) +void CPlayerInterface::showTeleportDialog(TeleportChannelID channel, TTeleportExitsList exits, bool impassable, QueryID askID) { EVENT_HANDLER_CALLED_BY_CLIENT; - ObjectInstanceID choosenExit; - if(destinationTeleport != ObjectInstanceID() && vstd::contains(exits, destinationTeleport)) - choosenExit = destinationTeleport; + int choosenExit = -1; + auto neededExit = std::make_pair(destinationTeleport, destinationTeleportPos); + if(destinationTeleport != ObjectInstanceID() && vstd::contains(exits, neededExit)) + choosenExit = vstd::find_pos(exits, neededExit); - cb->selectionMade(choosenExit.getNum(), askID); + cb->selectionMade(choosenExit, askID); } void CPlayerInterface::tileRevealed(const std::unordered_set &pos) @@ -1414,6 +1416,7 @@ void CPlayerInterface::requestRealized( PackageApplied *pa ) && stillMoveHero.get() == DURING_MOVE) { // After teleportation via CGTeleport object is finished destinationTeleport = ObjectInstanceID(); + destinationTeleportPos = int3(); stillMoveHero.setn(CONTINUE_MOVE); } } @@ -2663,6 +2666,7 @@ void CPlayerInterface::doMoveHero(const CGHeroInstance * h, CGPath path) { CCS->soundh->stopSound(sh); destinationTeleport = nextObject->id; + destinationTeleportPos = nextCoord; doMovement(h->pos, false); sh = CCS->soundh->playSound(CCS->soundh->horseSounds[currentTerrain], -1); continue; diff --git a/client/CPlayerInterface.h b/client/CPlayerInterface.h index 2e19aae30..ad9e43e85 100644 --- a/client/CPlayerInterface.h +++ b/client/CPlayerInterface.h @@ -90,6 +90,7 @@ class CPlayerInterface : public CGameInterface, public IUpdateable public: bool observerInDuelMode; ObjectInstanceID destinationTeleport; //contain -1 or object id if teleportation + int3 destinationTeleportPos; //minor interfaces CondSh *showingDialog; //indicates if dialog box is displayed @@ -168,7 +169,7 @@ public: void showRecruitmentDialog(const CGDwelling *dwelling, const CArmedInstance *dst, int level) override; void showShipyardDialog(const IShipyard *obj) override; //obj may be town or shipyard; void showBlockingDialog(const std::string &text, const std::vector &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 exits, bool impassable, QueryID askID) override; + void showTeleportDialog(TeleportChannelID channel, TTeleportExitsList exits, bool impassable, QueryID askID) override; void showGarrisonDialog(const CArmedInstance *up, const CGHeroInstance *down, bool removableUnits, QueryID queryID) override; void showPuzzleMap() override; void viewWorldMap() override; diff --git a/lib/Connection.h b/lib/Connection.h index ee07bf9aa..e56d71640 100644 --- a/lib/Connection.h +++ b/lib/Connection.h @@ -27,7 +27,7 @@ #include "mapping/CCampaignHandler.h" //for CCampaignState #include "rmg/CMapGenerator.h" // for CMapGenOptions -const ui32 version = 754; +const ui32 version = 755; const ui32 minSupportedVersion = 753; class CISer; diff --git a/lib/mapObjects/MiscObjects.cpp b/lib/mapObjects/MiscObjects.cpp index 4f2566960..8c0db729d 100644 --- a/lib/mapObjects/MiscObjects.cpp +++ b/lib/mapObjects/MiscObjects.cpp @@ -834,7 +834,7 @@ bool CGTeleport::isTeleport(const CGObjectInstance * obj) bool CGTeleport::isConnected(const CGTeleport * src, const CGTeleport * dst) { - return src && dst && src != dst && src->isChannelExit(dst->id); + return src && dst && src->isChannelExit(dst->id); } bool CGTeleport::isConnected(const CGObjectInstance * src, const CGObjectInstance * dst) @@ -913,7 +913,13 @@ void CGMonolith::onHeroVisit( const CGHeroInstance * h ) const if(isEntrance()) { if(cb->isTeleportChannelBidirectional(channel) && 1 < cb->getTeleportChannelExits(channel).size()) - td.exits = cb->getTeleportChannelExits(channel); + { + auto exits = cb->getTeleportChannelExits(channel); + for(auto exit : exits) + { + td.exits.push_back(std::make_pair(exit, CGHeroInstance::convertPosition(cb->getObj(exit)->visitablePos(), true))); + } + } if(cb->isTeleportChannelImpassable(channel)) { @@ -929,9 +935,9 @@ void CGMonolith::onHeroVisit( const CGHeroInstance * h ) const cb->showTeleportDialog(&td); } -void CGMonolith::teleportDialogAnswered(const CGHeroInstance *hero, ui32 answer, std::vector exits) const +void CGMonolith::teleportDialogAnswered(const CGHeroInstance *hero, ui32 answer, TTeleportExitsList exits) const { - ObjectInstanceID objId(answer); + int3 dPos; 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 @@ -939,14 +945,12 @@ void CGMonolith::teleportDialogAnswered(const CGHeroInstance *hero, ui32 answer, { return; } - else if(objId == ObjectInstanceID()) - objId = getRandomExit(hero); + else if(vstd::isValidIndex(exits, answer)) + dPos = exits[answer].second; else - assert(vstd::contains(exits, objId)); // Likely cheating attempt: not random teleporter choosen, but it's not from provided list + dPos = CGHeroInstance::convertPosition(cb->getObj(getRandomExit(hero))->visitablePos(), true); - auto obj = cb->getObj(objId); - if(obj) - cb->moveHero(hero->id,CGHeroInstance::convertPosition(obj->pos,true) - getVisitableOffset(), true); + cb->moveHero(hero->id, dPos, true); } void CGMonolith::initObj() @@ -986,7 +990,10 @@ void CGSubterraneanGate::onHeroVisit( const CGHeroInstance * h ) const td.impassable = true; } else - td.exits.push_back(getRandomExit(h)); + { + auto exit = getRandomExit(h); + td.exits.push_back(std::make_pair(exit, CGHeroInstance::convertPosition(cb->getObj(exit)->visitablePos(), true))); + } cb->showTeleportDialog(&td); } @@ -1085,29 +1092,35 @@ void CGWhirlpool::onHeroVisit( const CGHeroInstance * h ) const cb->changeStackCount(StackLocation(h, targetstack), -countToTake); } else - td.exits = getAllExits(true); + { + auto exits = getAllExits(); + for(auto exit : exits) + { + auto blockedPosList = cb->getObj(exit)->getBlockedPos(); + for(auto bPos : blockedPosList) + td.exits.push_back(std::make_pair(exit, CGHeroInstance::convertPosition(bPos, true))); + } + } cb->showTeleportDialog(&td); } -void CGWhirlpool::teleportDialogAnswered(const CGHeroInstance *hero, ui32 answer, std::vector exits) const +void CGWhirlpool::teleportDialogAnswered(const CGHeroInstance *hero, ui32 answer, TTeleportExitsList exits) const { - ObjectInstanceID objId(answer); + int3 dPos; auto realExits = getAllExits(); if(!exits.size() && !realExits.size()) return; - else if(objId == ObjectInstanceID()) - objId = getRandomExit(hero); + else if(vstd::isValidIndex(exits, answer)) + dPos = exits[answer].second; 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) { + auto obj = cb->getObj(getRandomExit(hero)); std::set tiles = obj->getBlockedPos(); - auto & tile = *RandomGeneratorUtil::nextItem(tiles, cb->gameState()->getRandomGenerator()); - cb->moveHero(hero->id, CGHeroInstance::convertPosition(tile, true), true); + dPos = CGHeroInstance::convertPosition(*RandomGeneratorUtil::nextItem(tiles, cb->gameState()->getRandomGenerator()), true); } + + cb->moveHero(hero->id, dPos, true); } bool CGWhirlpool::isProtected( const CGHeroInstance * h ) diff --git a/lib/mapObjects/MiscObjects.h b/lib/mapObjects/MiscObjects.h index 402deb1c4..161fe38e7 100644 --- a/lib/mapObjects/MiscObjects.h +++ b/lib/mapObjects/MiscObjects.h @@ -247,7 +247,7 @@ public: ui32 defaultResProduction(); }; -typedef std::vector TTeleportExitsList; +typedef std::vector> TTeleportExitsList; struct DLL_LINKAGE TeleportChannel { @@ -282,7 +282,7 @@ public: std::vector getAllExits(bool excludeCurrent = false) const; ObjectInstanceID getRandomExit(const CGHeroInstance * h) const; - virtual void teleportDialogAnswered(const CGHeroInstance *hero, ui32 answer, std::vector exits) const = 0; + virtual void teleportDialogAnswered(const CGHeroInstance *hero, ui32 answer, TTeleportExitsList exits) const = 0; static bool isTeleport(const CGObjectInstance * dst); static bool isConnected(const CGTeleport * src, const CGTeleport * dst); @@ -303,7 +303,7 @@ class DLL_LINKAGE CGMonolith : public CGTeleport public: void onHeroVisit(const CGHeroInstance * h) const override; - void teleportDialogAnswered(const CGHeroInstance *hero, ui32 answer, std::vector exits) const override; + void teleportDialogAnswered(const CGHeroInstance *hero, ui32 answer, TTeleportExitsList exits) const override; void initObj() override; template void serialize(Handler &h, const int version) @@ -329,7 +329,7 @@ class DLL_LINKAGE CGWhirlpool : public CGMonolith { public: void onHeroVisit(const CGHeroInstance * h) const override; - void teleportDialogAnswered(const CGHeroInstance *hero, ui32 answer, std::vector exits) const override; + void teleportDialogAnswered(const CGHeroInstance *hero, ui32 answer, TTeleportExitsList exits) const override; static bool isProtected( const CGHeroInstance * h ); template void serialize(Handler &h, const int version)