From f6de3f94ca6a9a593fc11d4b6957133a85012a5f Mon Sep 17 00:00:00 2001 From: ArseniyShestakov Date: Sat, 28 Nov 2015 02:41:30 +0300 Subject: [PATCH 1/9] Teleports: use TTeleportExitsList typedef for exits list There is several ideas that teleportation code have to be shared between object/spells and this way we can avoid changing showTeleportDialog declaration every time. --- AI/EmptyAI/CEmptyAI.cpp | 2 +- AI/EmptyAI/CEmptyAI.h | 2 +- AI/VCAI/VCAI.cpp | 2 +- AI/VCAI/VCAI.h | 2 +- lib/CGameInterface.h | 4 +++- lib/NetPacks.h | 2 +- lib/mapObjects/MiscObjects.h | 2 ++ 7 files changed, 10 insertions(+), 6 deletions(-) diff --git a/AI/EmptyAI/CEmptyAI.cpp b/AI/EmptyAI/CEmptyAI.cpp index f46bc1e6e..58d8fa946 100644 --- a/AI/EmptyAI/CEmptyAI.cpp +++ b/AI/EmptyAI/CEmptyAI.cpp @@ -30,7 +30,7 @@ void CEmptyAI::showBlockingDialog(const std::string &text, const std::vectorselectionMade(0, askID); } -void CEmptyAI::showTeleportDialog(TeleportChannelID channel, std::vector exits, bool impassable, QueryID askID) +void CEmptyAI::showTeleportDialog(TeleportChannelID channel, TTeleportExitsList exits, bool impassable, QueryID askID) { cb->selectionMade(0, askID); } diff --git a/AI/EmptyAI/CEmptyAI.h b/AI/EmptyAI/CEmptyAI.h index eb5e43a32..262511a5d 100644 --- a/AI/EmptyAI/CEmptyAI.h +++ b/AI/EmptyAI/CEmptyAI.h @@ -15,7 +15,7 @@ public: void heroGotLevel(const CGHeroInstance *hero, PrimarySkill::PrimarySkill pskill, std::vector &skills, QueryID queryID) override; void commanderGotLevel (const CCommanderInstance * commander, std::vector skills, QueryID queryID) override; void showBlockingDialog(const std::string &text, const std::vector &components, QueryID askID, const int soundID, bool selection, bool cancel) override; - 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; }; diff --git a/AI/VCAI/VCAI.cpp b/AI/VCAI/VCAI.cpp index a93695e94..18a1801ec 100644 --- a/AI/VCAI/VCAI.cpp +++ b/AI/VCAI/VCAI.cpp @@ -614,7 +614,7 @@ void VCAI::showBlockingDialog(const std::string &text, const std::vector exits, bool impassable, QueryID askID) +void VCAI::showTeleportDialog(TeleportChannelID channel, TTeleportExitsList exits, bool impassable, QueryID askID) { LOG_TRACE_PARAMS(logAi, "askID '%i', exits '%s'", askID % exits); NET_EVENT_HANDLER; diff --git a/AI/VCAI/VCAI.h b/AI/VCAI/VCAI.h index 23967df64..15fd7c7d4 100644 --- a/AI/VCAI/VCAI.h +++ b/AI/VCAI/VCAI.h @@ -202,7 +202,7 @@ public: virtual void commanderGotLevel (const CCommanderInstance * commander, std::vector skills, QueryID queryID) override; //TODO virtual void showBlockingDialog(const std::string &text, const std::vector &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 showTeleportDialog(TeleportChannelID channel, std::vector exits, bool impassable, QueryID askID) override; + virtual void showTeleportDialog(TeleportChannelID channel, TTeleportExitsList exits, bool impassable, QueryID askID) override; virtual void saveGame(COSer & h, const int version) override; //saving virtual void loadGame(CISer & h, const int version) override; //loading virtual void finish() override; diff --git a/lib/CGameInterface.h b/lib/CGameInterface.h index c35d52985..b120d95d7 100644 --- a/lib/CGameInterface.h +++ b/lib/CGameInterface.h @@ -7,6 +7,8 @@ #include "spells/ViewSpellInt.h" +#include "mapObjects/MiscObjects.h" + /* * CGameInterface.h, part of VCMI engine * @@ -94,7 +96,7 @@ public: // 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 showTeleportDialog(TeleportChannelID channel, std::vector exits, bool impassable, QueryID askID) = 0; + virtual void showTeleportDialog(TeleportChannelID channel, TTeleportExitsList exits, bool impassable, QueryID askID) = 0; virtual void finish(){}; //if for some reason we want to end virtual void showWorldViewEx(const std::vector & objectPositions){}; diff --git a/lib/NetPacks.h b/lib/NetPacks.h index cdbe3ebe9..5417438f1 100644 --- a/lib/NetPacks.h +++ b/lib/NetPacks.h @@ -1221,7 +1221,7 @@ struct TeleportDialog : public Query//2006 const CGHeroInstance *hero; TeleportChannelID channel; - std::vector exits; + TTeleportExitsList exits; bool impassable; template void serialize(Handler &h, const int version) diff --git a/lib/mapObjects/MiscObjects.h b/lib/mapObjects/MiscObjects.h index 21990b688..402deb1c4 100644 --- a/lib/mapObjects/MiscObjects.h +++ b/lib/mapObjects/MiscObjects.h @@ -247,6 +247,8 @@ public: ui32 defaultResProduction(); }; +typedef std::vector TTeleportExitsList; + struct DLL_LINKAGE TeleportChannel { enum EPassability {UNKNOWN, IMPASSABLE, PASSABLE}; From b5100bee94e28d33dcf9d14835ad895b40f1d57b Mon Sep 17 00:00:00 2001 From: ArseniyShestakov Date: Wed, 2 Dec 2015 17:56:26 +0300 Subject: [PATCH 2/9] 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) From eb9f29e3684bc311e11ae49f6b5d8d607d50b609 Mon Sep 17 00:00:00 2001 From: ArseniyShestakov Date: Wed, 2 Dec 2015 19:26:24 +0300 Subject: [PATCH 3/9] VCAI: restoring teleport probing feature for updated mechanics --- AI/VCAI/VCAI.cpp | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/AI/VCAI/VCAI.cpp b/AI/VCAI/VCAI.cpp index e84694703..80c2f7f10 100644 --- a/AI/VCAI/VCAI.cpp +++ b/AI/VCAI/VCAI.cpp @@ -631,13 +631,18 @@ void VCAI::showTeleportDialog(TeleportChannelID channel, TTeleportExitsList exit 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 + for(auto exit : exits) { - return !(vstd::contains(visitableObjs, cb->getObj(id)) || id == choosenExit); - }); - }*/ + if(!vstd::contains(visitableObjs, cb->getObj(exit.first)) && + !vstd::contains(teleportChannelProbingList, exit.first) && + exit.first != destinationTeleport) + { + teleportChannelProbingList.push_back(exit.first); + } + } + } } requestActionASAP([=]() From 2f9ca778b27f7790bc25cbfd81eedbc836d417f0 Mon Sep 17 00:00:00 2001 From: ArseniyShestakov Date: Thu, 3 Dec 2015 17:20:03 +0300 Subject: [PATCH 4/9] VCAI: add channel probing support for teleporters with multiple exits --- AI/VCAI/VCAI.cpp | 38 +++++++++++++++++++++++--------------- server/CGameHandler.cpp | 2 +- 2 files changed, 24 insertions(+), 16 deletions(-) diff --git a/AI/VCAI/VCAI.cpp b/AI/VCAI/VCAI.cpp index 80c2f7f10..ee87c0f5d 100644 --- a/AI/VCAI/VCAI.cpp +++ b/AI/VCAI/VCAI.cpp @@ -625,22 +625,29 @@ void VCAI::showTeleportDialog(TeleportChannelID channel, TTeleportExitsList exit int choosenExit = -1; if(impassable) knownTeleportChannels[channel]->passability = TeleportChannel::IMPASSABLE; - else + else if(destinationTeleport != ObjectInstanceID() && destinationTeleportPos.valid()) { auto neededExit = std::make_pair(destinationTeleport, destinationTeleportPos); if(destinationTeleport != ObjectInstanceID() && vstd::contains(exits, neededExit)) choosenExit = vstd::find_pos(exits, neededExit); + } - if(!status.channelProbing()) + for(auto exit : exits) + { + if(status.channelProbing() && exit.first == destinationTeleport) { - for(auto exit : exits) + choosenExit = vstd::find_pos(exits, exit); + break; + } + else + { + // FIXME: This code generate "Object is not visible." errors + // What is better way to check that certain teleport exit wasn't visited yet or not visible? + if(!vstd::contains(visitableObjs, cb->getObj(exit.first)) && + !vstd::contains(teleportChannelProbingList, exit.first) && + exit.first != destinationTeleport) { - if(!vstd::contains(visitableObjs, cb->getObj(exit.first)) && - !vstd::contains(teleportChannelProbingList, exit.first) && - exit.first != destinationTeleport) - { - teleportChannelProbingList.push_back(exit.first); - } + teleportChannelProbingList.push_back(exit.first); } } } @@ -1867,11 +1874,11 @@ bool VCAI::moveHeroToTile(int3 dst, HeroPtr h) cb->moveHero(*h, CGHeroInstance::convertPosition(dst, true), transit); }; - auto doTeleportMovement = [&](int3 dst, ObjectInstanceID exitId) + auto doTeleportMovement = [&](ObjectInstanceID exitId, int3 exitPos) { destinationTeleport = exitId; - destinationTeleportPos = CGHeroInstance::convertPosition(dst, true); - cb->moveHero(*h, destinationTeleportPos); + destinationTeleportPos = CGHeroInstance::convertPosition(exitPos, true); + cb->moveHero(*h, h->pos); destinationTeleport = ObjectInstanceID(); destinationTeleportPos = int3(); afterMovementCheck(); @@ -1880,13 +1887,14 @@ bool VCAI::moveHeroToTile(int3 dst, HeroPtr h) auto doChannelProbing = [&]() -> void { auto currentExit = getObj(CGHeroInstance::convertPosition(h->pos,false), false); + auto currentExitPos = CGHeroInstance::convertPosition(h->pos,false); assert(currentExit); status.setChannelProbing(true); for(auto exit : teleportChannelProbingList) - doTeleportMovement(CGHeroInstance::convertPosition(h->pos,false), exit); + doTeleportMovement(exit, int3()); teleportChannelProbingList.clear(); - doTeleportMovement(CGHeroInstance::convertPosition(h->pos,false), currentExit->id); + doTeleportMovement(currentExit->id, currentExitPos); status.setChannelProbing(false); }; @@ -1900,7 +1908,7 @@ bool VCAI::moveHeroToTile(int3 dst, HeroPtr h) auto nextObject = getObj(nextCoord, false); if(CGTeleport::isConnected(currentObject, nextObject)) { //we use special login if hero standing on teleporter it's mean we need - doTeleportMovement(currentCoord, nextObject->id); + doTeleportMovement(nextObject->id, nextCoord); if(teleportChannelProbingList.size()) doChannelProbing(); diff --git a/server/CGameHandler.cpp b/server/CGameHandler.cpp index 7be5428e6..645a90b25 100644 --- a/server/CGameHandler.cpp +++ b/server/CGameHandler.cpp @@ -1755,7 +1755,7 @@ bool CGameHandler::moveHero( ObjectInstanceID hid, int3 dst, ui8 teleporting, bo } logGlobal->traceStream() << "Player " << asker << " wants to move hero "<< hid.getNum() << " from "<< h->pos << " to " << dst; - const int3 hmpos = dst + int3(-1,0,0); + const int3 hmpos = CGHeroInstance::convertPosition(dst, false); if(!gs->map->isInTheMap(hmpos)) { From ee0874974328c8d45c116c6bfc91937788aceda8 Mon Sep 17 00:00:00 2001 From: ArseniyShestakov Date: Thu, 3 Dec 2015 21:18:40 +0300 Subject: [PATCH 5/9] VCAI: more work on teleport exit probing --- AI/VCAI/VCAI.cpp | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/AI/VCAI/VCAI.cpp b/AI/VCAI/VCAI.cpp index ee87c0f5d..cf99d0697 100644 --- a/AI/VCAI/VCAI.cpp +++ b/AI/VCAI/VCAI.cpp @@ -641,10 +641,10 @@ void VCAI::showTeleportDialog(TeleportChannelID channel, TTeleportExitsList exit } else { - // FIXME: This code generate "Object is not visible." errors - // What is better way to check that certain teleport exit wasn't visited yet or not visible? - if(!vstd::contains(visitableObjs, cb->getObj(exit.first)) && - !vstd::contains(teleportChannelProbingList, exit.first) && + // TODO: Implement checking if visiting that teleport will uncovert any FoW + // So far this is the best option to handle decision about probing + auto obj = cb->getObj(exit.first, false); + if(obj == nullptr && !vstd::contains(teleportChannelProbingList, exit.first) && exit.first != destinationTeleport) { teleportChannelProbingList.push_back(exit.first); @@ -1877,7 +1877,8 @@ bool VCAI::moveHeroToTile(int3 dst, HeroPtr h) auto doTeleportMovement = [&](ObjectInstanceID exitId, int3 exitPos) { destinationTeleport = exitId; - destinationTeleportPos = CGHeroInstance::convertPosition(exitPos, true); + if(exitPos.valid()) + destinationTeleportPos = CGHeroInstance::convertPosition(exitPos, true); cb->moveHero(*h, h->pos); destinationTeleport = ObjectInstanceID(); destinationTeleportPos = int3(); From 3800bd45b7f03396412ccfdff88d677efbdc8270 Mon Sep 17 00:00:00 2001 From: ArseniyShestakov Date: Fri, 4 Dec 2015 01:54:25 +0300 Subject: [PATCH 6/9] Movement: initialize destinationTeleportPos with invalid int3 position --- AI/VCAI/VCAI.cpp | 8 ++++---- AI/VCAI/VCAI.h | 2 +- client/CPlayerInterface.cpp | 4 ++-- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/AI/VCAI/VCAI.cpp b/AI/VCAI/VCAI.cpp index cf99d0697..bd0b195b4 100644 --- a/AI/VCAI/VCAI.cpp +++ b/AI/VCAI/VCAI.cpp @@ -96,7 +96,7 @@ VCAI::VCAI(void) LOG_TRACE(logAi); makingTurn = nullptr; destinationTeleport = ObjectInstanceID(); - destinationTeleportPos = int3(); + destinationTeleportPos = int3(-1); } VCAI::~VCAI(void) @@ -1881,7 +1881,7 @@ bool VCAI::moveHeroToTile(int3 dst, HeroPtr h) destinationTeleportPos = CGHeroInstance::convertPosition(exitPos, true); cb->moveHero(*h, h->pos); destinationTeleport = ObjectInstanceID(); - destinationTeleportPos = int3(); + destinationTeleportPos = int3(-1); afterMovementCheck(); }; @@ -1893,7 +1893,7 @@ bool VCAI::moveHeroToTile(int3 dst, HeroPtr h) status.setChannelProbing(true); for(auto exit : teleportChannelProbingList) - doTeleportMovement(exit, int3()); + doTeleportMovement(exit, int3(-1)); teleportChannelProbingList.clear(); doTeleportMovement(currentExit->id, currentExitPos); status.setChannelProbing(false); @@ -2931,7 +2931,7 @@ void AIStatus::setMove(bool ongoing) void AIStatus::setChannelProbing(bool ongoing) { boost::unique_lock lock(mx); - ongoingHeroMovement = ongoing; + ongoingChannelProbing = ongoing; cv.notify_all(); } diff --git a/AI/VCAI/VCAI.h b/AI/VCAI/VCAI.h index aa5eb521b..25eb312cd 100644 --- a/AI/VCAI/VCAI.h +++ b/AI/VCAI/VCAI.h @@ -367,7 +367,7 @@ public: } else if(!h.saving) { - destinationTeleportPos = int3(); + destinationTeleportPos = int3(-1); } h & townVisitsThisWeek & lockedHeroes & reservedHeroesMap; //FIXME: cannot instantiate abstract class h & visitableObjs & alreadyVisited & reservedObjs; diff --git a/client/CPlayerInterface.cpp b/client/CPlayerInterface.cpp index 6132d8035..089d94b88 100644 --- a/client/CPlayerInterface.cpp +++ b/client/CPlayerInterface.cpp @@ -97,7 +97,7 @@ CPlayerInterface::CPlayerInterface(PlayerColor Player) { logGlobal->traceStream() << "\tHuman player interface for player " << Player << " being constructed"; destinationTeleport = ObjectInstanceID(); - destinationTeleportPos = int3(); + destinationTeleportPos = int3(-1); observerInDuelMode = false; howManyPeople++; GH.defActionsDef = 0; @@ -1416,7 +1416,7 @@ void CPlayerInterface::requestRealized( PackageApplied *pa ) && stillMoveHero.get() == DURING_MOVE) { // After teleportation via CGTeleport object is finished destinationTeleport = ObjectInstanceID(); - destinationTeleportPos = int3(); + destinationTeleportPos = int3(-1); stillMoveHero.setn(CONTINUE_MOVE); } } From 791d1e7ab479b0edb5ed81acbfb6b2e53521a41d Mon Sep 17 00:00:00 2001 From: ArseniyShestakov Date: Fri, 4 Dec 2015 05:30:43 +0300 Subject: [PATCH 7/9] VCAI: finish fixing of teleport probing for whirlpools --- AI/VCAI/VCAI.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/AI/VCAI/VCAI.cpp b/AI/VCAI/VCAI.cpp index bd0b195b4..e94b5e49a 100644 --- a/AI/VCAI/VCAI.cpp +++ b/AI/VCAI/VCAI.cpp @@ -1887,16 +1887,16 @@ bool VCAI::moveHeroToTile(int3 dst, HeroPtr h) auto doChannelProbing = [&]() -> void { - auto currentExit = getObj(CGHeroInstance::convertPosition(h->pos,false), false); - auto currentExitPos = CGHeroInstance::convertPosition(h->pos,false); - assert(currentExit); + auto currentPos = CGHeroInstance::convertPosition(h->pos,false); + auto currentExit = getObj(currentPos, true)->id; status.setChannelProbing(true); for(auto exit : teleportChannelProbingList) doTeleportMovement(exit, int3(-1)); teleportChannelProbingList.clear(); - doTeleportMovement(currentExit->id, currentExitPos); status.setChannelProbing(false); + + doTeleportMovement(currentExit, currentPos); }; int i=path.nodes.size()-1; From eced16945e41c9dd4ebd0be4ea1f323e5f96ea0a Mon Sep 17 00:00:00 2001 From: ArseniyShestakov Date: Fri, 4 Dec 2015 05:43:55 +0300 Subject: [PATCH 8/9] getVisibleTeleportObjects: use getObj as not verbose We checking visibility in this function so there no need to send information about that into errorStream. --- lib/CGameInfoCallback.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/CGameInfoCallback.cpp b/lib/CGameInfoCallback.cpp index 56401514b..9d61db81f 100644 --- a/lib/CGameInfoCallback.cpp +++ b/lib/CGameInfoCallback.cpp @@ -853,7 +853,7 @@ std::vector CGameInfoCallback::getVisibleTeleportObjects(std:: { vstd::erase_if(ids, [&](ObjectInstanceID id) -> bool { - auto obj = getObj(id); + auto obj = getObj(id, false); return player != PlayerColor::UNFLAGGABLE && (!obj || !isVisible(obj->pos, player)); }); return ids; From 01c5bc25e519209991fb3b49967d5a1af88848d5 Mon Sep 17 00:00:00 2001 From: ArseniyShestakov Date: Sat, 5 Dec 2015 01:14:03 +0300 Subject: [PATCH 9/9] Move TTeleportExitsList to CObjectHandler.h Not a perfect solution, but this is the best location for now. --- lib/CGameInterface.h | 2 +- lib/mapObjects/CObjectHandler.h | 4 ++++ lib/mapObjects/MiscObjects.h | 2 -- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/lib/CGameInterface.h b/lib/CGameInterface.h index b120d95d7..a49b5894f 100644 --- a/lib/CGameInterface.h +++ b/lib/CGameInterface.h @@ -7,7 +7,7 @@ #include "spells/ViewSpellInt.h" -#include "mapObjects/MiscObjects.h" +#include "mapObjects/CObjectHandler.h" /* * CGameInterface.h, part of VCMI engine diff --git a/lib/mapObjects/CObjectHandler.h b/lib/mapObjects/CObjectHandler.h index adc71b3d1..b159482c8 100644 --- a/lib/mapObjects/CObjectHandler.h +++ b/lib/mapObjects/CObjectHandler.h @@ -22,6 +22,10 @@ class CGObjectInstance; struct MetaString; struct BattleResult; +// This one teleport-specific, but has to be available everywhere in callbacks and netpacks +// For now it's will be there till teleports code refactored and moved into own file +typedef std::vector> TTeleportExitsList; + class DLL_LINKAGE IObjectInterface { public: diff --git a/lib/mapObjects/MiscObjects.h b/lib/mapObjects/MiscObjects.h index 161fe38e7..6ae8650de 100644 --- a/lib/mapObjects/MiscObjects.h +++ b/lib/mapObjects/MiscObjects.h @@ -247,8 +247,6 @@ public: ui32 defaultResProduction(); }; -typedef std::vector> TTeleportExitsList; - struct DLL_LINKAGE TeleportChannel { enum EPassability {UNKNOWN, IMPASSABLE, PASSABLE};