ソースを参照

Moved town portal logic to mechanics class

AlexVinS 8 年 前
コミット
a65befaa08

+ 1 - 3
client/CPlayerInterface.cpp

@@ -1208,8 +1208,6 @@ void CPlayerInterface::showMapObjectSelectDialog(QueryID askID, const Component
 		cb->sendQueryReply(reply, askID);
 	};
 
-	CComponent * localIcon = new CComponent(icon);
-
 	const std::string localTitle = title.toString();
 	const std::string localDescription = description.toString();
 
@@ -1219,7 +1217,7 @@ void CPlayerInterface::showMapObjectSelectDialog(QueryID askID, const Component
 	for(auto item : objects)
 		tempList.push_back(item.getNum());
 
-	CObjectListWindow * wnd = new CObjectListWindow(tempList, localIcon, localTitle, localDescription, selectCallback);
+	CObjectListWindow * wnd = new CObjectListWindow(tempList, icon, localTitle, localDescription, selectCallback);
 	wnd->onExit = cancelCallback;
 	GH.pushInt(wnd);
 }

+ 0 - 66
client/windows/CSpellWindow.cpp

@@ -641,78 +641,12 @@ void CSpellWindow::SpellArea::clickLeft(tribool down, bool previousState)
 				delete owner;
 			});
 
-			if(mySpell->id == SpellID::TOWN_PORTAL)
-			{
-				//special case
-				//todo: move to mechanics
-
-				std::vector <const CGTownInstance*> Towns = owner->myInt->cb->getTownsInfo(false);
-
-				vstd::erase_if(Towns, [this](const CGTownInstance * t)
-				{
-					const auto relations = owner->myInt->cb->getPlayerRelations(t->tempOwner, owner->myInt->playerID);
-					return relations == PlayerRelations::ENEMIES;
-				});
-
-				if (Towns.empty())
-				{
-					owner->myInt->showInfoDialog(CGI->generaltexth->allTexts[124]);
-					return;
-				}
-
-				const int movementCost = (h->getSpellSchoolLevel(mySpell) >= 3) ? 200 : 300;
-
-				if(h->movement < movementCost)
-				{
-					owner->myInt->showInfoDialog(CGI->generaltexth->allTexts[125]);
-					return;
-				}
-
-				if (h->getSpellSchoolLevel(mySpell) < 2) //not advanced or expert - teleport to nearest available city
-				{
-					owner->myInt->cb->castSpell(h, mySpell->id, int3());// - town->getVisitableOffset());
-				}
-				else
-				{ //let the player choose
-					std::vector <int> availableTowns;
-					for(auto & Town : Towns)
-					{
-						const CGTownInstance *t = Town;
-						if (t->visitingHero == nullptr) //empty town and this is
-						{
-							availableTowns.push_back(t->id.getNum());//add to the list
-						}
-					}
-
-					auto castTownPortal = [h](int townId)
-					{
-						const CGTownInstance * dest = LOCPLINT->cb->getTown(ObjectInstanceID(townId));
-						LOCPLINT->cb->castSpell(h, SpellID::TOWN_PORTAL, dest->visitablePos());
-					};
-
-					if (availableTowns.empty())
-						owner->myInt->showInfoDialog(CGI->generaltexth->allTexts[124]);
-					else
-						GH.pushInt (new CObjectListWindow(availableTowns,
-							new CAnimImage("SPELLSCR",mySpell->id),
-							CGI->generaltexth->jktexts[40], CGI->generaltexth->jktexts[41],
-							castTownPortal));
-				}
-				return;
-			}
-
 			if(mySpell->getTargetType() == CSpell::LOCATION)
-			{
 				adventureInt->enterCastingMode(mySpell);
-			}
 			else if(mySpell->getTargetType() == CSpell::NO_TARGET)
-			{
 				owner->myInt->cb->castSpell(h, mySpell->id);
-			}
 			else
-			{
 				logGlobal->error("Invalid spell target type");
-			}
 		}
 	}
 }

+ 20 - 0
client/windows/GUIClasses.cpp

@@ -1744,6 +1744,26 @@ CObjectListWindow::CObjectListWindow(const std::vector<int> &_items, CIntObject
 	init(titlePic, _title, _descr);
 }
 
+CObjectListWindow::CObjectListWindow(const std::vector<int> &_items, const Component & titlePic, std::string _title, std::string _descr,
+				std::function<void(int)> Callback):
+	CWindowObject(PLAYER_COLORED, "TPGATE"),
+	onSelect(Callback),
+	selected(0)
+{
+	items.reserve(_items.size());
+	for(int id : _items)
+	{
+		items.push_back(std::make_pair(id, CGI->mh->map->objects[id]->getObjectName()));
+	}
+
+	OBJ_CONSTRUCTION_CAPTURING_ALL;
+
+	CComponent * icon = new CComponent(titlePic);
+
+	init(icon, _title, _descr);
+}
+
+
 CObjectListWindow::CObjectListWindow(const std::vector<std::string> &_items, CIntObject * titlePic, std::string _title, std::string _descr,
 				std::function<void(int)> Callback):
 	CWindowObject(PLAYER_COLORED, "TPGATE"),

+ 5 - 0
client/windows/GUIClasses.h

@@ -33,6 +33,7 @@ class CToggleButton;
 class CToggleGroup;
 class CVolumeSlider;
 class CGStatusBar;
+struct Component;
 
 /// Recruitment window where you can recruit creatures
 class CRecruitmentWindow : public CWindowObject
@@ -177,6 +178,10 @@ public:
 	///item names will be taken from map objects
 	CObjectListWindow(const std::vector<int> &_items, CIntObject * titlePic, std::string _title, std::string _descr,
                       std::function<void(int)> Callback);
+
+	CObjectListWindow(const std::vector<int> &_items, const Component & titlePic, std::string _title, std::string _descr,
+                      std::function<void(int)> Callback);
+
 	CObjectListWindow(const std::vector<std::string> &_items, CIntObject * titlePic, std::string _title, std::string _descr,
                       std::function<void(int)> Callback);
 

+ 138 - 43
lib/spells/AdventureSpellMechanics.cpp

@@ -25,7 +25,7 @@ AdventureSpellMechanics::AdventureSpellMechanics(const CSpell * s):
 {
 }
 
-bool AdventureSpellMechanics::adventureCast(const SpellCastEnvironment * env, AdventureSpellCastParameters & parameters) const
+bool AdventureSpellMechanics::adventureCast(const SpellCastEnvironment * env, const AdventureSpellCastParameters & parameters) const
 {
 	if(!owner->isAdventureSpell())
 	{
@@ -55,29 +55,12 @@ bool AdventureSpellMechanics::adventureCast(const SpellCastEnvironment * env, Ad
 		return false;
 	}
 
-	{
-		AdvmapSpellCast asc;
-		asc.caster = caster;
-		asc.spellID = owner->id;
-		env->sendAndApply(&asc);
-	}
+	ESpellCastResult result = beginCast(env, parameters);
 
-	switch(applyAdventureEffects(env, parameters))
-	{
-	case ESpellCastResult::OK:
-		{
-			SetMana sm;
-			sm.hid = caster->id;
-			sm.absolute = false;
-			sm.val = -cost;
-			env->sendAndApply(&sm);
-			return true;
-		}
-		break;
-	case ESpellCastResult::CANCEL:
-		return true;
-	}
-	return false;
+	if(result == ESpellCastResult::OK)
+		performCast(env, parameters);
+
+	return result != ESpellCastResult::ERROR;
 }
 
 ESpellCastResult AdventureSpellMechanics::applyAdventureEffects(const SpellCastEnvironment * env, const AdventureSpellCastParameters & parameters) const
@@ -109,6 +92,42 @@ ESpellCastResult AdventureSpellMechanics::applyAdventureEffects(const SpellCastE
 	}
 }
 
+ESpellCastResult AdventureSpellMechanics::beginCast(const SpellCastEnvironment * env, const AdventureSpellCastParameters & parameters) const
+{
+    return ESpellCastResult::OK;
+}
+
+void AdventureSpellMechanics::performCast(const SpellCastEnvironment * env, const AdventureSpellCastParameters & parameters) const
+{
+	AdvmapSpellCast asc;
+	asc.caster = parameters.caster;
+	asc.spellID = owner->id;
+	env->sendAndApply(&asc);
+
+	ESpellCastResult result = applyAdventureEffects(env, parameters);
+	endCast(env, parameters, result);
+}
+
+void AdventureSpellMechanics::endCast(const SpellCastEnvironment * env, const AdventureSpellCastParameters & parameters, const ESpellCastResult result) const
+{
+	const int cost = parameters.caster->getSpellCost(owner);
+
+	switch(result)
+	{
+	case ESpellCastResult::OK:
+		{
+			SetMana sm;
+			sm.hid = parameters.caster->id;
+			sm.absolute = false;
+			sm.val = -cost;
+			env->sendAndApply(&sm);
+		}
+		break;
+	default:
+		break;
+	}
+}
+
 ///SummonBoatMechanics
 SummonBoatMechanics::SummonBoatMechanics(const CSpell * s):
 	AdventureSpellMechanics(s)
@@ -308,7 +327,7 @@ TownPortalMechanics::TownPortalMechanics(const CSpell * s):
 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);
+	const int moveCost = movementCost(parameters);
 
     if(parameters.caster->getSpellSchoolLevel(owner) < 2)
     {
@@ -316,24 +335,12 @@ ESpellCastResult TownPortalMechanics::applyAdventureEffects(const SpellCastEnvir
 		destination = findNearestTown(env, parameters, pool);
 
 		if(nullptr == destination)
-		{
-			InfoWindow iw;
-			iw.player = parameters.caster->tempOwner;
-			iw.text.addTxt(MetaString::GENERAL_TXT, 124);
-			env->sendAndApply(&iw);
-			return ESpellCastResult::CANCEL;
-		}
+			return ESpellCastResult::ERROR;
 
-		if(parameters.caster->movement < movementCost)
-		{
-			InfoWindow iw;
-			iw.player = parameters.caster->tempOwner;
-			iw.text.addTxt(MetaString::GENERAL_TXT, 125);
-			env->sendAndApply(&iw);
-			return ESpellCastResult::CANCEL;
-		}
+		if(parameters.caster->movement < moveCost)
+			return ESpellCastResult::ERROR;
 
-		if (destination->visitingHero)
+		if(destination->visitingHero)
 		{
 			InfoWindow iw;
 			iw.player = parameters.caster->tempOwner;
@@ -367,7 +374,7 @@ ESpellCastResult TownPortalMechanics::applyAdventureEffects(const SpellCastEnvir
 			return ESpellCastResult::ERROR;
 		}
 
-		if(parameters.caster->movement < movementCost)
+		if(parameters.caster->movement < moveCost)
 		{
 			env->complain("This hero has not enough movement points!");
 			return ESpellCastResult::ERROR;
@@ -385,16 +392,99 @@ ESpellCastResult TownPortalMechanics::applyAdventureEffects(const SpellCastEnvir
 		return ESpellCastResult::ERROR;
 	}
 
-	if(env->moveHero(parameters.caster->id, destination->visitablePos() + parameters.caster->getVisitableOffset(), 1))
+	if(env->moveHero(parameters.caster->id, destination->visitablePos() + parameters.caster->getVisitableOffset(), true))
 	{
 		SetMovePoints smp;
 		smp.hid = parameters.caster->id;
-		smp.val = std::max<ui32>(0, parameters.caster->movement - movementCost);
+		smp.val = std::max<ui32>(0, parameters.caster->movement - moveCost);
 		env->sendAndApply(&smp);
 	}
 	return ESpellCastResult::OK;
 }
 
+ESpellCastResult TownPortalMechanics::beginCast(const SpellCastEnvironment * env, const AdventureSpellCastParameters & parameters) const
+{
+	std::vector<const CGTownInstance *>	towns = getPossibleTowns(env, parameters);
+
+	if(towns.empty())
+	{
+		InfoWindow iw;
+		iw.player = parameters.caster->tempOwner;
+		iw.text.addTxt(MetaString::GENERAL_TXT, 124);
+		env->sendAndApply(&iw);
+		return ESpellCastResult::CANCEL;
+	}
+
+	const int moveCost = movementCost(parameters);
+
+	if(parameters.caster->movement < moveCost)
+	{
+		InfoWindow iw;
+		iw.player = parameters.caster->tempOwner;
+		iw.text.addTxt(MetaString::GENERAL_TXT, 125);
+		env->sendAndApply(&iw);
+		return ESpellCastResult::CANCEL;
+	}
+
+    if(!parameters.pos.valid() && parameters.caster->getSpellSchoolLevel(owner) >= 2)
+	{
+		auto queryCallback = [=](const JsonNode & reply) -> void
+		{
+			if(reply.getType() == JsonNode::DATA_INTEGER)
+			{
+				ObjectInstanceID townId(reply.Integer());
+
+				const CGObjectInstance * o = env->getCb()->getObj(townId, true);
+				if(o == nullptr)
+				{
+					env->complain("Invalid object instance selected");
+					return;
+				}
+
+				if(!dynamic_cast<const CGTownInstance *>(o))
+				{
+					env->complain("Object instance is not town");
+					return;
+				}
+
+				AdventureSpellCastParameters p;
+				p.caster = parameters.caster;
+				p.pos = o->visitablePos();
+				performCast(env, p);
+			}
+		};
+
+		MapObjectSelectDialog request;
+
+		for(auto t : towns)
+		{
+			if (t->visitingHero == nullptr) //empty town
+				request.objects.push_back(t->id);
+		}
+
+		if(request.objects.empty())
+		{
+			InfoWindow iw;
+			iw.player = parameters.caster->tempOwner;
+			iw.text.addTxt(MetaString::GENERAL_TXT, 124);
+			env->sendAndApply(&iw);
+			return ESpellCastResult::CANCEL;
+		}
+
+		request.player = parameters.caster->getOwner();
+        request.title.addTxt(MetaString::JK_TXT, 40);
+        request.description.addTxt(MetaString::JK_TXT, 41);
+        request.icon.id = Component::SPELL;
+        request.icon.subtype = owner->id.toEnum();
+
+		env->genericQuery(&request, request.player, queryCallback);
+
+		return ESpellCastResult::PENDING;
+	}
+
+	return ESpellCastResult::OK;
+}
+
 const CGTownInstance * TownPortalMechanics::findNearestTown(const SpellCastEnvironment * env, const AdventureSpellCastParameters & parameters, const std::vector <const CGTownInstance *> & pool) const
 {
 	if(pool.empty())
@@ -432,6 +522,11 @@ std::vector <const CGTownInstance*> TownPortalMechanics::getPossibleTowns(const
 	return ret;
 }
 
+int TownPortalMechanics::movementCost(const AdventureSpellCastParameters & parameters) const
+{
+	return GameConstants::BASE_MOVEMENT_COST * ((parameters.caster->getSpellSchoolLevel(owner) >= 3) ? 2 : 3);
+}
+
 ///ViewMechanics
 ViewMechanics::ViewMechanics(const CSpell * s):
 	AdventureSpellMechanics(s)

+ 7 - 1
lib/spells/AdventureSpellMechanics.h

@@ -18,6 +18,7 @@ enum class ESpellCastResult
 {
 	OK,
 	CANCEL,//cast failed but it is not an error
+	PENDING,
 	ERROR//internal error occurred
 };
 
@@ -26,10 +27,13 @@ class DLL_LINKAGE AdventureSpellMechanics : public IAdventureSpellMechanics
 public:
 	AdventureSpellMechanics(const CSpell * s);
 
-	bool adventureCast(const SpellCastEnvironment * env, AdventureSpellCastParameters & parameters) const override final;
+	bool adventureCast(const SpellCastEnvironment * env, const AdventureSpellCastParameters & parameters) const override final;
 protected:
 	///actual adventure cast implementation
 	virtual ESpellCastResult applyAdventureEffects(const SpellCastEnvironment * env, const AdventureSpellCastParameters & parameters) const;
+	virtual ESpellCastResult beginCast(const SpellCastEnvironment * env, const AdventureSpellCastParameters & parameters) const;
+	void performCast(const SpellCastEnvironment * env, const AdventureSpellCastParameters & parameters) const;
+	void endCast(const SpellCastEnvironment * env, const AdventureSpellCastParameters & parameters, const ESpellCastResult result) const;
 };
 
 class DLL_LINKAGE SummonBoatMechanics : public AdventureSpellMechanics
@@ -62,8 +66,10 @@ public:
 	TownPortalMechanics(const CSpell * s);
 protected:
 	ESpellCastResult applyAdventureEffects(const SpellCastEnvironment * env, const AdventureSpellCastParameters & parameters) const override;
+	ESpellCastResult beginCast(const SpellCastEnvironment * env, const AdventureSpellCastParameters & parameters) const override;
 private:
 	const CGTownInstance * findNearestTown(const SpellCastEnvironment * env, const AdventureSpellCastParameters & parameters, const std::vector <const CGTownInstance*> & pool) const;
+	int movementCost(const AdventureSpellCastParameters & parameters) const;
 	std::vector <const CGTownInstance*> getPossibleTowns(const SpellCastEnvironment * env, const AdventureSpellCastParameters & parameters) const;
 };
 

+ 1 - 1
lib/spells/CSpellHandler.cpp

@@ -118,7 +118,7 @@ void CSpell::applyBattle(BattleInfo * battle, const BattleSpellCast * packet) co
 	mechanics->applyBattle(battle, packet);
 }
 
-bool CSpell::adventureCast(const SpellCastEnvironment * env, AdventureSpellCastParameters & parameters) const
+bool CSpell::adventureCast(const SpellCastEnvironment * env, const AdventureSpellCastParameters & parameters) const
 {
 	assert(env);
 

+ 1 - 1
lib/spells/CSpellHandler.h

@@ -306,7 +306,7 @@ public:
 	///Server logic. Has write access to GameState via packets.
 	///May be executed on client side by (future) non-cheat-proof scripts.
 
-	bool adventureCast(const SpellCastEnvironment * env, AdventureSpellCastParameters & parameters) const;
+	bool adventureCast(const SpellCastEnvironment * env, const AdventureSpellCastParameters & parameters) const;
 	void battleCast(const SpellCastEnvironment * env, const BattleSpellCastParameters & parameters) const;
 
 public:

+ 6 - 2
lib/spells/ISpellMechanics.h

@@ -13,6 +13,8 @@
 #include "CSpellHandler.h"
 #include "../battle/BattleHex.h"
 
+struct Query;
+
 ///callback to be provided by server
 class DLL_LINKAGE SpellCastEnvironment
 {
@@ -26,7 +28,9 @@ public:
 	virtual const CMap * getMap() const = 0;
 	virtual const CGameInfoCallback * getCb() const = 0;
 
-	virtual bool moveHero(ObjectInstanceID hid, int3 dst, ui8 teleporting, PlayerColor asker = PlayerColor::NEUTRAL) const =0;	//TODO: remove
+	virtual bool moveHero(ObjectInstanceID hid, int3 dst, bool teleporting) const = 0;	//TODO: remove
+
+	virtual void genericQuery(Query * request, PlayerColor color, std::function<void(const JsonNode &)> callback) const = 0;//TODO: type safety on query, use generic query packet when implemented
 };
 
 ///all parameters of particular cast event
@@ -140,7 +144,7 @@ public:
 	IAdventureSpellMechanics(const CSpell * s);
 	virtual ~IAdventureSpellMechanics() = default;
 
-	virtual bool adventureCast(const SpellCastEnvironment * env, AdventureSpellCastParameters & parameters) const = 0;
+	virtual bool adventureCast(const SpellCastEnvironment * env, const AdventureSpellCastParameters & parameters) const = 0;
 
 	static std::unique_ptr<IAdventureSpellMechanics> createMechanics(const CSpell * s);
 protected:

+ 12 - 4
server/CGameHandler.cpp

@@ -71,7 +71,8 @@ public:
 	void complain(const std::string & problem) const override;
 	const CMap * getMap() const override;
 	const CGameInfoCallback * getCb() const override;
-	bool moveHero(ObjectInstanceID hid, int3 dst, ui8 teleporting, PlayerColor asker = PlayerColor::NEUTRAL) const override;
+	bool moveHero(ObjectInstanceID hid, int3 dst, bool teleporting) const override;
+	void genericQuery(Query * request, PlayerColor color, std::function<void(const JsonNode &)> callback) const override;
 private:
 	mutable CGameHandler * gh;
 };
@@ -6516,13 +6517,20 @@ const CGameInfoCallback * ServerSpellCastEnvironment::getCb() const
 	return gh;
 }
 
-
 const CMap * ServerSpellCastEnvironment::getMap() const
 {
 	return gh->gameState()->map;
 }
 
-bool ServerSpellCastEnvironment::moveHero(ObjectInstanceID hid, int3 dst, ui8 teleporting, PlayerColor asker) const
+bool ServerSpellCastEnvironment::moveHero(ObjectInstanceID hid, int3 dst, bool teleporting) const
+{
+	return gh->moveHero(hid, dst, teleporting, false);
+}
+
+void ServerSpellCastEnvironment::genericQuery(Query * request, PlayerColor color, std::function<void(const JsonNode&)> callback) const
 {
-	return gh->moveHero(hid, dst, teleporting, false, asker);
+	auto query = std::make_shared<CGenericQuery>(&gh->queries, color, callback);
+	request->queryID = query->queryID;
+	gh->queries.addQuery(query);
+	gh->sendAndApply(request);
 }

+ 10 - 47
server/CQuery.cpp

@@ -3,7 +3,6 @@
 #include "CGameHandler.h"
 #include "../lib/battle/BattleInfo.h"
 #include "../lib/mapObjects/MiscObjects.h"
-#include "../lib/spells/ISpellMechanics.h"
 
 boost::mutex Queries::mx;
 
@@ -86,6 +85,7 @@ void CQuery::notifyObjectAboutRemoval(const CObjectVisitQuery &objectVisit) cons
 
 void CQuery::onExposure(QueryPtr topQuery)
 {
+	logGlobal->trace("Exposed query with id %d", queryID);
 	owner->popQuery(*this);
 }
 
@@ -472,66 +472,29 @@ void CHeroMovementQuery::onAdding(PlayerColor color)
 	gh->sendAndApply(&pb);
 }
 
-CMapObjectSelectQuery::CMapObjectSelectQuery(Queries * Owner):
-	CQuery(Owner)
+CGenericQuery::CGenericQuery(Queries * Owner, PlayerColor color, std::function<void(const JsonNode &)> Callback):
+	CQuery(Owner), callback(Callback)
 {
-
+	addPlayer(color);
 }
 
-bool CMapObjectSelectQuery::blocksPack(const CPack * pack) const
+bool CGenericQuery::blocksPack(const CPack * pack) const
 {
 	return blockAllButReply(pack);
 }
 
-bool CMapObjectSelectQuery::endsByPlayerAnswer() const
+bool CGenericQuery::endsByPlayerAnswer() const
 {
 	return true;
 }
 
-void CMapObjectSelectQuery::setReply(const JsonNode & reply)
-{
-	//TODO:
-}
-
-CSpellQuery::CSpellQuery(Queries * Owner, const SpellCastEnvironment * SpellEnv):
-	CQuery(Owner), spellEnv(SpellEnv)
+void CGenericQuery::onExposure(QueryPtr topQuery)
 {
-
+	//do nothing
 }
 
-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)
+void CGenericQuery::setReply(const JsonNode & reply)
 {
-	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);
+	callback(reply);
 }
 

+ 5 - 27
server/CQuery.h

@@ -175,39 +175,17 @@ public:
 	CommanderLevelUp clu;
 };
 
-class CMapObjectSelectQuery : public CQuery
+class CGenericQuery : public CQuery
 {
 public:
-	CMapObjectSelectQuery(Queries * Owner);
+	CGenericQuery(Queries * Owner, PlayerColor color, std::function<void(const JsonNode &)> Callback);
 
 	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;
+	void setReply(const JsonNode & reply) override;
+private:
+	std::function<void(const JsonNode &)> callback;
 };
 
 class Queries

+ 6 - 4
server/NetPacksServer.cpp

@@ -9,6 +9,8 @@
 #include "../lib/battle/BattleInfo.h"
 #include "../lib/battle/BattleAction.h"
 #include "../lib/serializer/Connection.h"
+#include "../lib/spells/CSpellHandler.h"
+#include "../lib/spells/ISpellMechanics.h"
 
 
 #define PLAYER_OWNS(id) (gh->getPlayerAt(c)==gh->getOwner(id))
@@ -286,11 +288,11 @@ bool CastAdvSpell::applyGh(CGameHandler * gh)
 	if(!h)
 		ERROR_AND_RETURN;
 
-	auto query = std::make_shared<AdventureSpellCastQuery>(&gh->queries, gh->spellEnv, s, h, pos);
+	AdventureSpellCastParameters p;
+	p.caster = h;
+	p.pos = pos;
 
-	gh->queries.addQuery(query);
-	gh->queries.popIfTop(query);//if we already can perform cast do it now
-	return true;
+	return s->adventureCast(gh->spellEnv, p);
 }
 
 bool PlayerMessage::applyGh( CGameHandler *gh )