From 3d1a84875ed9479e905cb1e12ffb28bd88e716d9 Mon Sep 17 00:00:00 2001 From: AlexVinS Date: Tue, 6 Jun 2017 07:53:51 +0300 Subject: [PATCH] Queries refactoring * Moved SUMMON_BOAT special case to mechanics * Partially moved Town portal logic to mechanics class * Added generic query reply to CCallback * Redesigned Queries so that base API do not depends on CGameHandler * Got rid of CGameHandler::castSpellRequest * Removed CGameHandler::castSpell * Added new Query type for town portal dialog (not used yet) --- AI/EmptyAI/CEmptyAI.cpp | 5 + AI/EmptyAI/CEmptyAI.h | 1 + AI/VCAI/VCAI.cpp | 8 + AI/VCAI/VCAI.h | 1 + CCallback.cpp | 11 +- CCallback.h | 2 + client/CPlayerInterface.cpp | 33 ++++ client/CPlayerInterface.h | 1 + client/NetPacksClient.cpp | 5 + client/windows/CSpellWindow.cpp | 37 +--- client/windows/GUIClasses.cpp | 10 +- client/windows/GUIClasses.h | 4 + lib/CGameInterface.h | 1 + lib/CGameState.cpp | 3 + lib/NetPacks.h | 33 ++-- lib/NetPacksBase.h | 4 +- lib/registerTypes/RegisterTypes.h | 1 + lib/spells/AdventureSpellMechanics.cpp | 228 ++++++++++++++++++------- lib/spells/AdventureSpellMechanics.h | 35 ++-- lib/spells/BattleSpellMechanics.cpp | 106 +++++++++++- lib/spells/BattleSpellMechanics.h | 50 +++--- lib/spells/CDefaultSpellMechanics.cpp | 11 ++ lib/spells/CDefaultSpellMechanics.h | 4 +- lib/spells/CreatureSpellMechanics.cpp | 15 ++ lib/spells/CreatureSpellMechanics.h | 12 +- lib/spells/ISpellMechanics.cpp | 10 +- lib/spells/ISpellMechanics.h | 14 +- server/CGameHandler.cpp | 39 ++--- server/CGameHandler.h | 12 +- server/CQuery.cpp | 186 +++++++++++++++----- server/CQuery.h | 124 +++++++++----- server/NetPacksServer.cpp | 18 +- 32 files changed, 729 insertions(+), 295 deletions(-) diff --git a/AI/EmptyAI/CEmptyAI.cpp b/AI/EmptyAI/CEmptyAI.cpp index bd4756f03..98da0f434 100644 --- a/AI/EmptyAI/CEmptyAI.cpp +++ b/AI/EmptyAI/CEmptyAI.cpp @@ -38,3 +38,8 @@ void CEmptyAI::showGarrisonDialog(const CArmedInstance *up, const CGHeroInstance { cb->selectionMade(0, queryID); } + +void CEmptyAI::showMapObjectSelectDialog(QueryID askID, const Component & icon, const MetaString & title, const MetaString & description, const std::vector & objects) +{ + cb->selectionMade(0, askID); +} diff --git a/AI/EmptyAI/CEmptyAI.h b/AI/EmptyAI/CEmptyAI.h index 83ad577a7..a7abccdea 100644 --- a/AI/EmptyAI/CEmptyAI.h +++ b/AI/EmptyAI/CEmptyAI.h @@ -17,6 +17,7 @@ public: void showBlockingDialog(const std::string &text, const std::vector &components, QueryID askID, const int soundID, bool selection, bool cancel) 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 showMapObjectSelectDialog(QueryID askID, const Component & icon, const MetaString & title, const MetaString & description, const std::vector & objects) override; }; #define NAME "EmptyAI 0.1" diff --git a/AI/VCAI/VCAI.cpp b/AI/VCAI/VCAI.cpp index c185e70bd..72e51ad39 100644 --- a/AI/VCAI/VCAI.cpp +++ b/AI/VCAI/VCAI.cpp @@ -701,6 +701,14 @@ void VCAI::showGarrisonDialog(const CArmedInstance *up, const CGHeroInstance *do }); } +void VCAI::showMapObjectSelectDialog(QueryID askID, const Component & icon, const MetaString & title, const MetaString & description, const std::vector & objects) +{ + status.addQuery(askID, "Map object select query"); + requestActionASAP([=]{ answerQuery(askID, 0); }); + + //TODO: Town portal destination selection goes here +} + void VCAI::saveGame(BinarySerializer & h, const int version) { LOG_TRACE_PARAMS(logAi, "version '%i'", version); diff --git a/AI/VCAI/VCAI.h b/AI/VCAI/VCAI.h index b321d7571..bf7f791ac 100644 --- a/AI/VCAI/VCAI.h +++ b/AI/VCAI/VCAI.h @@ -194,6 +194,7 @@ public: 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, TTeleportExitsList exits, bool impassable, QueryID askID) override; + void showMapObjectSelectDialog(QueryID askID, const Component & icon, const MetaString & title, const MetaString & description, const std::vector & objects) override; virtual void saveGame(BinarySerializer & h, const int version) override; //saving virtual void loadGame(BinaryDeserializer & h, const int version) override; //loading virtual void finish() override; diff --git a/CCallback.cpp b/CCallback.cpp index 23d76be44..d69bccbb6 100644 --- a/CCallback.cpp +++ b/CCallback.cpp @@ -45,15 +45,22 @@ bool CCallback::moveHero(const CGHeroInstance *h, int3 dst, bool transit) } int CCallback::selectionMade(int selection, QueryID queryID) +{ + JsonNode reply(JsonNode::DATA_INTEGER); + reply.Integer() = selection; + return sendQueryReply(reply, queryID); +} + +int CCallback::sendQueryReply(const JsonNode & reply, QueryID queryID) { ASSERT_IF_CALLED_WITH_PLAYER if(queryID == QueryID(-1)) { logGlobal->errorStream() << "Cannot answer the query -1!"; - return false; + return -1; } - QueryReply pack(queryID,selection); + QueryReply pack(queryID,reply); pack.player = *player; return sendRequest(&pack); } diff --git a/CCallback.h b/CCallback.h index 473055ebc..aab818c71 100644 --- a/CCallback.h +++ b/CCallback.h @@ -61,6 +61,7 @@ public: virtual void trade(const CGObjectInstance *market, EMarketMode::EMarketMode mode, int id1, int id2, int val1, const CGHeroInstance *hero = nullptr)=0; //mode==0: sell val1 units of id1 resource for id2 resiurce virtual int selectionMade(int selection, QueryID queryID) =0; + virtual int sendQueryReply(const JsonNode & reply, QueryID queryID) =0; virtual int swapCreatures(const CArmedInstance *s1, const CArmedInstance *s2, SlotID p1, SlotID p2)=0;//swaps creatures between two possibly different garrisons // TODO: AI-unsafe code - fix it! virtual int mergeStacks(const CArmedInstance *s1, const CArmedInstance *s2, SlotID p1, SlotID p2)=0;//joins first stack to the second (creatures must be same type) virtual int mergeOrSwapStacks(const CArmedInstance *s1, const CArmedInstance *s2, SlotID p1, SlotID p2) =0; //first goes to the second @@ -121,6 +122,7 @@ public: bool moveHero(const CGHeroInstance *h, int3 dst, bool transit = false) override; //dst must be free, neighbouring tile (this function can move hero only by one tile) bool teleportHero(const CGHeroInstance *who, const CGTownInstance *where); int selectionMade(int selection, QueryID queryID) override; + int sendQueryReply(const JsonNode & reply, QueryID queryID) override; int swapCreatures(const CArmedInstance *s1, const CArmedInstance *s2, SlotID p1, SlotID p2) override; int mergeOrSwapStacks(const CArmedInstance *s1, const CArmedInstance *s2, SlotID p1, SlotID p2) override; //first goes to the second int mergeStacks(const CArmedInstance *s1, const CArmedInstance *s2, SlotID p1, SlotID p2) override; //first goes to the second diff --git a/client/CPlayerInterface.cpp b/client/CPlayerInterface.cpp index 57f1eed43..66fadefff 100644 --- a/client/CPlayerInterface.cpp +++ b/client/CPlayerInterface.cpp @@ -1191,6 +1191,39 @@ void CPlayerInterface::showTeleportDialog(TeleportChannelID channel, TTeleportEx cb->selectionMade(choosenExit, askID); } +void CPlayerInterface::showMapObjectSelectDialog(QueryID askID, const Component & icon, const MetaString & title, const MetaString & description, const std::vector & objects) +{ + EVENT_HANDLER_CALLED_BY_CLIENT; + + auto selectCallback = [=](int selection) + { + JsonNode reply(JsonNode::DATA_INTEGER); + reply.Integer() = selection; + cb->sendQueryReply(reply, askID); + }; + + auto cancelCallback = [=]() + { + JsonNode reply(JsonNode::DATA_NULL); + cb->sendQueryReply(reply, askID); + }; + + CComponent * localIcon = new CComponent(icon); + + const std::string localTitle = title.toString(); + const std::string localDescription = description.toString(); + + std::vector tempList; + tempList.reserve(objects.size()); + + for(auto item : objects) + tempList.push_back(item.getNum()); + + CObjectListWindow * wnd = new CObjectListWindow(tempList, localIcon, localTitle, localDescription, selectCallback); + wnd->onExit = cancelCallback; + GH.pushInt(wnd); +} + void CPlayerInterface::tileRevealed(const std::unordered_set &pos) { EVENT_HANDLER_CALLED_BY_CLIENT; diff --git a/client/CPlayerInterface.h b/client/CPlayerInterface.h index 6e1c4bd93..4609d8387 100644 --- a/client/CPlayerInterface.h +++ b/client/CPlayerInterface.h @@ -166,6 +166,7 @@ public: 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. 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 showMapObjectSelectDialog(QueryID askID, const Component & icon, const MetaString & title, const MetaString & description, const std::vector & objects) override; void showPuzzleMap() override; void viewWorldMap() override; void showMarketWindow(const IMarket *market, const CGHeroInstance *visitor) override; diff --git a/client/NetPacksClient.cpp b/client/NetPacksClient.cpp index 6556f2d45..e6a76a6a1 100644 --- a/client/NetPacksClient.cpp +++ b/client/NetPacksClient.cpp @@ -592,6 +592,11 @@ void TeleportDialog::applyCl(CClient *cl) CALL_ONLY_THAT_INTERFACE(hero->tempOwner,showTeleportDialog,channel,exits,impassable,queryID); } +void MapObjectSelectDialog::applyCl(CClient * cl) +{ + CALL_ONLY_THAT_INTERFACE(player, showMapObjectSelectDialog, queryID, icon, title, description, objects); +} + void BattleStart::applyFirstCl(CClient *cl) { //Cannot use the usual macro because curB is not set yet diff --git a/client/windows/CSpellWindow.cpp b/client/windows/CSpellWindow.cpp index ed4c0c3f1..89461a0fa 100644 --- a/client/windows/CSpellWindow.cpp +++ b/client/windows/CSpellWindow.cpp @@ -646,7 +646,6 @@ void CSpellWindow::SpellArea::clickLeft(tribool down, bool previousState) //special case //todo: move to mechanics - std::vector availableTowns; std::vector Towns = owner->myInt->cb->getTownsInfo(false); vstd::erase_if(Towns, [this](const CGTownInstance * t) @@ -671,31 +670,11 @@ void CSpellWindow::SpellArea::clickLeft(tribool down, bool previousState) if (h->getSpellSchoolLevel(mySpell) < 2) //not advanced or expert - teleport to nearest available city { - auto nearest = Towns.cbegin(); //nearest town's iterator - si32 dist = owner->myInt->cb->getTown((*nearest)->id)->pos.dist2dSQ(h->pos); - - for (auto i = nearest + 1; i != Towns.cend(); ++i) - { - const CGTownInstance * dest = owner->myInt->cb->getTown((*i)->id); - si32 curDist = dest->pos.dist2dSQ(h->pos); - - if (curDist < dist) - { - nearest = i; - dist = curDist; - } - } - - if ((*nearest)->visitingHero) - owner->myInt->showInfoDialog(CGI->generaltexth->allTexts[123]); - else - { - const CGTownInstance * town = owner->myInt->cb->getTown((*nearest)->id); - owner->myInt->cb->castSpell(h, mySpell->id, town->visitablePos());// - town->getVisitableOffset()); - } + owner->myInt->cb->castSpell(h, mySpell->id, int3());// - town->getVisitableOffset()); } else { //let the player choose + std::vector availableTowns; for(auto & Town : Towns) { const CGTownInstance *t = Town; @@ -722,18 +701,6 @@ void CSpellWindow::SpellArea::clickLeft(tribool down, bool previousState) return; } - if(mySpell->id == SpellID::SUMMON_BOAT) - { - //special case - //todo: move to mechanics - int3 pos = h->bestLocation(); - if(pos.x < 0) - { - owner->myInt->showInfoDialog(CGI->generaltexth->allTexts[334]); //There is no place to put the boat. - return; - } - } - if(mySpell->getTargetType() == CSpell::LOCATION) { adventureInt->enterCastingMode(mySpell); diff --git a/client/windows/GUIClasses.cpp b/client/windows/GUIClasses.cpp index 2acecebce..23ee97b9c 100644 --- a/client/windows/GUIClasses.cpp +++ b/client/windows/GUIClasses.cpp @@ -1767,7 +1767,7 @@ void CObjectListWindow::init(CIntObject * titlePic, std::string _title, std::str ok = new CButton(Point(15, 402), "IOKAY.DEF", CButton::tooltip(), std::bind(&CObjectListWindow::elementSelected, this), SDLK_RETURN); ok->block(true); - exit = new CButton( Point(228, 402), "ICANCEL.DEF", CButton::tooltip(), std::bind(&CGuiHandler::popIntTotally,&GH, this), SDLK_ESCAPE); + exit = new CButton( Point(228, 402), "ICANCEL.DEF", CButton::tooltip(), std::bind(&CObjectListWindow::exitPressed, this), SDLK_ESCAPE); if (titlePic) { @@ -1796,6 +1796,14 @@ void CObjectListWindow::elementSelected() toCall(where);//and send selected object } +void CObjectListWindow::exitPressed() +{ + std::function toCall = onExit;//save + GH.popIntTotally(this);//then destroy window + if(toCall) + toCall(); +} + void CObjectListWindow::changeSelection(size_t which) { ok->block(false); diff --git a/client/windows/GUIClasses.h b/client/windows/GUIClasses.h index 262f1414f..21715626b 100644 --- a/client/windows/GUIClasses.h +++ b/client/windows/GUIClasses.h @@ -166,8 +166,12 @@ class CObjectListWindow : public CWindowObject std::vector< std::pair > items;//all items present in list void init(CIntObject * titlePic, std::string _title, std::string _descr); + void exitPressed(); public: size_t selected;//index of currently selected item + + std::function onExit;//optional exit callback + /// Callback will be called when OK button is pressed, returns id of selected item. initState = initially selected item /// Image can be nullptr ///item names will be taken from map objects diff --git a/lib/CGameInterface.h b/lib/CGameInterface.h index 86f60d373..8d5c55883 100644 --- a/lib/CGameInterface.h +++ b/lib/CGameInterface.h @@ -98,6 +98,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, TTeleportExitsList exits, bool impassable, QueryID askID) = 0; + virtual void showMapObjectSelectDialog(QueryID askID, const Component & icon, const MetaString & title, const MetaString & description, const std::vector & objects) = 0; virtual void finish(){}; //if for some reason we want to end virtual void showWorldViewEx(const std::vector & objectPositions){}; diff --git a/lib/CGameState.cpp b/lib/CGameState.cpp index 476228bf9..b454fa389 100644 --- a/lib/CGameState.cpp +++ b/lib/CGameState.cpp @@ -146,6 +146,9 @@ void MetaString::getLocalString(const std::pair &txt, std::string &dst case COLOR: vec = &VLC->generaltexth->capColors; break; + case JK_TXT: + vec = &VLC->generaltexth->jktexts; + break; default: logGlobal->errorStream() << "Failed string substitution because type is " << type; dst = "#@#"; diff --git a/lib/NetPacks.h b/lib/NetPacks.h index 474c3d927..0f7432155 100644 --- a/lib/NetPacks.h +++ b/lib/NetPacks.h @@ -3,6 +3,7 @@ #include "NetPacksBase.h" #include "battle/BattleAction.h" +#include "JsonNode.h" #include "mapObjects/CGHeroInstance.h" #include "ConstTransitivePtr.h" #include "int3.h" @@ -1141,12 +1142,6 @@ struct BlockingDialog : public Query soundID = 0; }; - void addResourceComponents(TResources resources) - { - for(TResources::nziterator i(resources); i.valid(); i++) - components.push_back(Component(Component::RESOURCE, i->resType, i->resVal, 0)); - } - template void serialize(Handler &h, const int version) { h & queryID & text & components & player & flags & soundID; @@ -1205,6 +1200,24 @@ struct TeleportDialog : public Query } }; +struct MapObjectSelectDialog : public Query +{ + PlayerColor player; + Component icon; + MetaString title; + MetaString description; + std::vector objects; + + MapObjectSelectDialog(){}; + + void applyCl(CClient * cl); + + template void serialize(Handler & h, const int version) + { + h & queryID & player & icon & title & description & objects; + } +}; + struct BattleInfo; struct BattleStart : public CPackForClient { @@ -2058,16 +2071,16 @@ struct BuildBoat : public CPackForServer struct QueryReply : public CPackForServer { - QueryReply():answer(0){}; - QueryReply(QueryID QID, ui32 Answer):qid(QID),answer(Answer){}; + QueryReply(){}; + QueryReply(QueryID QID, const JsonNode & Reply):qid(QID), reply(Reply){}; QueryID qid; - ui32 answer; //hero and artifact id PlayerColor player; + JsonNode reply; bool applyGh(CGameHandler *gh); template void serialize(Handler &h, const int version) { - h & qid & answer & player; + h & qid & player & reply; } }; diff --git a/lib/NetPacksBase.h b/lib/NetPacksBase.h index b2bb6e321..49eb0d4ca 100644 --- a/lib/NetPacksBase.h +++ b/lib/NetPacksBase.h @@ -44,11 +44,11 @@ private: enum EMessage {TEXACT_STRING, TLOCAL_STRING, TNUMBER, TREPLACE_ESTRING, TREPLACE_LSTRING, TREPLACE_NUMBER, TREPLACE_PLUSNUMBER}; public: enum {GENERAL_TXT=1, XTRAINFO_TXT, OBJ_NAMES, RES_NAMES, ART_NAMES, ARRAY_TXT, CRE_PL_NAMES, CREGENS, MINE_NAMES, - MINE_EVNTS, ADVOB_TXT, ART_EVNTS, SPELL_NAME, SEC_SKILL_NAME, CRE_SING_NAMES, CREGENS4, COLOR, ART_DESCR}; + MINE_EVNTS, ADVOB_TXT, ART_EVNTS, SPELL_NAME, SEC_SKILL_NAME, CRE_SING_NAMES, CREGENS4, COLOR, ART_DESCR, JK_TXT}; std::vector message; //vector of EMessage - std::vector > localStrings; //pairs; types: 1 - generaltexthandler->all; 2 - objh->xtrainfo; 3 - objh->names; 4 - objh->restypes; 5 - arth->artifacts[id].name; 6 - generaltexth->arraytxt; 7 - creh->creatures[os->subID].namePl; 8 - objh->creGens; 9 - objh->mines[ID]->first; 10 - objh->mines[ID]->second; 11 - objh->advobtxt + std::vector > localStrings; std::vector exactStrings; std::vector numbers; diff --git a/lib/registerTypes/RegisterTypes.h b/lib/registerTypes/RegisterTypes.h index 18869d99e..38b21d977 100644 --- a/lib/registerTypes/RegisterTypes.h +++ b/lib/registerTypes/RegisterTypes.h @@ -290,6 +290,7 @@ void registerTypesClientPacks2(Serializer &s) s.template registerType(); s.template registerType(); s.template registerType(); + s.template registerType(); s.template registerType(); s.template registerType(); diff --git a/lib/spells/AdventureSpellMechanics.cpp b/lib/spells/AdventureSpellMechanics.cpp index 9db4394e5..1bd9036b8 100644 --- a/lib/spells/AdventureSpellMechanics.cpp +++ b/lib/spells/AdventureSpellMechanics.cpp @@ -20,6 +20,11 @@ #include "../CPlayerState.h" ///AdventureSpellMechanics +AdventureSpellMechanics::AdventureSpellMechanics(const CSpell * s): + IAdventureSpellMechanics(s) +{ +} + bool AdventureSpellMechanics::adventureCast(const SpellCastEnvironment * env, AdventureSpellCastParameters & parameters) const { if(!owner->isAdventureSpell()) @@ -75,7 +80,7 @@ bool AdventureSpellMechanics::adventureCast(const SpellCastEnvironment * env, Ad return false; } -ESpellCastResult AdventureSpellMechanics::applyAdventureEffects(const SpellCastEnvironment * env, AdventureSpellCastParameters & parameters) const +ESpellCastResult AdventureSpellMechanics::applyAdventureEffects(const SpellCastEnvironment * env, const AdventureSpellCastParameters & parameters) const { if(owner->hasEffects()) { @@ -105,9 +110,25 @@ ESpellCastResult AdventureSpellMechanics::applyAdventureEffects(const SpellCastE } ///SummonBoatMechanics -ESpellCastResult SummonBoatMechanics::applyAdventureEffects(const SpellCastEnvironment * env, AdventureSpellCastParameters & parameters) const +SummonBoatMechanics::SummonBoatMechanics(const CSpell * s): + AdventureSpellMechanics(s) { +} + +ESpellCastResult SummonBoatMechanics::applyAdventureEffects(const SpellCastEnvironment * env, const AdventureSpellCastParameters & parameters) const +{ + int3 summonPos = parameters.caster->bestLocation(); + if(summonPos.x < 0) + { + InfoWindow iw; + iw.player = parameters.caster->tempOwner; + iw.text.addTxt(MetaString::GENERAL_TXT, 334);//There is no place to put the boat. + env->sendAndApply(&iw); + return ESpellCastResult::CANCEL; + } + const int schoolLevel = parameters.caster->getSpellSchoolLevel(owner); + //check if spell works at all if(env->getRandomGenerator().nextInt(99) >= owner->getPower(schoolLevel)) //power is % chance of success { @@ -122,13 +143,6 @@ ESpellCastResult SummonBoatMechanics::applyAdventureEffects(const SpellCastEnvir //try to find unoccupied boat to summon const CGBoat * nearest = nullptr; double dist = 0; - int3 summonPos = parameters.caster->bestLocation(); - if(summonPos.x < 0) - { - env->complain("There is no water tile available!"); - return ESpellCastResult::ERROR; - } - for(const CGObjectInstance * obj : env->getMap()->objects) { if(obj && obj->ID == Obj::BOAT) @@ -150,7 +164,7 @@ ESpellCastResult SummonBoatMechanics::applyAdventureEffects(const SpellCastEnvir { ChangeObjPos cop; cop.objid = nearest->id; - cop.nPos = summonPos + int3(1,0,0);; + cop.nPos = summonPos + int3(1,0,0); cop.flags = 1; env->sendAndApply(&cop); } @@ -166,14 +180,19 @@ ESpellCastResult SummonBoatMechanics::applyAdventureEffects(const SpellCastEnvir NewObject no; no.ID = Obj::BOAT; no.subID = parameters.caster->getBoatType(); - no.pos = summonPos + int3(1,0,0);; + no.pos = summonPos + int3(1,0,0); env->sendAndApply(&no); } return ESpellCastResult::OK; } ///ScuttleBoatMechanics -ESpellCastResult ScuttleBoatMechanics::applyAdventureEffects(const SpellCastEnvironment* env, AdventureSpellCastParameters& parameters) const +ScuttleBoatMechanics::ScuttleBoatMechanics(const CSpell * s): + AdventureSpellMechanics(s) +{ +} + +ESpellCastResult ScuttleBoatMechanics::applyAdventureEffects(const SpellCastEnvironment * env, const AdventureSpellCastParameters & parameters) const { const int schoolLevel = parameters.caster->getSpellSchoolLevel(owner); //check if spell works at all @@ -208,7 +227,12 @@ ESpellCastResult ScuttleBoatMechanics::applyAdventureEffects(const SpellCastEnvi } ///DimensionDoorMechanics -ESpellCastResult DimensionDoorMechanics::applyAdventureEffects(const SpellCastEnvironment* env, AdventureSpellCastParameters& parameters) const +DimensionDoorMechanics::DimensionDoorMechanics(const CSpell * s): + AdventureSpellMechanics(s) +{ +} + +ESpellCastResult DimensionDoorMechanics::applyAdventureEffects(const SpellCastEnvironment * env, const AdventureSpellCastParameters & parameters) const { if(!env->getMap()->isInTheMap(parameters.pos)) { @@ -276,67 +300,92 @@ ESpellCastResult DimensionDoorMechanics::applyAdventureEffects(const SpellCastEn } ///TownPortalMechanics -ESpellCastResult TownPortalMechanics::applyAdventureEffects(const SpellCastEnvironment * env, AdventureSpellCastParameters& parameters) const +TownPortalMechanics::TownPortalMechanics(const CSpell * s): + AdventureSpellMechanics(s) { - if (!env->getMap()->isInTheMap(parameters.pos)) - { - env->complain("Destination tile not present!"); - return ESpellCastResult::ERROR; - } +} - TerrainTile tile = env->getMap()->getTile(parameters.pos); - if (tile.visitableObjects.empty() || tile.visitableObjects.back()->ID != Obj::TOWN) - { - env->complain("Town not found for Town Portal!"); - return ESpellCastResult::ERROR; - } +ESpellCastResult TownPortalMechanics::applyAdventureEffects(const SpellCastEnvironment * env, const AdventureSpellCastParameters & parameters) const +{ + const CGTownInstance * destination = nullptr; + const int movementCost = GameConstants::BASE_MOVEMENT_COST * ((parameters.caster->getSpellSchoolLevel(owner) >= 3) ? 2 : 3); - CGTownInstance * town = static_cast(tile.visitableObjects.back()); + if(parameters.caster->getSpellSchoolLevel(owner) < 2) + { + std::vector pool = getPossibleTowns(env, parameters); + destination = findNearestTown(env, parameters, pool); - const auto relations = env->getCb()->getPlayerRelations(town->tempOwner, parameters.caster->tempOwner); - - if(relations == PlayerRelations::ENEMIES) - { - env->complain("Can't teleport to enemy!"); - return ESpellCastResult::ERROR; - } - - if (town->visitingHero) - { - env->complain("Can't teleport to occupied town!"); - return ESpellCastResult::ERROR; - } - - if (parameters.caster->getSpellSchoolLevel(owner) < 2) - { - si32 dist = town->pos.dist2dSQ(parameters.caster->pos); - ObjectInstanceID nearest = town->id; //nearest town's ID - for(const CGTownInstance * currTown : env->getCb()->getPlayer(parameters.caster->tempOwner)->towns) + if(nullptr == destination) { - si32 currDist = currTown->pos.dist2dSQ(parameters.caster->pos); - if (currDist < dist) - { - nearest = currTown->id; - dist = currDist; - } + InfoWindow iw; + iw.player = parameters.caster->tempOwner; + iw.text.addTxt(MetaString::GENERAL_TXT, 124); + env->sendAndApply(&iw); + return ESpellCastResult::CANCEL; } - if (town->id != nearest) + + if(parameters.caster->movement < movementCost) { - env->complain("This hero can only teleport to nearest town!"); + InfoWindow iw; + iw.player = parameters.caster->tempOwner; + iw.text.addTxt(MetaString::GENERAL_TXT, 125); + env->sendAndApply(&iw); + return ESpellCastResult::CANCEL; + } + + if (destination->visitingHero) + { + InfoWindow iw; + iw.player = parameters.caster->tempOwner; + iw.text.addTxt(MetaString::GENERAL_TXT, 123); + env->sendAndApply(&iw); + return ESpellCastResult::CANCEL; + } + } + else if(env->getMap()->isInTheMap(parameters.pos)) + { + const TerrainTile & tile = env->getMap()->getTile(parameters.pos); + if(tile.visitableObjects.empty() || tile.visitableObjects.back()->ID != Obj::TOWN) + { + env->complain("No town at destination tile"); return ESpellCastResult::ERROR; } + destination = dynamic_cast(tile.visitableObjects.back()); + + if(nullptr == destination) + { + env->complain("[Internal error] invalid town object"); + return ESpellCastResult::ERROR; + } + + const auto relations = env->getCb()->getPlayerRelations(destination->tempOwner, parameters.caster->tempOwner); + + if(relations == PlayerRelations::ENEMIES) + { + env->complain("Can't teleport to enemy!"); + return ESpellCastResult::ERROR; + } + + if(parameters.caster->movement < movementCost) + { + env->complain("This hero has not enough movement points!"); + return ESpellCastResult::ERROR; + } + + if(destination->visitingHero) + { + env->complain("Can't teleport to occupied town!"); + return ESpellCastResult::ERROR; + } } - - const int movementCost = GameConstants::BASE_MOVEMENT_COST * ((parameters.caster->getSpellSchoolLevel(owner) >= 3) ? 2 : 3); - - if(parameters.caster->movement < movementCost) + else { - env->complain("This hero has not enough movement points!"); + env->complain("Invalid destination tile"); return ESpellCastResult::ERROR; } - if(env->moveHero(parameters.caster->id, town->visitablePos() + parameters.caster->getVisitableOffset() ,1)) + if(env->moveHero(parameters.caster->id, destination->visitablePos() + parameters.caster->getVisitableOffset(), 1)) { SetMovePoints smp; smp.hid = parameters.caster->id; @@ -346,7 +395,50 @@ ESpellCastResult TownPortalMechanics::applyAdventureEffects(const SpellCastEnvir return ESpellCastResult::OK; } -ESpellCastResult ViewMechanics::applyAdventureEffects(const SpellCastEnvironment * env, AdventureSpellCastParameters & parameters) const +const CGTownInstance * TownPortalMechanics::findNearestTown(const SpellCastEnvironment * env, const AdventureSpellCastParameters & parameters, const std::vector & pool) const +{ + if(pool.empty()) + return nullptr; + + auto nearest = pool.cbegin(); //nearest town's iterator + si32 dist = (*nearest)->pos.dist2dSQ(parameters.caster->pos); + + for (auto i = nearest + 1; i != pool.cend(); ++i) + { + si32 curDist = (*i)->pos.dist2dSQ(parameters.caster->pos); + + if (curDist < dist) + { + nearest = i; + dist = curDist; + } + } + return *nearest; +} + +std::vector TownPortalMechanics::getPossibleTowns(const SpellCastEnvironment * env, const AdventureSpellCastParameters & parameters) const +{ + std::vector ret; + + const TeamState * team = env->getCb()->getPlayerTeam(parameters.caster->getOwner()); + + for(const auto & color : team->players) + { + for(auto currTown : env->getCb()->getPlayer(color)->towns) + { + ret.push_back(currTown.get()); + } + } + return ret; +} + +///ViewMechanics +ViewMechanics::ViewMechanics(const CSpell * s): + AdventureSpellMechanics(s) +{ +} + +ESpellCastResult ViewMechanics::applyAdventureEffects(const SpellCastEnvironment * env, const AdventureSpellCastParameters & parameters) const { ShowWorldViewEx pack; @@ -373,13 +465,25 @@ ESpellCastResult ViewMechanics::applyAdventureEffects(const SpellCastEnvironment return ESpellCastResult::OK; } +///ViewAirMechanics +ViewAirMechanics::ViewAirMechanics(const CSpell * s): + ViewMechanics(s) +{ +} + bool ViewAirMechanics::filterObject(const CGObjectInstance * obj, const int spellLevel) const { - return (obj->ID == Obj::ARTIFACT) || (spellLevel>1 && obj->ID == Obj::HERO) || (spellLevel>2 && obj->ID == Obj::TOWN); + return (obj->ID == Obj::ARTIFACT) || (spellLevel > 1 && obj->ID == Obj::HERO) || (spellLevel > 2 && obj->ID == Obj::TOWN); +} + +///ViewEarthMechanics +ViewEarthMechanics::ViewEarthMechanics(const CSpell * s): + ViewMechanics(s) +{ } bool ViewEarthMechanics::filterObject(const CGObjectInstance * obj, const int spellLevel) const { - return (obj->ID == Obj::RESOURCE) || (spellLevel>1 && obj->ID == Obj::MINE); + return (obj->ID == Obj::RESOURCE) || (spellLevel > 1 && obj->ID == Obj::MINE); } diff --git a/lib/spells/AdventureSpellMechanics.h b/lib/spells/AdventureSpellMechanics.h index b2ee4bda4..75dd21243 100644 --- a/lib/spells/AdventureSpellMechanics.h +++ b/lib/spells/AdventureSpellMechanics.h @@ -12,6 +12,8 @@ #include "ISpellMechanics.h" +class CGTownInstance; + enum class ESpellCastResult { OK, @@ -19,62 +21,65 @@ enum class ESpellCastResult ERROR//internal error occurred }; -class DLL_LINKAGE AdventureSpellMechanics: public IAdventureSpellMechanics +class DLL_LINKAGE AdventureSpellMechanics : public IAdventureSpellMechanics { public: - AdventureSpellMechanics(CSpell * s): IAdventureSpellMechanics(s){}; + AdventureSpellMechanics(const CSpell * s); bool adventureCast(const SpellCastEnvironment * env, AdventureSpellCastParameters & parameters) const override final; protected: ///actual adventure cast implementation - virtual ESpellCastResult applyAdventureEffects(const SpellCastEnvironment * env, AdventureSpellCastParameters & parameters) const; + virtual ESpellCastResult applyAdventureEffects(const SpellCastEnvironment * env, const AdventureSpellCastParameters & parameters) const; }; class DLL_LINKAGE SummonBoatMechanics : public AdventureSpellMechanics { public: - SummonBoatMechanics(CSpell * s): AdventureSpellMechanics(s){}; + SummonBoatMechanics(const CSpell * s); protected: - ESpellCastResult applyAdventureEffects(const SpellCastEnvironment * env, AdventureSpellCastParameters & parameters) const override; + ESpellCastResult applyAdventureEffects(const SpellCastEnvironment * env, const AdventureSpellCastParameters & parameters) const override; }; class DLL_LINKAGE ScuttleBoatMechanics : public AdventureSpellMechanics { public: - ScuttleBoatMechanics(CSpell * s): AdventureSpellMechanics(s){}; + ScuttleBoatMechanics(const CSpell * s); protected: - ESpellCastResult applyAdventureEffects(const SpellCastEnvironment * env, AdventureSpellCastParameters & parameters) const override; + ESpellCastResult applyAdventureEffects(const SpellCastEnvironment * env, const AdventureSpellCastParameters & parameters) const override; }; class DLL_LINKAGE DimensionDoorMechanics : public AdventureSpellMechanics { public: - DimensionDoorMechanics(CSpell * s): AdventureSpellMechanics(s){}; + DimensionDoorMechanics(const CSpell * s); protected: - ESpellCastResult applyAdventureEffects(const SpellCastEnvironment * env, AdventureSpellCastParameters & parameters) const override; + ESpellCastResult applyAdventureEffects(const SpellCastEnvironment * env, const AdventureSpellCastParameters & parameters) const override; }; class DLL_LINKAGE TownPortalMechanics : public AdventureSpellMechanics { public: - TownPortalMechanics(CSpell * s): AdventureSpellMechanics(s){}; + TownPortalMechanics(const CSpell * s); protected: - ESpellCastResult applyAdventureEffects(const SpellCastEnvironment * env, AdventureSpellCastParameters & parameters) const override; + ESpellCastResult applyAdventureEffects(const SpellCastEnvironment * env, const AdventureSpellCastParameters & parameters) const override; +private: + const CGTownInstance * findNearestTown(const SpellCastEnvironment * env, const AdventureSpellCastParameters & parameters, const std::vector & pool) const; + std::vector getPossibleTowns(const SpellCastEnvironment * env, const AdventureSpellCastParameters & parameters) const; }; class DLL_LINKAGE ViewMechanics : public AdventureSpellMechanics { public: - ViewMechanics(CSpell * s): AdventureSpellMechanics(s){}; + ViewMechanics(const CSpell * s); protected: - ESpellCastResult applyAdventureEffects(const SpellCastEnvironment * env, AdventureSpellCastParameters & parameters) const override; + ESpellCastResult applyAdventureEffects(const SpellCastEnvironment * env, const AdventureSpellCastParameters & parameters) const override; virtual bool filterObject(const CGObjectInstance * obj, const int spellLevel) const = 0; }; class DLL_LINKAGE ViewAirMechanics : public ViewMechanics { public: - ViewAirMechanics(CSpell * s): ViewMechanics(s){}; + ViewAirMechanics(const CSpell * s); protected: bool filterObject(const CGObjectInstance * obj, const int spellLevel) const override; }; @@ -82,7 +87,7 @@ protected: class DLL_LINKAGE ViewEarthMechanics : public ViewMechanics { public: - ViewEarthMechanics(CSpell * s): ViewMechanics(s){}; + ViewEarthMechanics(const CSpell * s); protected: bool filterObject(const CGObjectInstance * obj, const int spellLevel) const override; }; diff --git a/lib/spells/BattleSpellMechanics.cpp b/lib/spells/BattleSpellMechanics.cpp index ddbfad26d..35f167b9c 100644 --- a/lib/spells/BattleSpellMechanics.cpp +++ b/lib/spells/BattleSpellMechanics.cpp @@ -18,6 +18,11 @@ #include "../mapObjects/CGTownInstance.h" ///HealingSpellMechanics +HealingSpellMechanics::HealingSpellMechanics(const CSpell * s): + DefaultSpellMechanics(s) +{ +} + void HealingSpellMechanics::applyBattleEffects(const SpellCastEnvironment * env, const BattleSpellCastParameters & parameters, SpellCastContext & ctx) const { EHealLevel healLevel = getHealLevel(parameters.effectLevel); @@ -48,6 +53,11 @@ int HealingSpellMechanics::calculateHealedHP(const SpellCastEnvironment* env, co } ///AntimagicMechanics +AntimagicMechanics::AntimagicMechanics(const CSpell * s): + DefaultSpellMechanics(s) +{ +} + void AntimagicMechanics::applyBattle(BattleInfo * battle, const BattleSpellCast * packet) const { DefaultSpellMechanics::applyBattle(battle, packet); @@ -74,6 +84,11 @@ void AntimagicMechanics::applyBattle(BattleInfo * battle, const BattleSpellCast } ///ChainLightningMechanics +ChainLightningMechanics::ChainLightningMechanics(const CSpell * s): + DefaultSpellMechanics(s) +{ +} + std::vector ChainLightningMechanics::calculateAffectedStacks(const CBattleInfoCallback * cb, const SpellTargetingContext & ctx) const { std::vector res; @@ -111,6 +126,11 @@ std::vector ChainLightningMechanics::calculateAffectedStacks(con } ///CloneMechanics +CloneMechanics::CloneMechanics(const CSpell * s): + DefaultSpellMechanics(s) +{ +} + void CloneMechanics::applyBattleEffects(const SpellCastEnvironment * env, const BattleSpellCastParameters & parameters, SpellCastContext & ctx) const { const CStack * clonedStack = nullptr; @@ -180,10 +200,14 @@ ESpellCastProblem::ESpellCastProblem CloneMechanics::isImmuneByStack(const ISpel } ///CureMechanics +CureMechanics::CureMechanics(const CSpell * s): + HealingSpellMechanics(s) +{ +} + void CureMechanics::applyBattle(BattleInfo * battle, const BattleSpellCast * packet) const { DefaultSpellMechanics::applyBattle(battle, packet); - doDispell(battle, packet, dispellSelector); } @@ -212,6 +236,11 @@ ESpellCastProblem::ESpellCastProblem CureMechanics::isImmuneByStack(const ISpell } ///DispellMechanics +DispellMechanics::DispellMechanics(const CSpell * s): + DefaultSpellMechanics(s) +{ +} + void DispellMechanics::applyBattle(BattleInfo * battle, const BattleSpellCast * packet) const { DefaultSpellMechanics::applyBattle(battle, packet); @@ -261,6 +290,11 @@ void DispellMechanics::applyBattleEffects(const SpellCastEnvironment * env, cons } ///EarthquakeMechanics +EarthquakeMechanics::EarthquakeMechanics(const CSpell * s): + SpecialSpellMechanics(s) +{ +} + void EarthquakeMechanics::applyBattleEffects(const SpellCastEnvironment * env, const BattleSpellCastParameters & parameters, SpellCastContext & ctx) const { if(nullptr == parameters.cb->battleGetDefendedTown()) @@ -391,6 +425,11 @@ bool EarthquakeMechanics::requiresCreatureTarget() const } ///HypnotizeMechanics +HypnotizeMechanics::HypnotizeMechanics(const CSpell * s): + DefaultSpellMechanics(s) +{ +} + ESpellCastProblem::ESpellCastProblem HypnotizeMechanics::isImmuneByStack(const ISpellCaster * caster, const CStack * obj) const { //todo: maybe do not resist on passive cast @@ -407,6 +446,11 @@ ESpellCastProblem::ESpellCastProblem HypnotizeMechanics::isImmuneByStack(const I } ///ObstacleMechanics +ObstacleMechanics::ObstacleMechanics(const CSpell * s): + SpecialSpellMechanics(s) +{ +} + ESpellCastProblem::ESpellCastProblem ObstacleMechanics::canBeCast(const CBattleInfoCallback * cb, const SpellTargetingContext & ctx) const { const auto side = cb->playerToSide(ctx.caster->getOwner()); @@ -479,6 +523,11 @@ void ObstacleMechanics::placeObstacle(const SpellCastEnvironment * env, const Ba } ///PatchObstacleMechanics +PatchObstacleMechanics::PatchObstacleMechanics(const CSpell * s): + ObstacleMechanics(s) +{ +} + void PatchObstacleMechanics::applyBattleEffects(const SpellCastEnvironment * env, const BattleSpellCastParameters & parameters, SpellCastContext & ctx) const { std::vector availableTiles; @@ -498,6 +547,11 @@ void PatchObstacleMechanics::applyBattleEffects(const SpellCastEnvironment * env } ///LandMineMechanics +LandMineMechanics::LandMineMechanics(const CSpell * s): + PatchObstacleMechanics(s) +{ +} + ESpellCastProblem::ESpellCastProblem LandMineMechanics::canBeCast(const CBattleInfoCallback * cb, const ECastingMode::ECastingMode mode, const ISpellCaster * caster) const { //LandMine are useless if enemy has native stack and can see mines, check for LandMine damage immunity is done in general way by CSpell @@ -526,6 +580,11 @@ void LandMineMechanics::setupObstacle(SpellCreatedObstacle * obstacle) const } ///QuicksandMechanics +QuicksandMechanics::QuicksandMechanics(const CSpell * s): + PatchObstacleMechanics(s) +{ +} + bool QuicksandMechanics::requiresCreatureTarget() const { return false; @@ -539,6 +598,11 @@ void QuicksandMechanics::setupObstacle(SpellCreatedObstacle * obstacle) const } ///WallMechanics +WallMechanics::WallMechanics(const CSpell * s): + ObstacleMechanics(s) +{ +} + std::vector WallMechanics::rangeInHexes(BattleHex centralHex, ui8 schoolLvl, ui8 side, bool * outDroppedHexes) const { std::vector ret; @@ -576,6 +640,11 @@ std::vector WallMechanics::rangeInHexes(BattleHex centralHex, ui8 sch } ///FireWallMechanics +FireWallMechanics::FireWallMechanics(const CSpell * s): + WallMechanics(s) +{ +} + bool FireWallMechanics::requiresCreatureTarget() const { return true; @@ -604,6 +673,11 @@ void FireWallMechanics::setupObstacle(SpellCreatedObstacle * obstacle) const } ///ForceFieldMechanics +ForceFieldMechanics::ForceFieldMechanics(const CSpell * s): + WallMechanics(s) +{ +} + bool ForceFieldMechanics::requiresCreatureTarget() const { return false; @@ -629,6 +703,11 @@ void ForceFieldMechanics::setupObstacle(SpellCreatedObstacle * obstacle) const } ///RemoveObstacleMechanics +RemoveObstacleMechanics::RemoveObstacleMechanics(const CSpell * s): + SpecialSpellMechanics(s) +{ +} + void RemoveObstacleMechanics::applyBattleEffects(const SpellCastEnvironment * env, const BattleSpellCastParameters & parameters, SpellCastContext & ctx) const { if(auto obstacleToRemove = parameters.cb->battleGetObstacleOnPos(parameters.getFirstDestinationHex(), false)) @@ -705,6 +784,11 @@ bool RemoveObstacleMechanics::requiresCreatureTarget() const } ///RisingSpellMechanics +RisingSpellMechanics::RisingSpellMechanics(const CSpell * s): + HealingSpellMechanics(s) +{ +} + HealingSpellMechanics::EHealLevel RisingSpellMechanics::getHealLevel(int effectLevel) const { //this may be even distinct class @@ -715,6 +799,11 @@ HealingSpellMechanics::EHealLevel RisingSpellMechanics::getHealLevel(int effectL } ///SacrificeMechanics +SacrificeMechanics::SacrificeMechanics(const CSpell * s): + RisingSpellMechanics(s) +{ +} + ESpellCastProblem::ESpellCastProblem SacrificeMechanics::canBeCast(const CBattleInfoCallback * cb, const ECastingMode::ECastingMode mode, const ISpellCaster * caster) const { if(mode == ECastingMode::AFTER_ATTACK_CASTING || mode == ECastingMode::SPELL_LIKE_ATTACK || mode == ECastingMode::MAGIC_MIRROR) @@ -800,6 +889,11 @@ bool SacrificeMechanics::requiresCreatureTarget() const } ///SpecialRisingSpellMechanics +SpecialRisingSpellMechanics::SpecialRisingSpellMechanics(const CSpell * s): + RisingSpellMechanics(s) +{ +} + ESpellCastProblem::ESpellCastProblem SpecialRisingSpellMechanics::canBeCast(const CBattleInfoCallback * cb, const SpellTargetingContext & ctx) const { //find alive possible target @@ -861,6 +955,11 @@ ESpellCastProblem::ESpellCastProblem SpecialRisingSpellMechanics::isImmuneByStac } ///SummonMechanics +SummonMechanics::SummonMechanics(const CSpell * s, CreatureID cre): + SpecialSpellMechanics(s), creatureToSummon(cre) +{ +} + ESpellCastProblem::ESpellCastProblem SummonMechanics::canBeCast(const CBattleInfoCallback * cb, const ECastingMode::ECastingMode mode, const ISpellCaster * caster) const { if(mode == ECastingMode::AFTER_ATTACK_CASTING || mode == ECastingMode::SPELL_LIKE_ATTACK || mode == ECastingMode::MAGIC_MIRROR) @@ -911,6 +1010,11 @@ bool SummonMechanics::requiresCreatureTarget() const } ///TeleportMechanics +TeleportMechanics::TeleportMechanics(const CSpell * s): + DefaultSpellMechanics(s) +{ +} + void TeleportMechanics::applyBattleEffects(const SpellCastEnvironment * env, const BattleSpellCastParameters & parameters, SpellCastContext & ctx) const { if(parameters.destinations.size() == 2) diff --git a/lib/spells/BattleSpellMechanics.h b/lib/spells/BattleSpellMechanics.h index 056385d75..c7b5a9002 100644 --- a/lib/spells/BattleSpellMechanics.h +++ b/lib/spells/BattleSpellMechanics.h @@ -25,7 +25,7 @@ public: TRUE_RESURRECT }; - HealingSpellMechanics(CSpell * s): DefaultSpellMechanics(s){}; + HealingSpellMechanics(const CSpell * s); protected: void applyBattleEffects(const SpellCastEnvironment * env, const BattleSpellCastParameters & parameters, SpellCastContext & ctx) const override; virtual int calculateHealedHP(const SpellCastEnvironment * env, const BattleSpellCastParameters & parameters, SpellCastContext & ctx) const; @@ -35,15 +35,14 @@ protected: class DLL_LINKAGE AntimagicMechanics : public DefaultSpellMechanics { public: - AntimagicMechanics(CSpell * s): DefaultSpellMechanics(s){}; - + AntimagicMechanics(const CSpell * s); void applyBattle(BattleInfo * battle, const BattleSpellCast * packet) const override final; }; class DLL_LINKAGE ChainLightningMechanics : public DefaultSpellMechanics { public: - ChainLightningMechanics(CSpell * s): DefaultSpellMechanics(s){}; + ChainLightningMechanics(const CSpell * s); protected: std::vector calculateAffectedStacks(const CBattleInfoCallback * cb, const SpellTargetingContext & ctx) const override; }; @@ -51,7 +50,7 @@ protected: class DLL_LINKAGE CloneMechanics : public DefaultSpellMechanics { public: - CloneMechanics(CSpell * s): DefaultSpellMechanics(s){}; + CloneMechanics(const CSpell * s); ESpellCastProblem::ESpellCastProblem isImmuneByStack(const ISpellCaster * caster, const CStack * obj) const override; protected: void applyBattleEffects(const SpellCastEnvironment * env, const BattleSpellCastParameters & parameters, SpellCastContext & ctx) const override; @@ -60,11 +59,9 @@ protected: class DLL_LINKAGE CureMechanics : public HealingSpellMechanics { public: - CureMechanics(CSpell * s): HealingSpellMechanics(s){}; - + CureMechanics(const CSpell * s); void applyBattle(BattleInfo * battle, const BattleSpellCast * packet) const override final; ESpellCastProblem::ESpellCastProblem isImmuneByStack(const ISpellCaster * caster, const CStack * obj) const override; - EHealLevel getHealLevel(int effectLevel) const override final; private: static bool dispellSelector(const Bonus * b); @@ -73,9 +70,8 @@ private: class DLL_LINKAGE DispellMechanics : public DefaultSpellMechanics { public: - DispellMechanics(CSpell * s): DefaultSpellMechanics(s){}; + DispellMechanics(const CSpell * s); ESpellCastProblem::ESpellCastProblem isImmuneByStack(const ISpellCaster * caster, const CStack * obj) const override; - void applyBattle(BattleInfo * battle, const BattleSpellCast * packet) const override final; protected: void applyBattleEffects(const SpellCastEnvironment * env, const BattleSpellCastParameters & parameters, SpellCastContext & ctx) const override; @@ -84,7 +80,7 @@ protected: class DLL_LINKAGE EarthquakeMechanics : public SpecialSpellMechanics { public: - EarthquakeMechanics(CSpell * s): SpecialSpellMechanics(s){}; + EarthquakeMechanics(const CSpell * s); ESpellCastProblem::ESpellCastProblem canBeCast(const CBattleInfoCallback * cb, const ECastingMode::ECastingMode mode, const ISpellCaster * caster) const override; bool requiresCreatureTarget() const override; protected: @@ -94,14 +90,14 @@ protected: class DLL_LINKAGE HypnotizeMechanics : public DefaultSpellMechanics { public: - HypnotizeMechanics(CSpell * s): DefaultSpellMechanics(s){}; + HypnotizeMechanics(const CSpell * s); ESpellCastProblem::ESpellCastProblem isImmuneByStack(const ISpellCaster * caster, const CStack * obj) const override; }; class DLL_LINKAGE ObstacleMechanics : public SpecialSpellMechanics { public: - ObstacleMechanics(CSpell * s): SpecialSpellMechanics(s){}; + ObstacleMechanics(const CSpell * s); ESpellCastProblem::ESpellCastProblem canBeCast(const CBattleInfoCallback * cb, const SpellTargetingContext & ctx) const override; protected: static bool isHexAviable(const CBattleInfoCallback * cb, const BattleHex & hex, const bool mustBeClear); @@ -112,7 +108,7 @@ protected: class PatchObstacleMechanics : public ObstacleMechanics { public: - PatchObstacleMechanics(CSpell * s): ObstacleMechanics(s){}; + PatchObstacleMechanics(const CSpell * s); protected: void applyBattleEffects(const SpellCastEnvironment * env, const BattleSpellCastParameters & parameters, SpellCastContext & ctx) const override; }; @@ -120,7 +116,7 @@ protected: class DLL_LINKAGE LandMineMechanics : public PatchObstacleMechanics { public: - LandMineMechanics(CSpell * s): PatchObstacleMechanics(s){}; + LandMineMechanics(const CSpell * s); ESpellCastProblem::ESpellCastProblem canBeCast(const CBattleInfoCallback * cb, const ECastingMode::ECastingMode mode, const ISpellCaster * caster) const override; bool requiresCreatureTarget() const override; protected: @@ -130,7 +126,7 @@ protected: class DLL_LINKAGE QuicksandMechanics : public PatchObstacleMechanics { public: - QuicksandMechanics(CSpell * s): PatchObstacleMechanics(s){}; + QuicksandMechanics(const CSpell * s); bool requiresCreatureTarget() const override; protected: void setupObstacle(SpellCreatedObstacle * obstacle) const override; @@ -139,14 +135,14 @@ protected: class DLL_LINKAGE WallMechanics : public ObstacleMechanics { public: - WallMechanics(CSpell * s): ObstacleMechanics(s){}; + WallMechanics(const CSpell * s); std::vector rangeInHexes(BattleHex centralHex, ui8 schoolLvl, ui8 side, bool *outDroppedHexes = nullptr) const override; }; class DLL_LINKAGE FireWallMechanics : public WallMechanics { public: - FireWallMechanics(CSpell * s): WallMechanics(s){}; + FireWallMechanics(const CSpell * s); bool requiresCreatureTarget() const override; protected: void applyBattleEffects(const SpellCastEnvironment * env, const BattleSpellCastParameters & parameters, SpellCastContext & ctx) const override; @@ -156,7 +152,7 @@ protected: class DLL_LINKAGE ForceFieldMechanics : public WallMechanics { public: - ForceFieldMechanics(CSpell * s): WallMechanics(s){}; + ForceFieldMechanics(const CSpell * s); bool requiresCreatureTarget() const override; protected: void applyBattleEffects(const SpellCastEnvironment * env, const BattleSpellCastParameters & parameters, SpellCastContext & ctx) const override; @@ -166,7 +162,7 @@ protected: class DLL_LINKAGE RemoveObstacleMechanics : public SpecialSpellMechanics { public: - RemoveObstacleMechanics(CSpell * s): SpecialSpellMechanics(s){}; + RemoveObstacleMechanics(const CSpell * s); ESpellCastProblem::ESpellCastProblem canBeCast(const CBattleInfoCallback * cb, const ECastingMode::ECastingMode mode, const ISpellCaster * caster) const override; ESpellCastProblem::ESpellCastProblem canBeCast(const CBattleInfoCallback * cb, const SpellTargetingContext & ctx) const override; bool requiresCreatureTarget() const override; @@ -180,16 +176,14 @@ private: class DLL_LINKAGE RisingSpellMechanics : public HealingSpellMechanics { public: - RisingSpellMechanics(CSpell * s): HealingSpellMechanics(s){}; - + RisingSpellMechanics(const CSpell * s); EHealLevel getHealLevel(int effectLevel) const override; }; class DLL_LINKAGE SacrificeMechanics : public RisingSpellMechanics { public: - SacrificeMechanics(CSpell * s): RisingSpellMechanics(s){}; - + SacrificeMechanics(const CSpell * s); ESpellCastProblem::ESpellCastProblem canBeCast(const CBattleInfoCallback * cb, const ECastingMode::ECastingMode mode, const ISpellCaster * caster) const override; bool requiresCreatureTarget() const override; protected: @@ -201,7 +195,7 @@ protected: class DLL_LINKAGE SpecialRisingSpellMechanics : public RisingSpellMechanics { public: - SpecialRisingSpellMechanics(CSpell * s): RisingSpellMechanics(s){}; + SpecialRisingSpellMechanics(const CSpell * s); ESpellCastProblem::ESpellCastProblem canBeCast(const CBattleInfoCallback * cb, const SpellTargetingContext & ctx) const override; ESpellCastProblem::ESpellCastProblem isImmuneByStack(const ISpellCaster * caster, const CStack * obj) const override; }; @@ -209,8 +203,7 @@ public: class DLL_LINKAGE SummonMechanics : public SpecialSpellMechanics { public: - SummonMechanics(CSpell * s, CreatureID cre): SpecialSpellMechanics(s), creatureToSummon(cre){}; - + SummonMechanics(const CSpell * s, CreatureID cre); ESpellCastProblem::ESpellCastProblem canBeCast(const CBattleInfoCallback * cb, const ECastingMode::ECastingMode mode, const ISpellCaster * caster) const override; bool requiresCreatureTarget() const override; protected: @@ -222,8 +215,7 @@ private: class DLL_LINKAGE TeleportMechanics: public DefaultSpellMechanics { public: - TeleportMechanics(CSpell * s): DefaultSpellMechanics(s){}; - + TeleportMechanics(const CSpell * s); ESpellCastProblem::ESpellCastProblem canBeCast(const CBattleInfoCallback * cb, const ECastingMode::ECastingMode mode, const ISpellCaster * caster) const override; protected: void applyBattleEffects(const SpellCastEnvironment * env, const BattleSpellCastParameters & parameters, SpellCastContext & ctx) const override; diff --git a/lib/spells/CDefaultSpellMechanics.cpp b/lib/spells/CDefaultSpellMechanics.cpp index 5bfac9c44..f2ff6bcb5 100644 --- a/lib/spells/CDefaultSpellMechanics.cpp +++ b/lib/spells/CDefaultSpellMechanics.cpp @@ -246,6 +246,11 @@ void SpellCastContext::afterCast() } ///DefaultSpellMechanics +DefaultSpellMechanics::DefaultSpellMechanics(const CSpell * s): + ISpellMechanics(s) +{ +}; + void DefaultSpellMechanics::applyBattle(BattleInfo * battle, const BattleSpellCast * packet) const { if (packet->castByHero) @@ -884,6 +889,12 @@ bool DefaultSpellMechanics::requiresCreatureTarget() const return true; } +///SpecialSpellMechanics +SpecialSpellMechanics::SpecialSpellMechanics(const CSpell * s): + DefaultSpellMechanics(s) +{ +} + ESpellCastProblem::ESpellCastProblem SpecialSpellMechanics::canBeCast(const CBattleInfoCallback * cb, const SpellTargetingContext & ctx) const { //no problems by default diff --git a/lib/spells/CDefaultSpellMechanics.h b/lib/spells/CDefaultSpellMechanics.h index b2eea19e6..b2dc3834c 100644 --- a/lib/spells/CDefaultSpellMechanics.h +++ b/lib/spells/CDefaultSpellMechanics.h @@ -45,7 +45,7 @@ private: class DLL_LINKAGE DefaultSpellMechanics : public ISpellMechanics { public: - DefaultSpellMechanics(CSpell * s): ISpellMechanics(s){}; + DefaultSpellMechanics(const CSpell * s); std::vector rangeInHexes(BattleHex centralHex, ui8 schoolLvl, ui8 side, bool * outDroppedHexes = nullptr) const override; std::vector getAffectedStacks(const CBattleInfoCallback * cb, const SpellTargetingContext & ctx) const override final; @@ -94,7 +94,7 @@ private: class DLL_LINKAGE SpecialSpellMechanics : public DefaultSpellMechanics { public: - SpecialSpellMechanics(CSpell * s): DefaultSpellMechanics(s){}; + SpecialSpellMechanics(const CSpell * s); ESpellCastProblem::ESpellCastProblem canBeCast(const CBattleInfoCallback * cb, const SpellTargetingContext & ctx) const override; protected: diff --git a/lib/spells/CreatureSpellMechanics.cpp b/lib/spells/CreatureSpellMechanics.cpp index c9c1ee665..f2d26b4b2 100644 --- a/lib/spells/CreatureSpellMechanics.cpp +++ b/lib/spells/CreatureSpellMechanics.cpp @@ -17,6 +17,11 @@ #include "../battle/BattleInfo.h" ///AcidBreathDamageMechanics +AcidBreathDamageMechanics::AcidBreathDamageMechanics(const CSpell * s): + DefaultSpellMechanics(s) +{ +} + void AcidBreathDamageMechanics::applyBattleEffects(const SpellCastEnvironment * env, const BattleSpellCastParameters & parameters, SpellCastContext & ctx) const { //todo: this should be effectValue @@ -56,6 +61,11 @@ ESpellCastProblem::ESpellCastProblem AcidBreathDamageMechanics::isImmuneByStack( } ///DeathStareMechanics +DeathStareMechanics::DeathStareMechanics(const CSpell * s): + DefaultSpellMechanics(s) +{ +} + void DeathStareMechanics::applyBattleEffects(const SpellCastEnvironment * env, const BattleSpellCastParameters & parameters, SpellCastContext & ctx) const { //calculating dmg to display @@ -80,6 +90,11 @@ void DeathStareMechanics::applyBattleEffects(const SpellCastEnvironment * env, c } ///DispellHelpfulMechanics +DispellHelpfulMechanics::DispellHelpfulMechanics(const CSpell * s): + DefaultSpellMechanics(s) +{ +} + void DispellHelpfulMechanics::applyBattle(BattleInfo * battle, const BattleSpellCast * packet) const { DefaultSpellMechanics::applyBattle(battle, packet); diff --git a/lib/spells/CreatureSpellMechanics.h b/lib/spells/CreatureSpellMechanics.h index f6025c4f7..6353105de 100644 --- a/lib/spells/CreatureSpellMechanics.h +++ b/lib/spells/CreatureSpellMechanics.h @@ -16,10 +16,8 @@ class DLL_LINKAGE AcidBreathDamageMechanics : public DefaultSpellMechanics { public: - AcidBreathDamageMechanics(CSpell * s): DefaultSpellMechanics(s){}; - + AcidBreathDamageMechanics(const CSpell * s); ESpellCastProblem::ESpellCastProblem isImmuneByStack(const ISpellCaster * caster, const CStack * obj) const override; - protected: void applyBattleEffects(const SpellCastEnvironment * env, const BattleSpellCastParameters & parameters, SpellCastContext & ctx) const override; }; @@ -27,7 +25,7 @@ protected: class DLL_LINKAGE DeathStareMechanics : public DefaultSpellMechanics { public: - DeathStareMechanics(CSpell * s): DefaultSpellMechanics(s){}; + DeathStareMechanics(const CSpell * s); protected: void applyBattleEffects(const SpellCastEnvironment * env, const BattleSpellCastParameters & parameters, SpellCastContext & ctx) const override; }; @@ -35,11 +33,9 @@ protected: class DLL_LINKAGE DispellHelpfulMechanics : public DefaultSpellMechanics { public: - DispellHelpfulMechanics(CSpell * s): DefaultSpellMechanics(s){}; - + DispellHelpfulMechanics(const CSpell * s); void applyBattle(BattleInfo * battle, const BattleSpellCast * packet) const override final; - ESpellCastProblem::ESpellCastProblem isImmuneByStack(const ISpellCaster * caster, const CStack * obj) const override; private: - static bool positiveSpellEffects(const Bonus * b); + static bool positiveSpellEffects(const Bonus * b); }; diff --git a/lib/spells/ISpellMechanics.cpp b/lib/spells/ISpellMechanics.cpp index b1f961740..38b72fea1 100644 --- a/lib/spells/ISpellMechanics.cpp +++ b/lib/spells/ISpellMechanics.cpp @@ -113,13 +113,12 @@ int BattleSpellCastParameters::getEffectValue() const } ///ISpellMechanics -ISpellMechanics::ISpellMechanics(CSpell * s): +ISpellMechanics::ISpellMechanics(const CSpell * s): owner(s) { - } -std::unique_ptr ISpellMechanics::createMechanics(CSpell * s) +std::unique_ptr ISpellMechanics::createMechanics(const CSpell * s) { switch (s->id) { @@ -174,13 +173,12 @@ std::unique_ptr ISpellMechanics::createMechanics(CSpell * s) } //IAdventureSpellMechanics -IAdventureSpellMechanics::IAdventureSpellMechanics(CSpell * s): +IAdventureSpellMechanics::IAdventureSpellMechanics(const CSpell * s): owner(s) { - } -std::unique_ptr IAdventureSpellMechanics::createMechanics(CSpell * s) +std::unique_ptr IAdventureSpellMechanics::createMechanics(const CSpell * s) { switch (s->id) { diff --git a/lib/spells/ISpellMechanics.h b/lib/spells/ISpellMechanics.h index 6bf0fc499..6120b9d8f 100644 --- a/lib/spells/ISpellMechanics.h +++ b/lib/spells/ISpellMechanics.h @@ -13,7 +13,6 @@ #include "CSpellHandler.h" #include "../battle/BattleHex.h" - ///callback to be provided by server class DLL_LINKAGE SpellCastEnvironment { @@ -101,13 +100,12 @@ struct DLL_LINKAGE SpellTargetingContext SpellTargetingContext(const CSpell * s, ECastingMode::ECastingMode mode_, const ISpellCaster * caster_, int schoolLvl_, BattleHex destination_) : ti(s,schoolLvl_, mode_), mode(mode_), destination(destination_), caster(caster_), schoolLvl(schoolLvl_) {}; - }; class DLL_LINKAGE ISpellMechanics { public: - ISpellMechanics(CSpell * s); + ISpellMechanics(const CSpell * s); virtual ~ISpellMechanics(){}; virtual std::vector rangeInHexes(BattleHex centralHex, ui8 schoolLvl, ui8 side, bool * outDroppedHexes = nullptr) const = 0; @@ -125,9 +123,9 @@ public: //if true use generic algorithm for target existence check, see CSpell::canBeCast virtual bool requiresCreatureTarget() const = 0; - static std::unique_ptr createMechanics(CSpell * s); + static std::unique_ptr createMechanics(const CSpell * s); protected: - CSpell * owner; + const CSpell * owner; }; struct DLL_LINKAGE AdventureSpellCastParameters @@ -139,12 +137,12 @@ struct DLL_LINKAGE AdventureSpellCastParameters class DLL_LINKAGE IAdventureSpellMechanics { public: - IAdventureSpellMechanics(CSpell * s); + IAdventureSpellMechanics(const CSpell * s); virtual ~IAdventureSpellMechanics() = default; virtual bool adventureCast(const SpellCastEnvironment * env, AdventureSpellCastParameters & parameters) const = 0; - static std::unique_ptr createMechanics(CSpell * s); + static std::unique_ptr createMechanics(const CSpell * s); protected: - CSpell * owner; + const CSpell * owner; }; diff --git a/server/CGameHandler.cpp b/server/CGameHandler.cpp index c5fa7c4c0..6de80e18b 100644 --- a/server/CGameHandler.cpp +++ b/server/CGameHandler.cpp @@ -310,7 +310,7 @@ void CGameHandler::levelUpHero(const CGHeroInstance * hero) } else if (hlu.skills.size() > 1) { - auto levelUpQuery = std::make_shared(hlu); + auto levelUpQuery = std::make_shared(this, hlu); hlu.queryID = levelUpQuery->queryID; queries.addQuery(levelUpQuery); sendAndApply(&hlu); @@ -448,7 +448,7 @@ void CGameHandler::levelUpCommander(const CCommanderInstance * c) } else if (skillAmount > 1) //apply and ask for secondary skill { - auto commanderLevelUp = std::make_shared(clu); + auto commanderLevelUp = std::make_shared(this, clu); clu.queryID = commanderLevelUp->queryID; queries.addQuery(commanderLevelUp); sendAndApply(&clu); @@ -1434,7 +1434,6 @@ CGameHandler::CGameHandler(void) applier = new CApplier; registerTypesServerPacks(*applier); visitObjectAfterVictory = false; - queries.gh = this; spellEnv = new ServerSpellCastEnvironment(this); } @@ -2140,7 +2139,7 @@ bool CGameHandler::moveHero(ObjectInstanceID hid, int3 dst, ui8 teleporting, boo { LOG_TRACE_PARAMS(logGlobal, "Hero %s starts movement from %s to %s", h->name % tmh.start % tmh.end); - auto moveQuery = std::make_shared(tmh, h); + auto moveQuery = std::make_shared(this, tmh, h); queries.addQuery(moveQuery); if (leavingTile == LEAVING_TILE) @@ -2313,7 +2312,7 @@ void CGameHandler::setOwner(const CGObjectInstance * obj, PlayerColor owner) void CGameHandler::showBlockingDialog(BlockingDialog *iw) { - auto dialogQuery = std::make_shared(*iw); + auto dialogQuery = std::make_shared(this, *iw); queries.addQuery(dialogQuery); iw->queryID = dialogQuery->queryID; sendToAllClients(iw); @@ -2321,7 +2320,7 @@ void CGameHandler::showBlockingDialog(BlockingDialog *iw) void CGameHandler::showTeleportDialog(TeleportDialog *iw) { - auto dialogQuery = std::make_shared(*iw); + auto dialogQuery = std::make_shared(this, *iw); queries.addQuery(dialogQuery); iw->queryID = dialogQuery->queryID; sendToAllClients(iw); @@ -2449,7 +2448,7 @@ void CGameHandler::startBattlePrimary(const CArmedInstance *army1, const CArmedI setupBattle(tile, armies, heroes, creatureBank, town); //initializes stacks, places creatures on battlefield, blocks and informs player interfaces - auto battleQuery = std::make_shared(gs->curB); + auto battleQuery = std::make_shared(this, gs->curB); queries.addQuery(battleQuery); boost::thread(&CGameHandler::runBattle, this); @@ -2616,7 +2615,7 @@ void CGameHandler::heroExchange(ObjectInstanceID hero1, ObjectInstanceID hero2) if (getPlayerRelations(h1->getOwner(), h2->getOwner())) { - auto exchange = std::make_shared(h1, h2); + auto exchange = std::make_shared(this, h1, h2); ExchangeDialog hex; hex.queryID = exchange->queryID; hex.heroes[0] = getHero(hero1); @@ -3731,20 +3730,19 @@ bool CGameHandler::hireHero(const CGObjectInstance *obj, ui8 hid, PlayerColor pl return true; } -bool CGameHandler::queryReply(QueryID qid, ui32 answer, PlayerColor player) +bool CGameHandler::queryReply(QueryID qid, const JsonNode & answer, PlayerColor player) { boost::unique_lock lock(gsm); - logGlobal->trace("Player %s attempts answering query %d with answer %d", player, qid, answer); + logGlobal->trace("Player %s attempts answering query %d with answer:", player, qid); + logGlobal->traceStream() << answer; auto topQuery = queries.topQuery(player); COMPLAIN_RET_FALSE_IF(!topQuery, "This player doesn't have any queries!"); COMPLAIN_RET_FALSE_IF(topQuery->queryID != qid, "This player top query has different ID!"); COMPLAIN_RET_FALSE_IF(!topQuery->endsByPlayerAnswer(), "This query cannot be ended by player's answer!"); - if (auto dialogQuery = std::dynamic_pointer_cast(topQuery)) - dialogQuery->answer = answer; - + topQuery->setReply(answer); queries.popQuery(topQuery); return true; } @@ -4849,7 +4847,7 @@ void CGameHandler::showGarrisonDialog(ObjectInstanceID upobj, ObjectInstanceID h assert(lowerArmy); assert(upperArmy); - auto garrisonQuery = std::make_shared(upperArmy, lowerArmy); + auto garrisonQuery = std::make_shared(this, upperArmy, lowerArmy); queries.addQuery(garrisonQuery); GarrisonDialog gd; @@ -4920,7 +4918,7 @@ bool CGameHandler::isAllowedExchange(ObjectInstanceID id1, ObjectInstanceID id2) void CGameHandler::objectVisited(const CGObjectInstance * obj, const CGHeroInstance * h) { logGlobal->debug("%s visits %s (%d:%d)", h->nodeName(), obj->getObjectName(), obj->ID, obj->subID); - auto visitQuery = std::make_shared(obj, h, obj->visitablePos()); + auto visitQuery = std::make_shared(this, obj, h, obj->visitablePos()); queries.addQuery(visitQuery); //TODO real visit pos HeroVisit hv; @@ -5405,17 +5403,6 @@ void CGameHandler::handleAfterAttackCasting(const BattleAttack & bat) } } -bool CGameHandler::castSpell(const CGHeroInstance *h, SpellID spellID, const int3 &pos) -{ - const CSpell *s = spellID.toSpell(); - - AdventureSpellCastParameters p; - p.caster = h; - p.pos = pos; - - return s->adventureCast(spellEnv, p); -} - void CGameHandler::visitObjectOnTile(const TerrainTile &t, const CGHeroInstance * h) { if (!t.visitableObjects.empty()) diff --git a/server/CGameHandler.h b/server/CGameHandler.h index 387c363eb..b184d627a 100644 --- a/server/CGameHandler.h +++ b/server/CGameHandler.h @@ -31,7 +31,7 @@ struct NewStructures; class CGHeroInstance; class IMarket; -class ServerSpellCastEnvironment; +class SpellCastEnvironment; struct PlayerStatus { @@ -92,6 +92,8 @@ public: ui32 QID; Queries queries; + SpellCastEnvironment * spellEnv; + bool isValidObject(const CGObjectInstance *obj) const; bool isBlockedByQueries(const CPack *pack, PlayerColor player); bool isAllowedExchange(ObjectInstanceID id1, ObjectInstanceID id2); @@ -199,7 +201,7 @@ public: void stackTurnTrigger(const CStack *stack); void handleDamageFromObstacle(const CObstacleInstance &obstacle, const CStack * curStack); //checks if obstacle is land mine and handles possible consequences void removeObstacle(const CObstacleInstance &obstacle); - bool queryReply( QueryID qid, ui32 answer, PlayerColor player ); + bool queryReply( QueryID qid, const JsonNode & answer, PlayerColor player ); bool hireHero( const CGObjectInstance *obj, ui8 hid, PlayerColor player ); bool buildBoat( ObjectInstanceID objid ); bool setFormation( ObjectInstanceID hid, ui8 formation ); @@ -231,7 +233,6 @@ public: void objectVisitEnded(const CObjectVisitQuery &query); void engageIntoBattle( PlayerColor player ); bool dig(const CGHeroInstance *h); - bool castSpell(const CGHeroInstance *h, SpellID spellID, const int3 &pos); void moveArmy(const CArmedInstance *src, const CArmedInstance *dst, bool allowMerging); template void serialize(Handler &h, const int version) @@ -257,7 +258,6 @@ public: FinishingBattleHelper(); FinishingBattleHelper(std::shared_ptr Query, int RemainingBattleQueriesCount); - //std::shared_ptr query; const CGHeroInstance *winnerHero, *loserHero; PlayerColor victor, loser; @@ -265,7 +265,7 @@ public: template void serialize(Handler &h, const int version) { - h & /*query & */winnerHero & loserHero & victor & loser; + h & winnerHero & loserHero & victor & loser; if(version < 774 && !h.saving) { bool duel; @@ -292,8 +292,6 @@ public: CRandomGenerator & getRandomGenerator(); private: - ServerSpellCastEnvironment * spellEnv; - std::list generatePlayerTurnOrder() const; void makeStackDoNothing(const CStack * next); void getVictoryLossMessage(PlayerColor player, const EVictoryLossCheckResult & victoryLossCheckResult, InfoWindow & out) const; diff --git a/server/CQuery.cpp b/server/CQuery.cpp index f9a35e071..f72991aa8 100644 --- a/server/CQuery.cpp +++ b/server/CQuery.cpp @@ -3,6 +3,7 @@ #include "CGameHandler.h" #include "../lib/battle/BattleInfo.h" #include "../lib/mapObjects/MiscObjects.h" +#include "../lib/spells/ISpellMechanics.h" boost::mutex Queries::mx; @@ -34,7 +35,8 @@ std::ostream & operator<<(std::ostream &out, QueryPtr query) return out << "[" << query.get() << "] " << query->toString(); } -CQuery::CQuery(void) +CQuery::CQuery(Queries * Owner): + owner(Owner) { boost::unique_lock l(Queries::mx); @@ -69,7 +71,7 @@ bool CQuery::endsByPlayerAnswer() const return false; } -void CQuery::onRemoval(CGameHandler *gh, PlayerColor color) +void CQuery::onRemoval(PlayerColor color) { } @@ -82,18 +84,45 @@ void CQuery::notifyObjectAboutRemoval(const CObjectVisitQuery &objectVisit) cons { } -void CQuery::onExposure(CGameHandler *gh, QueryPtr topQuery) +void CQuery::onExposure(QueryPtr topQuery) { - gh->queries.popQuery(*this); + owner->popQuery(*this); } -void CQuery::onAdding(CGameHandler *gh, PlayerColor color) +void CQuery::onAdding(PlayerColor color) { } -CObjectVisitQuery::CObjectVisitQuery(const CGObjectInstance *Obj, const CGHeroInstance *Hero, int3 Tile) - : visitedObject(Obj), visitingHero(Hero), tile(Tile), removeObjectAfterVisit(false) +void CQuery::onAdded(PlayerColor color) +{ + +} + +void CQuery::setReply(const JsonNode & reply) +{ + +} + +bool CQuery::blockAllButReply(const CPack * pack) const +{ + //We accept only query replies from correct player + if(auto reply = dynamic_ptr_cast(pack)) + { + return !vstd::contains(players, reply->player); + } + + return true; +} + +CGhQuery::CGhQuery(CGameHandler * owner): + CQuery(&owner->queries), gh(owner) +{ + +} + +CObjectVisitQuery::CObjectVisitQuery(CGameHandler * owner, const CGObjectInstance * Obj, const CGHeroInstance * Hero, int3 Tile): + CGhQuery(owner), visitedObject(Obj), visitingHero(Hero), tile(Tile), removeObjectAfterVisit(false) { addPlayer(Hero->tempOwner); } @@ -105,7 +134,7 @@ bool CObjectVisitQuery::blocksPack(const CPack *pack) const return true; } -void CObjectVisitQuery::onRemoval(CGameHandler *gh, PlayerColor color) +void CObjectVisitQuery::onRemoval(PlayerColor color) { gh->objectVisitEnded(*this); @@ -115,13 +144,13 @@ void CObjectVisitQuery::onRemoval(CGameHandler *gh, PlayerColor color) gh->removeObject(visitedObject); } -void CObjectVisitQuery::onExposure(CGameHandler *gh, QueryPtr topQuery) +void CObjectVisitQuery::onExposure(QueryPtr topQuery) { //Object may have been removed and deleted. if(gh->isValidObject(visitedObject)) topQuery->notifyObjectAboutRemoval(*this); - gh->queries.popQuery(*this); + owner->popQuery(*this); } void Queries::popQuery(PlayerColor player, QueryPtr query) @@ -136,12 +165,12 @@ void Queries::popQuery(PlayerColor player, QueryPtr query) queries[player] -= query; auto nextQuery = topQuery(player); - query->onRemoval(gh, player); + query->onRemoval(player); //Exposure on query below happens only if removal didn't trigger any new query if(nextQuery && nextQuery == topQuery(player)) { - nextQuery->onExposure(gh, query); + nextQuery->onExposure(query); } } @@ -170,12 +199,15 @@ void Queries::addQuery(QueryPtr query) { for(auto player : query->players) addQuery(player, query); + + for(auto player : query->players) + query->onAdded(player); } void Queries::addQuery(PlayerColor player, QueryPtr query) { //LOG_TRACE_PARAMS(logGlobal, "player='%d', query='%s'", player.getNum() % query); - query->onAdding(gh, player); + query->onAdding(player); queries[player].push_back(query); } @@ -227,7 +259,8 @@ void CBattleQuery::notifyObjectAboutRemoval(const CObjectVisitQuery &objectVisit objectVisit.visitedObject->battleFinished(objectVisit.visitingHero, *result); } -CBattleQuery::CBattleQuery(const BattleInfo *Bi) +CBattleQuery::CBattleQuery(CGameHandler * owner, const BattleInfo *Bi): + CGhQuery(owner) { belligerents[0] = Bi->sides[0].armyObject; belligerents[1] = Bi->sides[1].armyObject; @@ -238,8 +271,8 @@ CBattleQuery::CBattleQuery(const BattleInfo *Bi) addPlayer(side.color); } -CBattleQuery::CBattleQuery() - :bi(nullptr) +CBattleQuery::CBattleQuery(CGameHandler * owner): + CGhQuery(owner), bi(nullptr) { belligerents[0] = belligerents[1] = nullptr; } @@ -250,7 +283,7 @@ bool CBattleQuery::blocksPack(const CPack *pack) const return strcmp(name, typeid(MakeAction).name()) && strcmp(name, typeid(MakeCustomAction).name()); } -void CBattleQuery::onRemoval(CGameHandler *gh, PlayerColor color) +void CBattleQuery::onRemoval(PlayerColor color) { gh->battleAfterLevelUp(*result); } @@ -260,7 +293,8 @@ void CGarrisonDialogQuery::notifyObjectAboutRemoval(const CObjectVisitQuery &obj objectVisit.visitedObject->garrisonDialogClosed(objectVisit.visitingHero); } -CGarrisonDialogQuery::CGarrisonDialogQuery(const CArmedInstance *up, const CArmedInstance *down) +CGarrisonDialogQuery::CGarrisonDialogQuery(CGameHandler * owner, const CArmedInstance * up, const CArmedInstance * down): + CDialogQuery(owner) { exchangingArmies[0] = up; exchangingArmies[1] = down; @@ -314,13 +348,14 @@ void CBlockingDialogQuery::notifyObjectAboutRemoval(const CObjectVisitQuery &obj objectVisit.visitedObject->blockingDialogAnswered(objectVisit.visitingHero, *answer); } -CBlockingDialogQuery::CBlockingDialogQuery(const BlockingDialog &bd) +CBlockingDialogQuery::CBlockingDialogQuery(CGameHandler * owner, const BlockingDialog & bd): + CDialogQuery(owner) { this->bd = bd; addPlayer(bd.player); } -void CTeleportDialogQuery::notifyObjectAboutRemoval(const CObjectVisitQuery &objectVisit) const +void CTeleportDialogQuery::notifyObjectAboutRemoval(const CObjectVisitQuery & objectVisit) const { // do not change to dynamic_ptr_cast - SIGSEGV! auto obj = dynamic_cast(objectVisit.visitedObject); @@ -330,19 +365,21 @@ void CTeleportDialogQuery::notifyObjectAboutRemoval(const CObjectVisitQuery &obj logGlobal->error("Invalid instance in teleport query"); } -CTeleportDialogQuery::CTeleportDialogQuery(const TeleportDialog &td) +CTeleportDialogQuery::CTeleportDialogQuery(CGameHandler * owner, const TeleportDialog & td): + CDialogQuery(owner) { this->td = td; addPlayer(td.hero->tempOwner); } -CHeroLevelUpDialogQuery::CHeroLevelUpDialogQuery(const HeroLevelUp &Hlu) +CHeroLevelUpDialogQuery::CHeroLevelUpDialogQuery(CGameHandler * owner, const HeroLevelUp & Hlu): + CDialogQuery(owner) { hlu = Hlu; addPlayer(hlu.hero->tempOwner); } -void CHeroLevelUpDialogQuery::onRemoval(CGameHandler *gh, PlayerColor color) +void CHeroLevelUpDialogQuery::onRemoval(PlayerColor color) { assert(answer); logGlobal->trace("Completing hero level-up query. %s gains skill %d", hlu.hero->getObjectName(), answer.get()); @@ -354,13 +391,14 @@ void CHeroLevelUpDialogQuery::notifyObjectAboutRemoval(const CObjectVisitQuery & objectVisit.visitedObject->heroLevelUpDone(objectVisit.visitingHero); } -CCommanderLevelUpDialogQuery::CCommanderLevelUpDialogQuery(const CommanderLevelUp &Clu) +CCommanderLevelUpDialogQuery::CCommanderLevelUpDialogQuery(CGameHandler * owner, const CommanderLevelUp & Clu): + CDialogQuery(owner) { clu = Clu; addPlayer(clu.hero->tempOwner); } -void CCommanderLevelUpDialogQuery::onRemoval(CGameHandler *gh, PlayerColor color) +void CCommanderLevelUpDialogQuery::onRemoval(PlayerColor color) { assert(answer); logGlobal->trace("Completing commander level-up query. Commander of hero %s gains skill %s", clu.hero->getObjectName(), answer.get()); @@ -372,29 +410,35 @@ void CCommanderLevelUpDialogQuery::notifyObjectAboutRemoval(const CObjectVisitQu objectVisit.visitedObject->heroLevelUpDone(objectVisit.visitingHero); } +CDialogQuery::CDialogQuery(CGameHandler * owner): + CGhQuery(owner) +{ + +} + bool CDialogQuery::endsByPlayerAnswer() const { return true; } -bool CDialogQuery::blocksPack(const CPack *pack) const +bool CDialogQuery::blocksPack(const CPack * pack) const { - //We accept only query replies from correct player - if(auto reply = dynamic_ptr_cast(pack)) - { - return !vstd::contains(players, reply->player); - } - - return true; + return blockAllButReply(pack); } -CHeroMovementQuery::CHeroMovementQuery(const TryMoveHero &Tmh, const CGHeroInstance *Hero, bool VisitDestAfterVictory) - : tmh(Tmh), visitDestAfterVictory(VisitDestAfterVictory), hero(Hero) +void CDialogQuery::setReply(const JsonNode & reply) +{ + if(reply.getType() == JsonNode::DATA_INTEGER) + answer = reply.Integer(); +} + +CHeroMovementQuery::CHeroMovementQuery(CGameHandler * owner, const TryMoveHero & Tmh, const CGHeroInstance * Hero, bool VisitDestAfterVictory): + CGhQuery(owner), tmh(Tmh), visitDestAfterVictory(VisitDestAfterVictory), hero(Hero) { players.push_back(hero->tempOwner); } -void CHeroMovementQuery::onExposure(CGameHandler *gh, QueryPtr topQuery) +void CHeroMovementQuery::onExposure(QueryPtr topQuery) { assert(players.size() == 1); @@ -407,10 +451,10 @@ void CHeroMovementQuery::onExposure(CGameHandler *gh, QueryPtr topQuery) gh->visitObjectOnTile(*gh->getTile(CGHeroInstance::convertPosition(tmh.end, false)), hero); } - gh->queries.popIfTop(*this); + owner->popIfTop(*this); } -void CHeroMovementQuery::onRemoval(CGameHandler *gh, PlayerColor color) +void CHeroMovementQuery::onRemoval(PlayerColor color) { PlayerBlocked pb; pb.player = color; @@ -419,7 +463,7 @@ void CHeroMovementQuery::onRemoval(CGameHandler *gh, PlayerColor color) gh->sendAndApply(&pb); } -void CHeroMovementQuery::onAdding(CGameHandler *gh, PlayerColor color) +void CHeroMovementQuery::onAdding(PlayerColor color) { PlayerBlocked pb; pb.player = color; @@ -427,3 +471,67 @@ void CHeroMovementQuery::onAdding(CGameHandler *gh, PlayerColor color) pb.startOrEnd = PlayerBlocked::BLOCKADE_STARTED; gh->sendAndApply(&pb); } + +CMapObjectSelectQuery::CMapObjectSelectQuery(Queries * Owner): + CQuery(Owner) +{ + +} + +bool CMapObjectSelectQuery::blocksPack(const CPack * pack) const +{ + return blockAllButReply(pack); +} + +bool CMapObjectSelectQuery::endsByPlayerAnswer() const +{ + return true; +} + +void CMapObjectSelectQuery::setReply(const JsonNode & reply) +{ + //TODO: +} + +CSpellQuery::CSpellQuery(Queries * Owner, const SpellCastEnvironment * SpellEnv): + CQuery(Owner), spellEnv(SpellEnv) +{ + +} + +AdventureSpellCastQuery::AdventureSpellCastQuery(Queries * Owner, const SpellCastEnvironment * SpellEnv, const CSpell * Spell, const CGHeroInstance * Caster, const int3 & Position): + CSpellQuery(Owner, SpellEnv), spell(Spell), caster(Caster), position(Position), requiresPositions(false) +{ + assert(owner); + assert(spellEnv); + assert(spell); + assert(caster); + + addPlayer(caster->getOwner()); +} + +bool AdventureSpellCastQuery::blocksPack(const CPack * pack) const +{ + return true; +} + +void AdventureSpellCastQuery::onAdded(PlayerColor color) +{ + //TODO: destination select request + +} + +void AdventureSpellCastQuery::onExposure(QueryPtr topQuery) +{ + CQuery::onExposure(topQuery); +} + +void AdventureSpellCastQuery::onRemoval(PlayerColor color) +{ + AdventureSpellCastParameters p; + p.caster = caster; + p.pos = position; + + spell->adventureCast(spellEnv, p); +} + diff --git a/server/CQuery.h b/server/CQuery.h index 5ba354591..9bd7b3bbe 100644 --- a/server/CQuery.h +++ b/server/CQuery.h @@ -9,53 +9,63 @@ class CArmedInstance; class CGameHandler; class CObjectVisitQuery; class CQuery; +class Queries; +class CSpell; +class SpellCastEnvironment; typedef std::shared_ptr QueryPtr; // This class represents any kind of prolonged interaction that may need to do something special after it is over. // It does not necessarily has to be "query" requiring player action, it can be also used internally within server. // Examples: -// - all kinds of blocking dialog windows -// - battle +// - all kinds of blocking dialog windows +// - battle // - object visit // - hero movement // Queries can cause another queries, forming a stack of queries for each player. Eg: hero movement -> object visit -> dialog. class CQuery { -protected: - void addPlayer(PlayerColor color); public: std::vector players; //players that are affected (often "blocked") by query QueryID queryID; - CQuery(void); + CQuery(Queries * Owner); virtual bool blocksPack(const CPack *pack) const; //query can block attempting actions by player. Eg. he can't move hero during the battle. virtual bool endsByPlayerAnswer() const; //query is removed after player gives answer (like dialogs) - virtual void onAdding(CGameHandler *gh, PlayerColor color); //called just before query is pushed on stack - virtual void onRemoval(CGameHandler *gh, PlayerColor color); //called after query is removed from stack - virtual void onExposure(CGameHandler *gh, QueryPtr topQuery);//called when query immediately above is removed and this is exposed (becomes top) + virtual void onAdding(PlayerColor color); //called just before query is pushed on stack + virtual void onAdded(PlayerColor color); //called right after query is pushed on stack + virtual void onRemoval(PlayerColor color); //called after query is removed from stack + virtual void onExposure(QueryPtr topQuery);//called when query immediately above is removed and this is exposed (becomes top) virtual std::string toString() const; virtual void notifyObjectAboutRemoval(const CObjectVisitQuery &objectVisit) const; + virtual void setReply(const JsonNode & reply); + virtual ~CQuery(void); - - - template void serialize(Handler &h, const int version) - { - h & players & queryID; - } +protected: + Queries * owner; + void addPlayer(PlayerColor color); + bool blockAllButReply(const CPack * pack) const; }; std::ostream &operator<<(std::ostream &out, const CQuery &query); std::ostream &operator<<(std::ostream &out, QueryPtr query); +class CGhQuery : public CQuery +{ +public: + CGhQuery(CGameHandler * owner); +protected: + CGameHandler * gh; +}; + //Created when hero visits object. //Removed when query above is resolved (or immediately after visit if no queries were created) -class CObjectVisitQuery : public CQuery +class CObjectVisitQuery : public CGhQuery { public: const CGObjectInstance *visitedObject; @@ -63,14 +73,14 @@ public: int3 tile; //may be different than hero pos -> eg. visit via teleport bool removeObjectAfterVisit; - CObjectVisitQuery(const CGObjectInstance *Obj, const CGHeroInstance *Hero, int3 Tile); + CObjectVisitQuery(CGameHandler * owner, const CGObjectInstance *Obj, const CGHeroInstance *Hero, int3 Tile); virtual bool blocksPack(const CPack *pack) const override; - virtual void onRemoval(CGameHandler *gh, PlayerColor color) override; - virtual void onExposure(CGameHandler *gh, QueryPtr topQuery) override; + virtual void onRemoval(PlayerColor color) override; + virtual void onExposure(QueryPtr topQuery) override; }; -class CBattleQuery : public CQuery +class CBattleQuery : public CGhQuery { public: std::array belligerents; @@ -78,35 +88,38 @@ public: const BattleInfo *bi; boost::optional result; - CBattleQuery(); - CBattleQuery(const BattleInfo *Bi); //TODO + CBattleQuery(CGameHandler * owner); + CBattleQuery(CGameHandler * owner, const BattleInfo * Bi); //TODO virtual void notifyObjectAboutRemoval(const CObjectVisitQuery &objectVisit) const override; virtual bool blocksPack(const CPack *pack) const override; - virtual void onRemoval(CGameHandler *gh, PlayerColor color) override; + virtual void onRemoval(PlayerColor color) override; }; //Created when hero attempts move and something happens //(not necessarily position change, could be just an object interaction). -class CHeroMovementQuery : public CQuery +class CHeroMovementQuery : public CGhQuery { public: TryMoveHero tmh; bool visitDestAfterVictory; //if hero moved to guarded tile and it should be visited once guard is defeated const CGHeroInstance *hero; - virtual void onExposure(CGameHandler *gh, QueryPtr topQuery) override; + virtual void onExposure(QueryPtr topQuery) override; - CHeroMovementQuery(const TryMoveHero &Tmh, const CGHeroInstance *Hero, bool VisitDestAfterVictory = false); - virtual void onAdding(CGameHandler *gh, PlayerColor color) override; - virtual void onRemoval(CGameHandler *gh, PlayerColor color) override; + CHeroMovementQuery(CGameHandler * owner, const TryMoveHero & Tmh, const CGHeroInstance * Hero, bool VisitDestAfterVictory = false); + virtual void onAdding(PlayerColor color) override; + virtual void onRemoval(PlayerColor color) override; }; -class CDialogQuery : public CQuery +class CDialogQuery : public CGhQuery { public: - boost::optional answer; + CDialogQuery(CGameHandler * owner); virtual bool endsByPlayerAnswer() const override; virtual bool blocksPack(const CPack *pack) const override; + void setReply(const JsonNode & reply) override; +protected: + boost::optional answer; }; class CGarrisonDialogQuery : public CDialogQuery //used also for hero exchange dialogs @@ -114,7 +127,7 @@ class CGarrisonDialogQuery : public CDialogQuery //used also for hero exchange d public: std::array exchangingArmies; - CGarrisonDialogQuery(const CArmedInstance *up, const CArmedInstance *down); + CGarrisonDialogQuery(CGameHandler * owner, const CArmedInstance *up, const CArmedInstance *down); virtual void notifyObjectAboutRemoval(const CObjectVisitQuery &objectVisit) const override; virtual bool blocksPack(const CPack *pack) const override; }; @@ -125,7 +138,7 @@ class CBlockingDialogQuery : public CDialogQuery public: BlockingDialog bd; //copy of pack... debug purposes - CBlockingDialogQuery(const BlockingDialog &bd); + CBlockingDialogQuery(CGameHandler * owner, const BlockingDialog &bd); virtual void notifyObjectAboutRemoval(const CObjectVisitQuery &objectVisit) const override; }; @@ -135,7 +148,7 @@ class CTeleportDialogQuery : public CDialogQuery public: TeleportDialog td; //copy of pack... debug purposes - CTeleportDialogQuery(const TeleportDialog &td); + CTeleportDialogQuery(CGameHandler * owner, const TeleportDialog &td); virtual void notifyObjectAboutRemoval(const CObjectVisitQuery &objectVisit) const override; }; @@ -143,27 +156,61 @@ public: class CHeroLevelUpDialogQuery : public CDialogQuery { public: - CHeroLevelUpDialogQuery(const HeroLevelUp &Hlu); + CHeroLevelUpDialogQuery(CGameHandler * owner, const HeroLevelUp &Hlu); - virtual void onRemoval(CGameHandler *gh, PlayerColor color) override; + virtual void onRemoval(PlayerColor color) override; virtual void notifyObjectAboutRemoval(const CObjectVisitQuery &objectVisit) const override; HeroLevelUp hlu; }; - class CCommanderLevelUpDialogQuery : public CDialogQuery { public: - CCommanderLevelUpDialogQuery(const CommanderLevelUp &Clu); + CCommanderLevelUpDialogQuery(CGameHandler * owner, const CommanderLevelUp &Clu); - virtual void onRemoval(CGameHandler *gh, PlayerColor color) override; + virtual void onRemoval(PlayerColor color) override; virtual void notifyObjectAboutRemoval(const CObjectVisitQuery &objectVisit) const override; CommanderLevelUp clu; }; -struct Queries +class CMapObjectSelectQuery : public CQuery +{ +public: + CMapObjectSelectQuery(Queries * Owner); + + bool blocksPack(const CPack * pack) const override; + bool endsByPlayerAnswer() const override; + void setReply(const JsonNode & reply) override; +}; + +class CSpellQuery : public CQuery +{ +public: + CSpellQuery(Queries * Owner, const SpellCastEnvironment * SpellEnv); +protected: + const SpellCastEnvironment * spellEnv; +}; + +class AdventureSpellCastQuery : public CSpellQuery +{ +public: + AdventureSpellCastQuery(Queries * Owner, const SpellCastEnvironment * SpellEnv, const CSpell * Spell, const CGHeroInstance * Caster, const int3 & Position); + + bool blocksPack(const CPack * pack) const override; + + void onAdded(PlayerColor color) override; + void onExposure(QueryPtr topQuery) override; + void onRemoval(PlayerColor color) override; + + const CSpell * spell; + const CGHeroInstance * caster; + int3 position; + bool requiresPositions; +}; + +class Queries { private: void addQuery(PlayerColor player, QueryPtr query); @@ -172,7 +219,6 @@ private: std::map> queries; //player => stack of queries public: - CGameHandler *gh; static boost::mutex mx; void addQuery(QueryPtr query); diff --git a/server/NetPacksServer.cpp b/server/NetPacksServer.cpp index c7b8c3b1b..40df1c33e 100644 --- a/server/NetPacksServer.cpp +++ b/server/NetPacksServer.cpp @@ -235,7 +235,7 @@ bool QueryReply::applyGh( CGameHandler *gh ) COMPLAIN_AND_RETURN("Cannot answer the query with id -1!"); assert(vstd::contains(gh->states.players, player)); - return gh->queryReply(qid, answer, player); + return gh->queryReply(qid, reply, player); } bool MakeAction::applyGh( CGameHandler *gh ) @@ -275,10 +275,22 @@ bool DigWithHero::applyGh( CGameHandler *gh ) return gh->dig(gh->getHero(id)); } -bool CastAdvSpell::applyGh( CGameHandler *gh ) +bool CastAdvSpell::applyGh(CGameHandler * gh) { ERROR_IF_NOT_OWNS(hid); - return gh->castSpell(gh->getHero(hid), sid, pos); + + const CSpell * s = sid.toSpell(); + if(!s) + ERROR_AND_RETURN; + const CGHeroInstance * h = gh->getHero(hid); + if(!h) + ERROR_AND_RETURN; + + auto query = std::make_shared(&gh->queries, gh->spellEnv, s, h, pos); + + gh->queries.addQuery(query); + gh->queries.popIfTop(query);//if we already can perform cast do it now + return true; } bool PlayerMessage::applyGh( CGameHandler *gh )