mirror of
				https://github.com/vcmi/vcmi.git
				synced 2025-10-31 00:07:39 +02:00 
			
		
		
		
	Cleanup
This commit is contained in:
		| @@ -88,7 +88,7 @@ public: | ||||
| 	//void battleResultsApplied() override; //called when all effects of last battle are applied | ||||
| 	//void battleNewRoundFirst(int round) override; //called at the beginning of each turn before changes are applied; | ||||
| 	//void battleNewRound(int round) override; //called at the beginning of each turn, round=-1 is the tactic phase, round=0 is the first "normal" turn | ||||
| 	//void battleStackMoved(const CStack * stack, std::vector<BattleHex> dest, int distance) override; | ||||
| 	//void battleStackMoved(const CStack * stack, BattleHexArray dest, int distance) override; | ||||
| 	//void battleSpellCast(const BattleSpellCast *sc) override; | ||||
| 	//void battleStacksEffectsSet(const SetStackEffect & sse) override;//called when a specific effect is set to stacks | ||||
| 	//void battleTriggerEffect(const BattleTriggerEffect & bte) override; | ||||
|   | ||||
| @@ -1,49 +1,49 @@ | ||||
| /* | ||||
| * ExplorationHelper.h, part of VCMI engine | ||||
| * | ||||
| * Authors: listed in file AUTHORS in main folder | ||||
| * | ||||
| * License: GNU General Public License v2.0 or later | ||||
| * Full text of license available in license.txt file, in main folder | ||||
| * | ||||
| */ | ||||
| #pragma once | ||||
|  | ||||
| #include "../AIUtility.h" | ||||
|  | ||||
| #include "../../../lib/GameConstants.h" | ||||
| #include "../../../lib/VCMI_Lib.h" | ||||
| #include "../Goals/AbstractGoal.h" | ||||
|  | ||||
| namespace NKAI | ||||
| { | ||||
|  | ||||
| class ExplorationHelper | ||||
| { | ||||
| private: | ||||
| 	const CGHeroInstance * hero; | ||||
| 	int sightRadius; | ||||
| 	float bestValue; | ||||
| 	Goals::TSubgoal bestGoal; | ||||
| 	int3 bestTile; | ||||
| 	int bestTilesDiscovered; | ||||
| 	const Nullkiller * ai; | ||||
| 	CCallback * cbp; | ||||
| 	const TeamState * ts; | ||||
| 	int3 ourPos; | ||||
| 	bool allowDeadEndCancellation; | ||||
| 	bool useCPathfinderAccessibility; | ||||
|  | ||||
| public: | ||||
| 	ExplorationHelper(const CGHeroInstance * hero, const Nullkiller * ai, bool useCPathfinderAccessibility = false); | ||||
| 	Goals::TSubgoal makeComposition() const; | ||||
| 	bool scanSector(int scanRadius); | ||||
| 	bool scanMap(); | ||||
| 	int howManyTilesWillBeDiscovered(const int3 & pos) const; | ||||
|  | ||||
| private: | ||||
| 	void scanTile(const int3 & tile); | ||||
| 	bool hasReachableneighbour(const int3 & pos) const; | ||||
| }; | ||||
|  | ||||
| } | ||||
| /* | ||||
| * ExplorationHelper.h, part of VCMI engine | ||||
| * | ||||
| * Authors: listed in file AUTHORS in main folder | ||||
| * | ||||
| * License: GNU General Public License v2.0 or later | ||||
| * Full text of license available in license.txt file, in main folder | ||||
| * | ||||
| */ | ||||
| #pragma once | ||||
|  | ||||
| #include "../AIUtility.h" | ||||
|  | ||||
| #include "../../../lib/GameConstants.h" | ||||
| #include "../../../lib/VCMI_Lib.h" | ||||
| #include "../Goals/AbstractGoal.h" | ||||
|  | ||||
| namespace NKAI | ||||
| { | ||||
|  | ||||
| class ExplorationHelper | ||||
| { | ||||
| private: | ||||
| 	const CGHeroInstance * hero; | ||||
| 	int sightRadius; | ||||
| 	float bestValue; | ||||
| 	Goals::TSubgoal bestGoal; | ||||
| 	int3 bestTile; | ||||
| 	int bestTilesDiscovered; | ||||
| 	const Nullkiller * ai; | ||||
| 	CCallback * cbp; | ||||
| 	const TeamState * ts; | ||||
| 	int3 ourPos; | ||||
| 	bool allowDeadEndCancellation; | ||||
| 	bool useCPathfinderAccessibility; | ||||
|  | ||||
| public: | ||||
| 	ExplorationHelper(const CGHeroInstance * hero, const Nullkiller * ai, bool useCPathfinderAccessibility = false); | ||||
| 	Goals::TSubgoal makeComposition() const; | ||||
| 	bool scanSector(int scanRadius); | ||||
| 	bool scanMap(); | ||||
| 	int howManyTilesWillBeDiscovered(const int3 & pos) const; | ||||
|  | ||||
| private: | ||||
| 	void scanTile(const int3 & tile); | ||||
| 	bool hasReachableNeighbor(const int3 & pos) const; | ||||
| }; | ||||
|  | ||||
| } | ||||
|   | ||||
| @@ -399,7 +399,7 @@ void AINodeStorage::calculateNeighbours( | ||||
|  | ||||
| #if NKAI_PATHFINDER_TRACE_LEVEL >= 2 | ||||
| 		logAi->trace( | ||||
| 			"Node %s added to neighbours of %s, layer %d", | ||||
| 			"Node %s added to neighbors of %s, layer %d", | ||||
| 			neighbour.toString(), | ||||
| 			source.coord.toString(), | ||||
| 			static_cast<int32_t>(layer)); | ||||
|   | ||||
| @@ -56,9 +56,9 @@ void ObjectGraphCalculator::calculateConnections() | ||||
| 	removeExtraConnections(); | ||||
| } | ||||
|  | ||||
| float ObjectGraphCalculator::getneighbourConnectionsCost(const int3 & pos, std::vector<AIPath> & pathCache) | ||||
| float ObjectGraphCalculator::getNeighborConnectionsCost(const int3 & pos, std::vector<AIPath> & pathCache) | ||||
| { | ||||
| 	float neighbourCost = std::numeric_limits<float>::max(); | ||||
| 	float neighborCost = std::numeric_limits<float>::max(); | ||||
|  | ||||
| 	if(NKAI_GRAPH_TRACE_LEVEL >= 2) | ||||
| 	{ | ||||
| @@ -68,24 +68,24 @@ float ObjectGraphCalculator::getneighbourConnectionsCost(const int3 & pos, std:: | ||||
| 	foreach_neighbour( | ||||
| 		ai->cb.get(), | ||||
| 		pos, | ||||
| 		[this, &neighbourCost, &pathCache](const CPlayerSpecificInfoCallback * cb, const int3 & neighbour) | ||||
| 		[this, &neighborCost, &pathCache](const CPlayerSpecificInfoCallback * cb, const int3 & neighbor) | ||||
| 		{ | ||||
| 			ai->pathfinder->calculatePathInfo(pathCache, neighbour); | ||||
| 			ai->pathfinder->calculatePathInfo(pathCache, neighbor); | ||||
|  | ||||
| 			auto costTotal = this->getConnectionsCost(pathCache); | ||||
|  | ||||
| 			if(costTotal.connectionsCount > 2 && costTotal.avg < neighbourCost) | ||||
| 			if(costTotal.connectionsCount > 2 && costTotal.avg < neighborCost) | ||||
| 			{ | ||||
| 				neighbourCost = costTotal.avg; | ||||
| 				neighborCost = costTotal.avg; | ||||
|  | ||||
| 				if(NKAI_GRAPH_TRACE_LEVEL >= 2) | ||||
| 				{ | ||||
| 					logAi->trace("Better node found at %s", neighbour.toString()); | ||||
| 					logAi->trace("Better node found at %s", neighbor.toString()); | ||||
| 				} | ||||
| 			} | ||||
| 		}); | ||||
|  | ||||
| 	return neighbourCost; | ||||
| 	return neighborCost; | ||||
| } | ||||
|  | ||||
| void ObjectGraphCalculator::addMinimalDistanceJunctions() | ||||
| @@ -105,9 +105,9 @@ void ObjectGraphCalculator::addMinimalDistanceJunctions() | ||||
| 			if(currentCost.connectionsCount <= 2) | ||||
| 				return; | ||||
|  | ||||
| 			float neighbourCost = getneighbourConnectionsCost(pos, paths); | ||||
| 			float neighborCost = getNeighborConnectionsCost(pos, paths); | ||||
|  | ||||
| 			if(currentCost.avg < neighbourCost) | ||||
| 			if(currentCost.avg < neighborCost) | ||||
| 			{ | ||||
| 				junctions.insert(pos); | ||||
| 			} | ||||
| @@ -137,17 +137,17 @@ void ObjectGraphCalculator::calculateConnections(const int3 & pos, std::vector<A | ||||
| 		foreach_neighbour( | ||||
| 			ai->cb.get(), | ||||
| 			pos, | ||||
| 			[this, &pos, &pathCache](const CPlayerSpecificInfoCallback * cb, const int3 & neighbour) | ||||
| 			[this, &pos, &pathCache](const CPlayerSpecificInfoCallback * cb, const int3 & neighbor) | ||||
| 			{ | ||||
| 				if(target->hasNodeAt(neighbour)) | ||||
| 				if(target->hasNodeAt(neighbor)) | ||||
| 				{ | ||||
| 					ai->pathfinder->calculatePathInfo(pathCache, neighbour); | ||||
| 					ai->pathfinder->calculatePathInfo(pathCache, neighbor); | ||||
|  | ||||
| 					for(auto & path : pathCache) | ||||
| 					{ | ||||
| 						if(pos == path.targetHero->visitablePos()) | ||||
| 						{ | ||||
| 							target->tryAddConnection(pos, neighbour, path.movementCost(), path.getTotalDanger()); | ||||
| 							target->tryAddConnection(pos, neighbor, path.movementCost(), path.getTotalDanger()); | ||||
| 						} | ||||
| 					} | ||||
| 				} | ||||
|   | ||||
| @@ -1,56 +1,56 @@ | ||||
| /* | ||||
| * ObjectGraphCalculator.h, part of VCMI engine | ||||
| * | ||||
| * Authors: listed in file AUTHORS in main folder | ||||
| * | ||||
| * License: GNU General Public License v2.0 or later | ||||
| * Full text of license available in license.txt file, in main folder | ||||
| * | ||||
| */ | ||||
|  | ||||
| #pragma once | ||||
|  | ||||
| #include "ObjectGraph.h" | ||||
| #include "../AIUtility.h" | ||||
|  | ||||
| namespace NKAI | ||||
| { | ||||
|  | ||||
| struct ConnectionCostInfo | ||||
| { | ||||
| 	float totalCost = 0; | ||||
| 	float avg = 0; | ||||
| 	int connectionsCount = 0; | ||||
| }; | ||||
|  | ||||
| class ObjectGraphCalculator | ||||
| { | ||||
| private: | ||||
| 	ObjectGraph * target; | ||||
| 	const Nullkiller * ai; | ||||
| 	std::mutex syncLock; | ||||
|  | ||||
| 	std::map<const CGHeroInstance *, HeroRole> actors; | ||||
| 	std::map<const CGHeroInstance *, const CGObjectInstance *> actorObjectMap; | ||||
|  | ||||
| 	std::vector<std::unique_ptr<CGBoat>> temporaryBoats; | ||||
| 	std::vector<std::unique_ptr<CGHeroInstance>> temporaryActorHeroes; | ||||
|  | ||||
| public: | ||||
| 	ObjectGraphCalculator(ObjectGraph * target, const Nullkiller * ai); | ||||
| 	void setGraphObjects(); | ||||
| 	void calculateConnections(); | ||||
| 	float getneighbourConnectionsCost(const int3 & pos, std::vector<AIPath> & pathCache); | ||||
| 	void addMinimalDistanceJunctions(); | ||||
|  | ||||
| private: | ||||
| 	void updatePaths(); | ||||
| 	void calculateConnections(const int3 & pos, std::vector<AIPath> & pathCache); | ||||
| 	bool isExtraConnection(float direct, float side1, float side2) const; | ||||
| 	void removeExtraConnections(); | ||||
| 	void addObjectActor(const CGObjectInstance * obj); | ||||
| 	void addJunctionActor(const int3 & visitablePos, bool isVirtualBoat = false); | ||||
| 	ConnectionCostInfo getConnectionsCost(std::vector<AIPath> & paths) const; | ||||
| }; | ||||
|  | ||||
| } | ||||
| /* | ||||
| * ObjectGraphCalculator.h, part of VCMI engine | ||||
| * | ||||
| * Authors: listed in file AUTHORS in main folder | ||||
| * | ||||
| * License: GNU General Public License v2.0 or later | ||||
| * Full text of license available in license.txt file, in main folder | ||||
| * | ||||
| */ | ||||
|  | ||||
| #pragma once | ||||
|  | ||||
| #include "ObjectGraph.h" | ||||
| #include "../AIUtility.h" | ||||
|  | ||||
| namespace NKAI | ||||
| { | ||||
|  | ||||
| struct ConnectionCostInfo | ||||
| { | ||||
| 	float totalCost = 0; | ||||
| 	float avg = 0; | ||||
| 	int connectionsCount = 0; | ||||
| }; | ||||
|  | ||||
| class ObjectGraphCalculator | ||||
| { | ||||
| private: | ||||
| 	ObjectGraph * target; | ||||
| 	const Nullkiller * ai; | ||||
| 	std::mutex syncLock; | ||||
|  | ||||
| 	std::map<const CGHeroInstance *, HeroRole> actors; | ||||
| 	std::map<const CGHeroInstance *, const CGObjectInstance *> actorObjectMap; | ||||
|  | ||||
| 	std::vector<std::unique_ptr<CGBoat>> temporaryBoats; | ||||
| 	std::vector<std::unique_ptr<CGHeroInstance>> temporaryActorHeroes; | ||||
|  | ||||
| public: | ||||
| 	ObjectGraphCalculator(ObjectGraph * target, const Nullkiller * ai); | ||||
| 	void setGraphObjects(); | ||||
| 	void calculateConnections(); | ||||
| 	float getNeighborConnectionsCost(const int3 & pos, std::vector<AIPath> & pathCache); | ||||
| 	void addMinimalDistanceJunctions(); | ||||
|  | ||||
| private: | ||||
| 	void updatePaths(); | ||||
| 	void calculateConnections(const int3 & pos, std::vector<AIPath> & pathCache); | ||||
| 	bool isExtraConnection(float direct, float side1, float side2) const; | ||||
| 	void removeExtraConnections(); | ||||
| 	void addObjectActor(const CGObjectInstance * obj); | ||||
| 	void addJunctionActor(const int3 & visitablePos, bool isVirtualBoat = false); | ||||
| 	ConnectionCostInfo getConnectionsCost(std::vector<AIPath> & paths) const; | ||||
| }; | ||||
|  | ||||
| } | ||||
|   | ||||
| @@ -1,56 +1,56 @@ | ||||
| /* | ||||
|  * StupidAI.h, part of VCMI engine | ||||
|  * | ||||
|  * Authors: listed in file AUTHORS in main folder | ||||
|  * | ||||
|  * License: GNU General Public License v2.0 or later | ||||
|  * Full text of license available in license.txt file, in main folder | ||||
|  * | ||||
|  */ | ||||
| #pragma once | ||||
|  | ||||
| #include "../../lib/battle/BattleHex.h" | ||||
| #include "../../lib/battle/ReachabilityInfo.h" | ||||
| #include "../../lib/CGameInterface.h" | ||||
|  | ||||
| class EnemyInfo; | ||||
|  | ||||
| class CStupidAI : public CBattleGameInterface | ||||
| { | ||||
| 	BattleSide side; | ||||
| 	std::shared_ptr<CBattleCallback> cb; | ||||
| 	std::shared_ptr<Environment> env; | ||||
|  | ||||
| 	bool wasWaitingForRealize; | ||||
| 	bool wasUnlockingGs; | ||||
|  | ||||
| 	void print(const std::string &text) const; | ||||
| public: | ||||
| 	CStupidAI(); | ||||
| 	~CStupidAI(); | ||||
|  | ||||
| 	void initBattleInterface(std::shared_ptr<Environment> ENV, std::shared_ptr<CBattleCallback> CB) override; | ||||
| 	void initBattleInterface(std::shared_ptr<Environment> ENV, std::shared_ptr<CBattleCallback> CB, AutocombatPreferences autocombatPreferences) override; | ||||
|  | ||||
| 	void actionFinished(const BattleID & battleID, const BattleAction &action) override;//occurs AFTER every action taken by any stack or by the hero | ||||
| 	void actionStarted(const BattleID & battleID, const BattleAction &action) override;//occurs BEFORE every action taken by any stack or by the hero | ||||
| 	void activeStack(const BattleID & battleID, const CStack * stack) override; //called when it's turn of that stack | ||||
| 	void yourTacticPhase(const BattleID & battleID, int distance) override; | ||||
|  | ||||
| 	void battleAttack(const BattleID & battleID, const BattleAttack *ba) override; //called when stack is performing attack | ||||
| 	void battleStacksAttacked(const BattleID & battleID, const std::vector<BattleStackAttacked> & bsa, bool ranged) override; //called when stack receives damage (after battleAttack()) | ||||
| 	void battleEnd(const BattleID & battleID, const BattleResult *br, QueryID queryID) override; | ||||
| 	//void battleResultsApplied() override; //called when all effects of last battle are applied | ||||
| 	void battleNewRoundFirst(const BattleID & battleID) override; //called at the beginning of each turn before changes are applied; | ||||
| 	void battleNewRound(const BattleID & battleID) override; //called at the beginning of each turn, round=-1 is the tactic phase, round=0 is the first "normal" turn | ||||
| 	void battleStackMoved(const BattleID & battleID, const CStack * stack, BattleHexArray dest, int distance, bool teleport) override; | ||||
| 	void battleSpellCast(const BattleID & battleID, const BattleSpellCast *sc) override; | ||||
| 	void battleStacksEffectsSet(const BattleID & battleID, const SetStackEffect & sse) override;//called when a specific effect is set to stacks | ||||
| 	//void battleTriggerEffect(const BattleTriggerEffect & bte) override; | ||||
| 	void battleStart(const BattleID & battleID, const CCreatureSet *army1, const CCreatureSet *army2, int3 tile, const CGHeroInstance *hero1, const CGHeroInstance *hero2, BattleSide side, bool replayAllowed) override; //called by engine when battle starts; side=0 - left, side=1 - right | ||||
| 	void battleCatapultAttacked(const BattleID & battleID, const CatapultAttack & ca) override; //called when catapult makes an attack | ||||
|  | ||||
| private: | ||||
| 	BattleAction goTowards(const BattleID & battleID, const CStack * stack, BattleHexArray hexes) const; | ||||
| }; | ||||
|  | ||||
| /* | ||||
|  * StupidAI.h, part of VCMI engine | ||||
|  * | ||||
|  * Authors: listed in file AUTHORS in main folder | ||||
|  * | ||||
|  * License: GNU General Public License v2.0 or later | ||||
|  * Full text of license available in license.txt file, in main folder | ||||
|  * | ||||
|  */ | ||||
| #pragma once | ||||
|  | ||||
| #include "../../lib/battle/BattleHex.h" | ||||
| #include "../../lib/battle/ReachabilityInfo.h" | ||||
| #include "../../lib/CGameInterface.h" | ||||
|  | ||||
| class EnemyInfo; | ||||
|  | ||||
| class CStupidAI : public CBattleGameInterface | ||||
| { | ||||
| 	BattleSide side; | ||||
| 	std::shared_ptr<CBattleCallback> cb; | ||||
| 	std::shared_ptr<Environment> env; | ||||
|  | ||||
| 	bool wasWaitingForRealize; | ||||
| 	bool wasUnlockingGs; | ||||
|  | ||||
| 	void print(const std::string &text) const; | ||||
| public: | ||||
| 	CStupidAI(); | ||||
| 	~CStupidAI(); | ||||
|  | ||||
| 	void initBattleInterface(std::shared_ptr<Environment> ENV, std::shared_ptr<CBattleCallback> CB) override; | ||||
| 	void initBattleInterface(std::shared_ptr<Environment> ENV, std::shared_ptr<CBattleCallback> CB, AutocombatPreferences autocombatPreferences) override; | ||||
|  | ||||
| 	void actionFinished(const BattleID & battleID, const BattleAction &action) override;//occurs AFTER every action taken by any stack or by the hero | ||||
| 	void actionStarted(const BattleID & battleID, const BattleAction &action) override;//occurs BEFORE every action taken by any stack or by the hero | ||||
| 	void activeStack(const BattleID & battleID, const CStack * stack) override; //called when it's turn of that stack | ||||
| 	void yourTacticPhase(const BattleID & battleID, int distance) override; | ||||
|  | ||||
| 	void battleAttack(const BattleID & battleID, const BattleAttack *ba) override; //called when stack is performing attack | ||||
| 	void battleStacksAttacked(const BattleID & battleID, const std::vector<BattleStackAttacked> & bsa, bool ranged) override; //called when stack receives damage (after battleAttack()) | ||||
| 	void battleEnd(const BattleID & battleID, const BattleResult *br, QueryID queryID) override; | ||||
| 	//void battleResultsApplied() override; //called when all effects of last battle are applied | ||||
| 	void battleNewRoundFirst(const BattleID & battleID) override; //called at the beginning of each turn before changes are applied; | ||||
| 	void battleNewRound(const BattleID & battleID) override; //called at the beginning of each turn, round=-1 is the tactic phase, round=0 is the first "normal" turn | ||||
| 	void battleStackMoved(const BattleID & battleID, const CStack * stack, BattleHexArray dest, int distance, bool teleport) override; | ||||
| 	void battleSpellCast(const BattleID & battleID, const BattleSpellCast *sc) override; | ||||
| 	void battleStacksEffectsSet(const BattleID & battleID, const SetStackEffect & sse) override;//called when a specific effect is set to stacks | ||||
| 	//void battleTriggerEffect(const BattleTriggerEffect & bte) override; | ||||
| 	void battleStart(const BattleID & battleID, const CCreatureSet *army1, const CCreatureSet *army2, int3 tile, const CGHeroInstance *hero1, const CGHeroInstance *hero2, BattleSide side, bool replayAllowed) override; //called by engine when battle starts; side=0 - left, side=1 - right | ||||
| 	void battleCatapultAttacked(const BattleID & battleID, const CatapultAttack & ca) override; //called when catapult makes an attack | ||||
|  | ||||
| private: | ||||
| 	BattleAction goTowards(const BattleID & battleID, const CStack * stack, BattleHexArray hexes) const; | ||||
| }; | ||||
|  | ||||
|   | ||||
| @@ -1,448 +1,448 @@ | ||||
| /* | ||||
| * Explore.cpp, part of VCMI engine | ||||
| * | ||||
| * Authors: listed in file AUTHORS in main folder | ||||
| * | ||||
| * License: GNU General Public License v2.0 or later | ||||
| * Full text of license available in license.txt file, in main folder | ||||
| * | ||||
| */ | ||||
| #include "StdInc.h" | ||||
| #include "Goals.h" | ||||
| #include "../VCAI.h" | ||||
| #include "../AIUtility.h" | ||||
| #include "../AIhelper.h" | ||||
| #include "../FuzzyHelper.h" | ||||
| #include "../ResourceManager.h" | ||||
| #include "../BuildingManager.h" | ||||
| #include "../../../lib/constants/StringConstants.h" | ||||
| #include "../../../lib/CPlayerState.h" | ||||
|  | ||||
| using namespace Goals; | ||||
|  | ||||
| namespace Goals | ||||
| { | ||||
| 	struct ExplorationHelper | ||||
| 	{ | ||||
| 		HeroPtr hero; | ||||
| 		int sightRadius; | ||||
| 		float bestValue; | ||||
| 		TSubgoal bestGoal; | ||||
| 		VCAI * aip; | ||||
| 		CCallback * cbp; | ||||
| 		const TeamState * ts; | ||||
| 		int3 ourPos; | ||||
| 		bool allowDeadEndCancellation; | ||||
| 		bool allowGatherArmy; | ||||
|  | ||||
| 		ExplorationHelper(HeroPtr h, bool gatherArmy) | ||||
| 		{ | ||||
| 			cbp = cb; | ||||
| 			aip = ai; | ||||
| 			hero = h; | ||||
| 			ts = cbp->getPlayerTeam(ai->playerID); | ||||
| 			sightRadius = hero->getSightRadius(); | ||||
| 			bestGoal = sptr(Goals::Invalid()); | ||||
| 			bestValue = 0; | ||||
| 			ourPos = h->visitablePos(); | ||||
| 			allowDeadEndCancellation = true; | ||||
| 			allowGatherArmy = gatherArmy; | ||||
| 		} | ||||
|  | ||||
| 		void scanSector(int scanRadius) | ||||
| 		{ | ||||
| 			int3 tile = int3(0, 0, ourPos.z); | ||||
|  | ||||
| 			const auto & slice = ts->fogOfWarMap[ourPos.z]; | ||||
|  | ||||
| 			for(tile.x = ourPos.x - scanRadius; tile.x <= ourPos.x + scanRadius; tile.x++) | ||||
| 			{ | ||||
| 				for(tile.y = ourPos.y - scanRadius; tile.y <= ourPos.y + scanRadius; tile.y++) | ||||
| 				{ | ||||
|  | ||||
| 					if(cbp->isInTheMap(tile) && slice[tile.x][tile.y]) | ||||
| 					{ | ||||
| 						scanTile(tile); | ||||
| 					} | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		void scanMap() | ||||
| 		{ | ||||
| 			int3 mapSize = cbp->getMapSize(); | ||||
| 			int perimeter = 2 * sightRadius * (mapSize.x + mapSize.y); | ||||
|  | ||||
| 			std::vector<int3> from; | ||||
| 			std::vector<int3> to; | ||||
|  | ||||
| 			from.reserve(perimeter); | ||||
| 			to.reserve(perimeter); | ||||
|  | ||||
| 			foreach_tile_pos([&](const int3 & pos) | ||||
| 			{ | ||||
| 				if(ts->fogOfWarMap[pos.z][pos.x][pos.y]) | ||||
| 				{ | ||||
| 					bool hasInvisibleneighbour = false; | ||||
|  | ||||
| 					foreach_neighbour(cbp, pos, [&](CCallback * cbp, int3 neighbour) | ||||
| 					{ | ||||
| 						if(!ts->fogOfWarMap[neighbour.z][neighbour.x][neighbour.y]) | ||||
| 						{ | ||||
| 							hasInvisibleneighbour = true; | ||||
| 						} | ||||
| 					}); | ||||
|  | ||||
| 					if(hasInvisibleneighbour) | ||||
| 						from.push_back(pos); | ||||
| 				} | ||||
| 			}); | ||||
|  | ||||
| 			logAi->debug("Exploration scan visible area perimeter for hero %s", hero.name); | ||||
|  | ||||
| 			for(const int3 & tile : from) | ||||
| 			{ | ||||
| 				scanTile(tile); | ||||
| 			} | ||||
|  | ||||
| 			if(!bestGoal->invalid()) | ||||
| 			{ | ||||
| 				return; | ||||
| 			} | ||||
|  | ||||
| 			allowDeadEndCancellation = false; | ||||
|  | ||||
| 			for(int i = 0; i < sightRadius; i++) | ||||
| 			{ | ||||
| 				getVisibleNeighbours(from, to); | ||||
| 				vstd::concatenate(from, to); | ||||
| 				vstd::removeDuplicates(from); | ||||
| 			} | ||||
|  | ||||
| 			logAi->debug("Exploration scan all possible tiles for hero %s", hero.name); | ||||
|  | ||||
| 			for(const int3 & tile : from) | ||||
| 			{ | ||||
| 				scanTile(tile); | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		void scanTile(const int3 & tile) | ||||
| 		{ | ||||
| 			if(tile == ourPos | ||||
| 				|| !aip->ah->isTileAccessible(hero, tile)) //shouldn't happen, but it does | ||||
| 				return; | ||||
|  | ||||
| 			int tilesDiscovered = howManyTilesWillBeDiscovered(tile); | ||||
| 			if(!tilesDiscovered) | ||||
| 				return; | ||||
|  | ||||
| 			auto waysToVisit = aip->ah->howToVisitTile(hero, tile, allowGatherArmy); | ||||
| 			for(auto goal : waysToVisit) | ||||
| 			{ | ||||
| 				if(goal->evaluationContext.movementCost <= 0.0) // should not happen | ||||
| 					continue; | ||||
|  | ||||
| 				float ourValue = (float)tilesDiscovered * tilesDiscovered / goal->evaluationContext.movementCost; | ||||
|  | ||||
| 				if(ourValue > bestValue) //avoid costly checks of tiles that don't reveal much | ||||
| 				{ | ||||
| 					auto obj = cb->getTopObj(tile); | ||||
|  | ||||
| 					// picking up resources does not yield any exploration at all. | ||||
| 					// if it blocks the way to some explorable tile AIPathfinder will take care of it | ||||
| 					if(obj && obj->isBlockedVisitable()) | ||||
| 					{ | ||||
| 						continue; | ||||
| 					} | ||||
|  | ||||
| 					if(isSafeToVisit(hero, tile)) | ||||
| 					{ | ||||
| 						bestGoal = goal; | ||||
| 						bestValue = ourValue; | ||||
| 					} | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		void getVisibleNeighbours(const std::vector<int3> & tiles, std::vector<int3> & out) const | ||||
| 		{ | ||||
| 			for(const int3 & tile : tiles) | ||||
| 			{ | ||||
| 				foreach_neighbour(cbp, tile, [&](CCallback * cbp, int3 neighbour) | ||||
| 				{ | ||||
| 					if(ts->fogOfWarMap[neighbour.z][neighbour.x][neighbour.y]) | ||||
| 					{ | ||||
| 						out.push_back(neighbour); | ||||
| 					} | ||||
| 				}); | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		int howManyTilesWillBeDiscovered(const int3 & pos) const | ||||
| 		{ | ||||
| 			int ret = 0; | ||||
| 			int3 npos = int3(0, 0, pos.z); | ||||
|  | ||||
| 			const auto & slice = ts->fogOfWarMap[pos.z]; | ||||
|  | ||||
| 			for(npos.x = pos.x - sightRadius; npos.x <= pos.x + sightRadius; npos.x++) | ||||
| 			{ | ||||
| 				for(npos.y = pos.y - sightRadius; npos.y <= pos.y + sightRadius; npos.y++) | ||||
| 				{ | ||||
| 					if(cbp->isInTheMap(npos) | ||||
| 						&& pos.dist2d(npos) - 0.5 < sightRadius | ||||
| 						&& !slice[npos.x][npos.y]) | ||||
| 					{ | ||||
| 						if(allowDeadEndCancellation | ||||
| 							&& !hasReachableneighbour(npos)) | ||||
| 						{ | ||||
| 							continue; | ||||
| 						} | ||||
|  | ||||
| 						ret++; | ||||
| 					} | ||||
| 				} | ||||
| 			} | ||||
|  | ||||
| 			return ret; | ||||
| 		} | ||||
|  | ||||
| 		bool hasReachableneighbour(const int3 &pos) const | ||||
| 		{ | ||||
| 			for(crint3 dir : int3::getDirs()) | ||||
| 			{ | ||||
| 				int3 tile = pos + dir; | ||||
| 				if(cbp->isInTheMap(tile)) | ||||
| 				{ | ||||
| 					auto isAccessible = aip->ah->isTileAccessible(hero, tile); | ||||
|  | ||||
| 					if(isAccessible) | ||||
| 						return true; | ||||
| 				} | ||||
| 			} | ||||
|  | ||||
| 			return false; | ||||
| 		} | ||||
| 	}; | ||||
| } | ||||
|  | ||||
| bool Explore::operator==(const Explore & other) const | ||||
| { | ||||
| 	return other.hero.h == hero.h && other.allowGatherArmy == allowGatherArmy; | ||||
| } | ||||
|  | ||||
| std::string Explore::completeMessage() const | ||||
| { | ||||
| 	return "Hero " + hero.get()->getNameTranslated() + " completed exploration"; | ||||
| } | ||||
|  | ||||
| TSubgoal Explore::whatToDoToAchieve() | ||||
| { | ||||
| 	return fh->chooseSolution(getAllPossibleSubgoals()); | ||||
| } | ||||
|  | ||||
| TGoalVec Explore::getAllPossibleSubgoals() | ||||
| { | ||||
| 	TGoalVec ret; | ||||
| 	std::vector<const CGHeroInstance *> heroes; | ||||
|  | ||||
| 	if(hero) | ||||
| 	{ | ||||
| 		heroes.push_back(hero.h); | ||||
| 	} | ||||
| 	else | ||||
| 	{ | ||||
| 		//heroes = ai->getUnblockedHeroes(); | ||||
| 		heroes = cb->getHeroesInfo(); | ||||
| 		vstd::erase_if(heroes, [](const HeroPtr h) | ||||
| 		{ | ||||
| 			if(ai->getGoal(h)->goalType == EXPLORE) //do not reassign hero who is already explorer | ||||
| 				return true; | ||||
|  | ||||
| 			if(!ai->isAbleToExplore(h)) | ||||
| 				return true; | ||||
|  | ||||
| 			return !h->movementPointsRemaining(); //saves time, immobile heroes are useless anyway | ||||
| 		}); | ||||
| 	} | ||||
|  | ||||
| 	//try to use buildings that uncover map | ||||
| 	std::vector<const CGObjectInstance *> objs; | ||||
| 	for(auto obj : ai->visitableObjs) | ||||
| 	{ | ||||
| 		if(!vstd::contains(ai->alreadyVisited, obj)) | ||||
| 		{ | ||||
| 			switch(obj->ID.num) | ||||
| 			{ | ||||
| 			case Obj::REDWOOD_OBSERVATORY: | ||||
| 			case Obj::PILLAR_OF_FIRE: | ||||
| 			case Obj::CARTOGRAPHER: | ||||
| 				objs.push_back(obj); | ||||
| 				break; | ||||
| 			case Obj::MONOLITH_ONE_WAY_ENTRANCE: | ||||
| 			case Obj::MONOLITH_TWO_WAY: | ||||
| 			case Obj::SUBTERRANEAN_GATE: | ||||
| 				auto tObj = dynamic_cast<const CGTeleport *>(obj); | ||||
| 				assert(ai->knownTeleportChannels.find(tObj->channel) != ai->knownTeleportChannels.end()); | ||||
| 				if(TeleportChannel::IMPASSABLE != ai->knownTeleportChannels[tObj->channel]->passability) | ||||
| 					objs.push_back(obj); | ||||
| 				break; | ||||
| 			} | ||||
| 		} | ||||
| 		else | ||||
| 		{ | ||||
| 			switch(obj->ID.num) | ||||
| 			{ | ||||
| 			case Obj::MONOLITH_TWO_WAY: | ||||
| 			case Obj::SUBTERRANEAN_GATE: | ||||
| 				auto tObj = dynamic_cast<const CGTeleport *>(obj); | ||||
| 				if(TeleportChannel::IMPASSABLE == ai->knownTeleportChannels[tObj->channel]->passability) | ||||
| 					break; | ||||
| 				for(auto exit : ai->knownTeleportChannels[tObj->channel]->exits) | ||||
| 				{ | ||||
| 					if(!cb->getObj(exit)) | ||||
| 					{ // Always attempt to visit two-way teleports if one of channel exits is not visible | ||||
| 						objs.push_back(obj); | ||||
| 						break; | ||||
| 					} | ||||
| 				} | ||||
| 				break; | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	for(auto h : heroes) | ||||
| 	{ | ||||
| 		for(auto obj : objs) //double loop, performance risk? | ||||
| 		{ | ||||
| 			auto waysToVisitObj = ai->ah->howToVisitObj(h, obj, allowGatherArmy); | ||||
|  | ||||
| 			vstd::concatenate(ret, waysToVisitObj); | ||||
| 		} | ||||
|  | ||||
| 		TSubgoal goal = exploreNearestNeighbour(h); | ||||
|  | ||||
| 		if(!goal->invalid()) | ||||
| 		{ | ||||
| 			ret.push_back(goal); | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	if(ret.empty()) | ||||
| 	{ | ||||
| 		for(auto h : heroes) | ||||
| 		{ | ||||
| 			logAi->trace("Exploration searching for a new point for hero %s", h->getNameTranslated()); | ||||
|  | ||||
| 			TSubgoal goal = explorationNewPoint(h); | ||||
|  | ||||
| 			if(goal->invalid()) | ||||
| 			{ | ||||
| 				ai->markHeroUnableToExplore(h); //there is no freely accessible tile, do not poll this hero anymore | ||||
| 			} | ||||
| 			else | ||||
| 			{ | ||||
| 				ret.push_back(goal); | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	//we either don't have hero yet or none of heroes can explore | ||||
| 	if((!hero || ret.empty()) && ai->canRecruitAnyHero()) | ||||
| 		ret.push_back(sptr(RecruitHero())); | ||||
|  | ||||
| 	if(ret.empty()) | ||||
| 	{ | ||||
| 		throw goalFulfilledException(sptr(Explore().sethero(hero))); | ||||
| 	} | ||||
|  | ||||
| 	return ret; | ||||
| } | ||||
|  | ||||
| bool Explore::fulfillsMe(TSubgoal goal) | ||||
| { | ||||
| 	if(goal->goalType == EXPLORE) | ||||
| 	{ | ||||
| 		if(goal->hero) | ||||
| 			return hero == goal->hero; | ||||
| 		else | ||||
| 			return true; //cancel ALL exploration | ||||
| 	} | ||||
| 	return false; | ||||
| } | ||||
|  | ||||
| TSubgoal Explore::explorationBestNeighbour(int3 hpos, HeroPtr h) const | ||||
| { | ||||
| 	ExplorationHelper scanResult(h, allowGatherArmy); | ||||
|  | ||||
| 	for(crint3 dir : int3::getDirs()) | ||||
| 	{ | ||||
| 		int3 tile = hpos + dir; | ||||
| 		if(cb->isInTheMap(tile)) | ||||
| 		{ | ||||
| 			scanResult.scanTile(tile); | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	return scanResult.bestGoal; | ||||
| } | ||||
|  | ||||
|  | ||||
| TSubgoal Explore::explorationNewPoint(HeroPtr h) const | ||||
| { | ||||
| 	ExplorationHelper scanResult(h, allowGatherArmy); | ||||
|  | ||||
| 	scanResult.scanSector(10); | ||||
|  | ||||
| 	if(!scanResult.bestGoal->invalid()) | ||||
| 	{ | ||||
| 		return scanResult.bestGoal; | ||||
| 	} | ||||
|  | ||||
| 	scanResult.scanMap(); | ||||
|  | ||||
| 	return scanResult.bestGoal; | ||||
| } | ||||
|  | ||||
|  | ||||
| TSubgoal Explore::exploreNearestNeighbour(HeroPtr h) const | ||||
| { | ||||
| 	TimeCheck tc("where to explore"); | ||||
| 	int3 hpos = h->visitablePos(); | ||||
|  | ||||
| 	//look for nearby objs -> visit them if they're close enough | ||||
| 	const int DIST_LIMIT = 3; | ||||
| 	const float COST_LIMIT = .2f; //todo: fine tune | ||||
|  | ||||
| 	std::vector<const CGObjectInstance *> nearbyVisitableObjs; | ||||
| 	for(int x = hpos.x - DIST_LIMIT; x <= hpos.x + DIST_LIMIT; ++x) //get only local objects instead of all possible objects on the map | ||||
| 	{ | ||||
| 		for(int y = hpos.y - DIST_LIMIT; y <= hpos.y + DIST_LIMIT; ++y) | ||||
| 		{ | ||||
| 			for(auto obj : cb->getVisitableObjs(int3(x, y, hpos.z), false)) | ||||
| 			{ | ||||
| 				if(ai->isGoodForVisit(obj, h, COST_LIMIT)) | ||||
| 				{ | ||||
| 					nearbyVisitableObjs.push_back(obj); | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	if(nearbyVisitableObjs.size()) | ||||
| 	{ | ||||
| 		vstd::removeDuplicates(nearbyVisitableObjs); //one object may occupy multiple tiles | ||||
| 		boost::sort(nearbyVisitableObjs, CDistanceSorter(h.get())); | ||||
|  | ||||
| 		TSubgoal pickupNearestObj = fh->chooseSolution(ai->ah->howToVisitObj(h, nearbyVisitableObjs.back(), false)); | ||||
|  | ||||
| 		if(!pickupNearestObj->invalid()) | ||||
| 		{ | ||||
| 			return pickupNearestObj; | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	//check if nearby tiles allow us to reveal anything - this is quick | ||||
| 	return explorationBestNeighbour(hpos, h); | ||||
| } | ||||
| /* | ||||
| * Explore.cpp, part of VCMI engine | ||||
| * | ||||
| * Authors: listed in file AUTHORS in main folder | ||||
| * | ||||
| * License: GNU General Public License v2.0 or later | ||||
| * Full text of license available in license.txt file, in main folder | ||||
| * | ||||
| */ | ||||
| #include "StdInc.h" | ||||
| #include "Goals.h" | ||||
| #include "../VCAI.h" | ||||
| #include "../AIUtility.h" | ||||
| #include "../AIhelper.h" | ||||
| #include "../FuzzyHelper.h" | ||||
| #include "../ResourceManager.h" | ||||
| #include "../BuildingManager.h" | ||||
| #include "../../../lib/constants/StringConstants.h" | ||||
| #include "../../../lib/CPlayerState.h" | ||||
|  | ||||
| using namespace Goals; | ||||
|  | ||||
| namespace Goals | ||||
| { | ||||
| 	struct ExplorationHelper | ||||
| 	{ | ||||
| 		HeroPtr hero; | ||||
| 		int sightRadius; | ||||
| 		float bestValue; | ||||
| 		TSubgoal bestGoal; | ||||
| 		VCAI * aip; | ||||
| 		CCallback * cbp; | ||||
| 		const TeamState * ts; | ||||
| 		int3 ourPos; | ||||
| 		bool allowDeadEndCancellation; | ||||
| 		bool allowGatherArmy; | ||||
|  | ||||
| 		ExplorationHelper(HeroPtr h, bool gatherArmy) | ||||
| 		{ | ||||
| 			cbp = cb; | ||||
| 			aip = ai; | ||||
| 			hero = h; | ||||
| 			ts = cbp->getPlayerTeam(ai->playerID); | ||||
| 			sightRadius = hero->getSightRadius(); | ||||
| 			bestGoal = sptr(Goals::Invalid()); | ||||
| 			bestValue = 0; | ||||
| 			ourPos = h->visitablePos(); | ||||
| 			allowDeadEndCancellation = true; | ||||
| 			allowGatherArmy = gatherArmy; | ||||
| 		} | ||||
|  | ||||
| 		void scanSector(int scanRadius) | ||||
| 		{ | ||||
| 			int3 tile = int3(0, 0, ourPos.z); | ||||
|  | ||||
| 			const auto & slice = ts->fogOfWarMap[ourPos.z]; | ||||
|  | ||||
| 			for(tile.x = ourPos.x - scanRadius; tile.x <= ourPos.x + scanRadius; tile.x++) | ||||
| 			{ | ||||
| 				for(tile.y = ourPos.y - scanRadius; tile.y <= ourPos.y + scanRadius; tile.y++) | ||||
| 				{ | ||||
|  | ||||
| 					if(cbp->isInTheMap(tile) && slice[tile.x][tile.y]) | ||||
| 					{ | ||||
| 						scanTile(tile); | ||||
| 					} | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		void scanMap() | ||||
| 		{ | ||||
| 			int3 mapSize = cbp->getMapSize(); | ||||
| 			int perimeter = 2 * sightRadius * (mapSize.x + mapSize.y); | ||||
|  | ||||
| 			std::vector<int3> from; | ||||
| 			std::vector<int3> to; | ||||
|  | ||||
| 			from.reserve(perimeter); | ||||
| 			to.reserve(perimeter); | ||||
|  | ||||
| 			foreach_tile_pos([&](const int3 & pos) | ||||
| 			{ | ||||
| 				if(ts->fogOfWarMap[pos.z][pos.x][pos.y]) | ||||
| 				{ | ||||
| 					bool hasInvisibleNeighbor = false; | ||||
|  | ||||
| 					foreach_neighbour(cbp, pos, [&](CCallback * cbp, int3 neighbour) | ||||
| 					{ | ||||
| 						if(!ts->fogOfWarMap[neighbour.z][neighbour.x][neighbour.y]) | ||||
| 						{ | ||||
| 							hasInvisibleNeighbor = true; | ||||
| 						} | ||||
| 					}); | ||||
|  | ||||
| 					if(hasInvisibleNeighbor) | ||||
| 						from.push_back(pos); | ||||
| 				} | ||||
| 			}); | ||||
|  | ||||
| 			logAi->debug("Exploration scan visible area perimeter for hero %s", hero.name); | ||||
|  | ||||
| 			for(const int3 & tile : from) | ||||
| 			{ | ||||
| 				scanTile(tile); | ||||
| 			} | ||||
|  | ||||
| 			if(!bestGoal->invalid()) | ||||
| 			{ | ||||
| 				return; | ||||
| 			} | ||||
|  | ||||
| 			allowDeadEndCancellation = false; | ||||
|  | ||||
| 			for(int i = 0; i < sightRadius; i++) | ||||
| 			{ | ||||
| 				getVisibleNeighbours(from, to); | ||||
| 				vstd::concatenate(from, to); | ||||
| 				vstd::removeDuplicates(from); | ||||
| 			} | ||||
|  | ||||
| 			logAi->debug("Exploration scan all possible tiles for hero %s", hero.name); | ||||
|  | ||||
| 			for(const int3 & tile : from) | ||||
| 			{ | ||||
| 				scanTile(tile); | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		void scanTile(const int3 & tile) | ||||
| 		{ | ||||
| 			if(tile == ourPos | ||||
| 				|| !aip->ah->isTileAccessible(hero, tile)) //shouldn't happen, but it does | ||||
| 				return; | ||||
|  | ||||
| 			int tilesDiscovered = howManyTilesWillBeDiscovered(tile); | ||||
| 			if(!tilesDiscovered) | ||||
| 				return; | ||||
|  | ||||
| 			auto waysToVisit = aip->ah->howToVisitTile(hero, tile, allowGatherArmy); | ||||
| 			for(auto goal : waysToVisit) | ||||
| 			{ | ||||
| 				if(goal->evaluationContext.movementCost <= 0.0) // should not happen | ||||
| 					continue; | ||||
|  | ||||
| 				float ourValue = (float)tilesDiscovered * tilesDiscovered / goal->evaluationContext.movementCost; | ||||
|  | ||||
| 				if(ourValue > bestValue) //avoid costly checks of tiles that don't reveal much | ||||
| 				{ | ||||
| 					auto obj = cb->getTopObj(tile); | ||||
|  | ||||
| 					// picking up resources does not yield any exploration at all. | ||||
| 					// if it blocks the way to some explorable tile AIPathfinder will take care of it | ||||
| 					if(obj && obj->isBlockedVisitable()) | ||||
| 					{ | ||||
| 						continue; | ||||
| 					} | ||||
|  | ||||
| 					if(isSafeToVisit(hero, tile)) | ||||
| 					{ | ||||
| 						bestGoal = goal; | ||||
| 						bestValue = ourValue; | ||||
| 					} | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		void getVisibleNeighbours(const std::vector<int3> & tiles, std::vector<int3> & out) const | ||||
| 		{ | ||||
| 			for(const int3 & tile : tiles) | ||||
| 			{ | ||||
| 				foreach_neighbour(cbp, tile, [&](CCallback * cbp, int3 neighbour) | ||||
| 				{ | ||||
| 					if(ts->fogOfWarMap[neighbour.z][neighbour.x][neighbour.y]) | ||||
| 					{ | ||||
| 						out.push_back(neighbour); | ||||
| 					} | ||||
| 				}); | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		int howManyTilesWillBeDiscovered(const int3 & pos) const | ||||
| 		{ | ||||
| 			int ret = 0; | ||||
| 			int3 npos = int3(0, 0, pos.z); | ||||
|  | ||||
| 			const auto & slice = ts->fogOfWarMap[pos.z]; | ||||
|  | ||||
| 			for(npos.x = pos.x - sightRadius; npos.x <= pos.x + sightRadius; npos.x++) | ||||
| 			{ | ||||
| 				for(npos.y = pos.y - sightRadius; npos.y <= pos.y + sightRadius; npos.y++) | ||||
| 				{ | ||||
| 					if(cbp->isInTheMap(npos) | ||||
| 						&& pos.dist2d(npos) - 0.5 < sightRadius | ||||
| 						&& !slice[npos.x][npos.y]) | ||||
| 					{ | ||||
| 						if(allowDeadEndCancellation | ||||
| 							&& !hasReachableNeighbor(npos)) | ||||
| 						{ | ||||
| 							continue; | ||||
| 						} | ||||
|  | ||||
| 						ret++; | ||||
| 					} | ||||
| 				} | ||||
| 			} | ||||
|  | ||||
| 			return ret; | ||||
| 		} | ||||
|  | ||||
| 		bool hasReachableNeighbor(const int3 &pos) const | ||||
| 		{ | ||||
| 			for(crint3 dir : int3::getDirs()) | ||||
| 			{ | ||||
| 				int3 tile = pos + dir; | ||||
| 				if(cbp->isInTheMap(tile)) | ||||
| 				{ | ||||
| 					auto isAccessible = aip->ah->isTileAccessible(hero, tile); | ||||
|  | ||||
| 					if(isAccessible) | ||||
| 						return true; | ||||
| 				} | ||||
| 			} | ||||
|  | ||||
| 			return false; | ||||
| 		} | ||||
| 	}; | ||||
| } | ||||
|  | ||||
| bool Explore::operator==(const Explore & other) const | ||||
| { | ||||
| 	return other.hero.h == hero.h && other.allowGatherArmy == allowGatherArmy; | ||||
| } | ||||
|  | ||||
| std::string Explore::completeMessage() const | ||||
| { | ||||
| 	return "Hero " + hero.get()->getNameTranslated() + " completed exploration"; | ||||
| } | ||||
|  | ||||
| TSubgoal Explore::whatToDoToAchieve() | ||||
| { | ||||
| 	return fh->chooseSolution(getAllPossibleSubgoals()); | ||||
| } | ||||
|  | ||||
| TGoalVec Explore::getAllPossibleSubgoals() | ||||
| { | ||||
| 	TGoalVec ret; | ||||
| 	std::vector<const CGHeroInstance *> heroes; | ||||
|  | ||||
| 	if(hero) | ||||
| 	{ | ||||
| 		heroes.push_back(hero.h); | ||||
| 	} | ||||
| 	else | ||||
| 	{ | ||||
| 		//heroes = ai->getUnblockedHeroes(); | ||||
| 		heroes = cb->getHeroesInfo(); | ||||
| 		vstd::erase_if(heroes, [](const HeroPtr h) | ||||
| 		{ | ||||
| 			if(ai->getGoal(h)->goalType == EXPLORE) //do not reassign hero who is already explorer | ||||
| 				return true; | ||||
|  | ||||
| 			if(!ai->isAbleToExplore(h)) | ||||
| 				return true; | ||||
|  | ||||
| 			return !h->movementPointsRemaining(); //saves time, immobile heroes are useless anyway | ||||
| 		}); | ||||
| 	} | ||||
|  | ||||
| 	//try to use buildings that uncover map | ||||
| 	std::vector<const CGObjectInstance *> objs; | ||||
| 	for(auto obj : ai->visitableObjs) | ||||
| 	{ | ||||
| 		if(!vstd::contains(ai->alreadyVisited, obj)) | ||||
| 		{ | ||||
| 			switch(obj->ID.num) | ||||
| 			{ | ||||
| 			case Obj::REDWOOD_OBSERVATORY: | ||||
| 			case Obj::PILLAR_OF_FIRE: | ||||
| 			case Obj::CARTOGRAPHER: | ||||
| 				objs.push_back(obj); | ||||
| 				break; | ||||
| 			case Obj::MONOLITH_ONE_WAY_ENTRANCE: | ||||
| 			case Obj::MONOLITH_TWO_WAY: | ||||
| 			case Obj::SUBTERRANEAN_GATE: | ||||
| 				auto tObj = dynamic_cast<const CGTeleport *>(obj); | ||||
| 				assert(ai->knownTeleportChannels.find(tObj->channel) != ai->knownTeleportChannels.end()); | ||||
| 				if(TeleportChannel::IMPASSABLE != ai->knownTeleportChannels[tObj->channel]->passability) | ||||
| 					objs.push_back(obj); | ||||
| 				break; | ||||
| 			} | ||||
| 		} | ||||
| 		else | ||||
| 		{ | ||||
| 			switch(obj->ID.num) | ||||
| 			{ | ||||
| 			case Obj::MONOLITH_TWO_WAY: | ||||
| 			case Obj::SUBTERRANEAN_GATE: | ||||
| 				auto tObj = dynamic_cast<const CGTeleport *>(obj); | ||||
| 				if(TeleportChannel::IMPASSABLE == ai->knownTeleportChannels[tObj->channel]->passability) | ||||
| 					break; | ||||
| 				for(auto exit : ai->knownTeleportChannels[tObj->channel]->exits) | ||||
| 				{ | ||||
| 					if(!cb->getObj(exit)) | ||||
| 					{ // Always attempt to visit two-way teleports if one of channel exits is not visible | ||||
| 						objs.push_back(obj); | ||||
| 						break; | ||||
| 					} | ||||
| 				} | ||||
| 				break; | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	for(auto h : heroes) | ||||
| 	{ | ||||
| 		for(auto obj : objs) //double loop, performance risk? | ||||
| 		{ | ||||
| 			auto waysToVisitObj = ai->ah->howToVisitObj(h, obj, allowGatherArmy); | ||||
|  | ||||
| 			vstd::concatenate(ret, waysToVisitObj); | ||||
| 		} | ||||
|  | ||||
| 		TSubgoal goal = exploreNearestNeighbour(h); | ||||
|  | ||||
| 		if(!goal->invalid()) | ||||
| 		{ | ||||
| 			ret.push_back(goal); | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	if(ret.empty()) | ||||
| 	{ | ||||
| 		for(auto h : heroes) | ||||
| 		{ | ||||
| 			logAi->trace("Exploration searching for a new point for hero %s", h->getNameTranslated()); | ||||
|  | ||||
| 			TSubgoal goal = explorationNewPoint(h); | ||||
|  | ||||
| 			if(goal->invalid()) | ||||
| 			{ | ||||
| 				ai->markHeroUnableToExplore(h); //there is no freely accessible tile, do not poll this hero anymore | ||||
| 			} | ||||
| 			else | ||||
| 			{ | ||||
| 				ret.push_back(goal); | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	//we either don't have hero yet or none of heroes can explore | ||||
| 	if((!hero || ret.empty()) && ai->canRecruitAnyHero()) | ||||
| 		ret.push_back(sptr(RecruitHero())); | ||||
|  | ||||
| 	if(ret.empty()) | ||||
| 	{ | ||||
| 		throw goalFulfilledException(sptr(Explore().sethero(hero))); | ||||
| 	} | ||||
|  | ||||
| 	return ret; | ||||
| } | ||||
|  | ||||
| bool Explore::fulfillsMe(TSubgoal goal) | ||||
| { | ||||
| 	if(goal->goalType == EXPLORE) | ||||
| 	{ | ||||
| 		if(goal->hero) | ||||
| 			return hero == goal->hero; | ||||
| 		else | ||||
| 			return true; //cancel ALL exploration | ||||
| 	} | ||||
| 	return false; | ||||
| } | ||||
|  | ||||
| TSubgoal Explore::explorationBestNeighbour(int3 hpos, HeroPtr h) const | ||||
| { | ||||
| 	ExplorationHelper scanResult(h, allowGatherArmy); | ||||
|  | ||||
| 	for(crint3 dir : int3::getDirs()) | ||||
| 	{ | ||||
| 		int3 tile = hpos + dir; | ||||
| 		if(cb->isInTheMap(tile)) | ||||
| 		{ | ||||
| 			scanResult.scanTile(tile); | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	return scanResult.bestGoal; | ||||
| } | ||||
|  | ||||
|  | ||||
| TSubgoal Explore::explorationNewPoint(HeroPtr h) const | ||||
| { | ||||
| 	ExplorationHelper scanResult(h, allowGatherArmy); | ||||
|  | ||||
| 	scanResult.scanSector(10); | ||||
|  | ||||
| 	if(!scanResult.bestGoal->invalid()) | ||||
| 	{ | ||||
| 		return scanResult.bestGoal; | ||||
| 	} | ||||
|  | ||||
| 	scanResult.scanMap(); | ||||
|  | ||||
| 	return scanResult.bestGoal; | ||||
| } | ||||
|  | ||||
|  | ||||
| TSubgoal Explore::exploreNearestNeighbour(HeroPtr h) const | ||||
| { | ||||
| 	TimeCheck tc("where to explore"); | ||||
| 	int3 hpos = h->visitablePos(); | ||||
|  | ||||
| 	//look for nearby objs -> visit them if they're close enough | ||||
| 	const int DIST_LIMIT = 3; | ||||
| 	const float COST_LIMIT = .2f; //todo: fine tune | ||||
|  | ||||
| 	std::vector<const CGObjectInstance *> nearbyVisitableObjs; | ||||
| 	for(int x = hpos.x - DIST_LIMIT; x <= hpos.x + DIST_LIMIT; ++x) //get only local objects instead of all possible objects on the map | ||||
| 	{ | ||||
| 		for(int y = hpos.y - DIST_LIMIT; y <= hpos.y + DIST_LIMIT; ++y) | ||||
| 		{ | ||||
| 			for(auto obj : cb->getVisitableObjs(int3(x, y, hpos.z), false)) | ||||
| 			{ | ||||
| 				if(ai->isGoodForVisit(obj, h, COST_LIMIT)) | ||||
| 				{ | ||||
| 					nearbyVisitableObjs.push_back(obj); | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	if(nearbyVisitableObjs.size()) | ||||
| 	{ | ||||
| 		vstd::removeDuplicates(nearbyVisitableObjs); //one object may occupy multiple tiles | ||||
| 		boost::sort(nearbyVisitableObjs, CDistanceSorter(h.get())); | ||||
|  | ||||
| 		TSubgoal pickupNearestObj = fh->chooseSolution(ai->ah->howToVisitObj(h, nearbyVisitableObjs.back(), false)); | ||||
|  | ||||
| 		if(!pickupNearestObj->invalid()) | ||||
| 		{ | ||||
| 			return pickupNearestObj; | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	//check if nearby tiles allow us to reveal anything - this is quick | ||||
| 	return explorationBestNeighbour(hpos, h); | ||||
| } | ||||
|   | ||||
| @@ -1,66 +1,66 @@ | ||||
| /* | ||||
| * Explore.h, part of VCMI engine | ||||
| * | ||||
| * Authors: listed in file AUTHORS in main folder | ||||
| * | ||||
| * License: GNU General Public License v2.0 or later | ||||
| * Full text of license available in license.txt file, in main folder | ||||
| * | ||||
| */ | ||||
| #pragma once | ||||
|  | ||||
| #include "CGoal.h" | ||||
|  | ||||
| struct HeroPtr; | ||||
| class VCAI; | ||||
| class FuzzyHelper; | ||||
|  | ||||
| namespace Goals | ||||
| { | ||||
| 	struct ExplorationHelper; | ||||
|  | ||||
| 	class DLL_EXPORT Explore : public CGoal<Explore> | ||||
| 	{ | ||||
| 	private: | ||||
| 		bool allowGatherArmy; | ||||
|  | ||||
| 	public: | ||||
| 		Explore(bool allowGatherArmy) | ||||
| 			: CGoal(Goals::EXPLORE), allowGatherArmy(allowGatherArmy) | ||||
| 		{ | ||||
| 			priority = 1; | ||||
| 		} | ||||
|  | ||||
| 		Explore() | ||||
| 			: Explore(true) | ||||
| 		{ | ||||
| 		} | ||||
|  | ||||
| 		Explore(HeroPtr h) | ||||
| 			: CGoal(Goals::EXPLORE) | ||||
| 		{ | ||||
| 			hero = h; | ||||
| 			priority = 1; | ||||
| 		} | ||||
| 		TGoalVec getAllPossibleSubgoals() override; | ||||
| 		TSubgoal whatToDoToAchieve() override; | ||||
| 		std::string completeMessage() const override; | ||||
| 		bool fulfillsMe(TSubgoal goal) override; | ||||
| 		bool operator==(const Explore & other) const override; | ||||
|  | ||||
| 	private: | ||||
| 		TSubgoal exploreNearestNeighbour(HeroPtr h) const; | ||||
| 		TSubgoal explorationNewPoint(HeroPtr h) const; | ||||
| 		TSubgoal explorationBestNeighbour(int3 hpos, HeroPtr h) const; | ||||
| 		void explorationScanTile(const int3 & tile, ExplorationHelper & scanResult) const; | ||||
| 		bool hasReachableneighbour(const int3 &pos, HeroPtr hero, CCallback * cbp, VCAI * vcai) const; | ||||
|  | ||||
| 		void getVisibleNeighbours( | ||||
| 			const std::vector<int3> & tiles, | ||||
| 			std::vector<int3> & out, | ||||
| 			CCallback * cbp, | ||||
| 			const TeamState * ts) const; | ||||
|  | ||||
| 		int howManyTilesWillBeDiscovered(const int3 & pos, ExplorationHelper & scanResult) const; | ||||
| 	}; | ||||
| } | ||||
| /* | ||||
| * Explore.h, part of VCMI engine | ||||
| * | ||||
| * Authors: listed in file AUTHORS in main folder | ||||
| * | ||||
| * License: GNU General Public License v2.0 or later | ||||
| * Full text of license available in license.txt file, in main folder | ||||
| * | ||||
| */ | ||||
| #pragma once | ||||
|  | ||||
| #include "CGoal.h" | ||||
|  | ||||
| struct HeroPtr; | ||||
| class VCAI; | ||||
| class FuzzyHelper; | ||||
|  | ||||
| namespace Goals | ||||
| { | ||||
| 	struct ExplorationHelper; | ||||
|  | ||||
| 	class DLL_EXPORT Explore : public CGoal<Explore> | ||||
| 	{ | ||||
| 	private: | ||||
| 		bool allowGatherArmy; | ||||
|  | ||||
| 	public: | ||||
| 		Explore(bool allowGatherArmy) | ||||
| 			: CGoal(Goals::EXPLORE), allowGatherArmy(allowGatherArmy) | ||||
| 		{ | ||||
| 			priority = 1; | ||||
| 		} | ||||
|  | ||||
| 		Explore() | ||||
| 			: Explore(true) | ||||
| 		{ | ||||
| 		} | ||||
|  | ||||
| 		Explore(HeroPtr h) | ||||
| 			: CGoal(Goals::EXPLORE) | ||||
| 		{ | ||||
| 			hero = h; | ||||
| 			priority = 1; | ||||
| 		} | ||||
| 		TGoalVec getAllPossibleSubgoals() override; | ||||
| 		TSubgoal whatToDoToAchieve() override; | ||||
| 		std::string completeMessage() const override; | ||||
| 		bool fulfillsMe(TSubgoal goal) override; | ||||
| 		bool operator==(const Explore & other) const override; | ||||
|  | ||||
| 	private: | ||||
| 		TSubgoal exploreNearestNeighbour(HeroPtr h) const; | ||||
| 		TSubgoal explorationNewPoint(HeroPtr h) const; | ||||
| 		TSubgoal explorationBestNeighbour(int3 hpos, HeroPtr h) const; | ||||
| 		void explorationScanTile(const int3 & tile, ExplorationHelper & scanResult) const; | ||||
| 		bool hasReachableNeighbor(const int3 &pos, HeroPtr hero, CCallback * cbp, VCAI * vcai) const; | ||||
|  | ||||
| 		void getVisibleNeighbours( | ||||
| 			const std::vector<int3> & tiles, | ||||
| 			std::vector<int3> & out, | ||||
| 			CCallback * cbp, | ||||
| 			const TeamState * ts) const; | ||||
|  | ||||
| 		int howManyTilesWillBeDiscovered(const int3 & pos, ExplorationHelper & scanResult) const; | ||||
| 	}; | ||||
| } | ||||
|   | ||||
| @@ -819,6 +819,9 @@ BattleHex BattleFieldController::fromWhichHexAttack(BattleHex attackTarget) | ||||
|  | ||||
| bool BattleFieldController::isTileAttackable(const BattleHex & number) const | ||||
| { | ||||
| 	if(!number.isValid()) | ||||
| 		return false; | ||||
|  | ||||
| 	for (auto & elem : occupiableHexes) | ||||
| 	{ | ||||
| 		if (BattleHex::mutualPosition(elem, number) != -1 || elem == number) | ||||
|   | ||||
| @@ -1,141 +1,141 @@ | ||||
| /* | ||||
|  * BattleFieldController.h, part of VCMI engine | ||||
|  * | ||||
|  * Authors: listed in file AUTHORS in main folder | ||||
|  * | ||||
|  * License: GNU General Public License v2.0 or later | ||||
|  * Full text of license available in license.txt file, in main folder | ||||
|  * | ||||
|  */ | ||||
| #pragma once | ||||
|  | ||||
| #include "../../lib/battle/BattleHexArray.h" | ||||
| #include "../../lib/Point.h" | ||||
| #include "../gui/CIntObject.h" | ||||
|  | ||||
| VCMI_LIB_NAMESPACE_BEGIN | ||||
| class CStack; | ||||
| class Rect; | ||||
| VCMI_LIB_NAMESPACE_END | ||||
|  | ||||
| class BattleHero; | ||||
| class CAnimation; | ||||
| class Canvas; | ||||
| class IImage; | ||||
| class BattleInterface; | ||||
|  | ||||
| /// Handles battlefield grid as well as rendering of background layer of battle interface | ||||
| class BattleFieldController : public CIntObject | ||||
| { | ||||
| 	BattleInterface & owner; | ||||
|  | ||||
| 	std::shared_ptr<IImage> background; | ||||
| 	std::shared_ptr<IImage> cellBorder; | ||||
| 	std::shared_ptr<IImage> cellUnitMovementHighlight; | ||||
| 	std::shared_ptr<IImage> cellUnitMaxMovementHighlight; | ||||
| 	std::shared_ptr<IImage> cellShade; | ||||
| 	std::shared_ptr<CAnimation> rangedFullDamageLimitImages; | ||||
| 	std::shared_ptr<CAnimation> shootingRangeLimitImages; | ||||
|  | ||||
| 	std::shared_ptr<CAnimation> attackCursors; | ||||
| 	std::shared_ptr<CAnimation> spellCursors; | ||||
|  | ||||
| 	/// Canvas that contains background, hex grid (if enabled), absolute obstacles and movement range of active stack | ||||
| 	std::unique_ptr<Canvas> backgroundWithHexes; | ||||
|  | ||||
| 	/// direction which will be used to perform attack with current cursor position | ||||
| 	Point currentAttackOriginPoint; | ||||
|  | ||||
| 	/// hex currently under mouse hover | ||||
| 	BattleHex hoveredHex; | ||||
|  | ||||
| 	/// hexes to which currently active stack can move | ||||
| 	BattleHexArray occupiableHexes; | ||||
|  | ||||
| 	/// hexes that when in front of a unit cause it's amount box to move back | ||||
| 	std::array<bool, GameConstants::BFIELD_SIZE> stackCountOutsideHexes; | ||||
|  | ||||
| 	void showHighlightedHex(Canvas & to, std::shared_ptr<IImage> highlight, BattleHex hex, bool darkBorder); | ||||
|  | ||||
| 	BattleHexArray getHighlightedHexesForActiveStack(); | ||||
| 	BattleHexArray getMovementRangeForHoveredStack(); | ||||
| 	BattleHexArray getHighlightedHexesForSpellRange(); | ||||
| 	BattleHexArray getHighlightedHexesForMovementTarget(); | ||||
|  | ||||
| 	// Range limit highlight helpers | ||||
|  | ||||
| 	/// get all hexes within a certain distance of given hex | ||||
| 	BattleHexArray getRangeHexes(BattleHex sourceHex, uint8_t distance); | ||||
|  | ||||
| 	/// get only hexes at the limit of a range | ||||
| 	BattleHexArray getRangeLimitHexes(BattleHex hoveredHex, BattleHexArray hexRange, uint8_t distanceToLimit); | ||||
|  | ||||
| 	/// calculate if a hex is in range limit and return its index in range | ||||
| 	bool IsHexInRangeLimit(BattleHex hex, BattleHexArray & rangeLimitHexes, int * hexIndexInRangeLimit); | ||||
|  | ||||
| 	/// get an array that has for each hex in range, an array with all directions where an outside neighbour hex exists | ||||
| 	std::vector<std::vector<BattleHex::EDir>> getOutsideNeighbourDirectionsForLimitHexes(BattleHexArray rangeHexes, BattleHexArray rangeLimitHexes); | ||||
|  | ||||
| 	/// calculates what image to use as range limit, depending on the direction of neighbours | ||||
| 	/// a mask is used internally to mark the directions of all neighbours | ||||
| 	/// based on this mask the corresponding image is selected | ||||
| 	std::vector<std::shared_ptr<IImage>> calculateRangeLimitHighlightImages(std::vector<std::vector<BattleHex::EDir>> hexesNeighbourDirections, std::shared_ptr<CAnimation> limitImages); | ||||
|  | ||||
| 	/// calculates all hexes for a range limit and what images to be shown as highlight for each of the hexes | ||||
| 	void calculateRangeLimitAndHighlightImages(uint8_t distance, std::shared_ptr<CAnimation> rangeLimitImages, BattleHexArray & rangeLimitHexes, std::vector<std::shared_ptr<IImage>> & rangeLimitHexesHighlights); | ||||
|  | ||||
| 	void showBackground(Canvas & canvas); | ||||
| 	void showBackgroundImage(Canvas & canvas); | ||||
| 	void showBackgroundImageWithHexes(Canvas & canvas); | ||||
| 	void showHighlightedHexes(Canvas & canvas); | ||||
| 	void updateAccessibleHexes(); | ||||
|  | ||||
| 	BattleHex getHexAtPosition(Point hoverPosition); | ||||
|  | ||||
| 	/// Checks whether selected pixel is transparent, uses local coordinates of a hex | ||||
| 	bool isPixelInHex(Point const & position); | ||||
| 	size_t selectBattleCursor(BattleHex myNumber); | ||||
|  | ||||
| 	void gesture(bool on, const Point & initialPosition, const Point & finalPosition) override; | ||||
| 	void gesturePanning(const Point & initialPosition, const Point & currentPosition, const Point & lastUpdateDistance) override; | ||||
| 	void mouseMoved(const Point & cursorPosition, const Point & lastUpdateDistance) override; | ||||
| 	void clickPressed(const Point & cursorPosition) override; | ||||
| 	void showPopupWindow(const Point & cursorPosition) override; | ||||
| 	void activate() override; | ||||
|  | ||||
| 	void showAll(Canvas & to) override; | ||||
| 	void show(Canvas & to) override; | ||||
| 	void tick(uint32_t msPassed) override; | ||||
|  | ||||
| 	bool receiveEvent(const Point & position, int eventType) const override; | ||||
|  | ||||
| public: | ||||
| 	BattleFieldController(BattleInterface & owner); | ||||
|  | ||||
| 	void createHeroes(); | ||||
|  | ||||
| 	void redrawBackgroundWithHexes(); | ||||
| 	void renderBattlefield(Canvas & canvas); | ||||
|  | ||||
| 	/// Returns position of hex relative to owner (BattleInterface) | ||||
| 	Rect hexPositionLocal(BattleHex hex) const; | ||||
|  | ||||
| 	/// Returns position of hex relative to game window | ||||
| 	Rect hexPositionAbsolute(BattleHex hex) const; | ||||
|  | ||||
| 	/// Returns ID of currently hovered hex or BattleHex::INVALID if none | ||||
| 	BattleHex getHoveredHex(); | ||||
|  | ||||
| 	/// Returns the currently hovered stack | ||||
| 	const CStack* getHoveredStack(); | ||||
|  | ||||
| 	/// returns true if selected tile can be attacked in melee by current stack | ||||
| 	bool isTileAttackable(const BattleHex & number) const; | ||||
|  | ||||
| 	/// returns true if stack should render its stack count image in default position - outside own hex | ||||
| 	bool stackCountOutsideHex(const BattleHex & number) const; | ||||
|  | ||||
| 	BattleHex::EDir selectAttackDirection(BattleHex myNumber); | ||||
|  | ||||
| 	BattleHex fromWhichHexAttack(BattleHex myNumber); | ||||
| }; | ||||
| /* | ||||
|  * BattleFieldController.h, part of VCMI engine | ||||
|  * | ||||
|  * Authors: listed in file AUTHORS in main folder | ||||
|  * | ||||
|  * License: GNU General Public License v2.0 or later | ||||
|  * Full text of license available in license.txt file, in main folder | ||||
|  * | ||||
|  */ | ||||
| #pragma once | ||||
|  | ||||
| #include "../../lib/battle/BattleHexArray.h" | ||||
| #include "../../lib/Point.h" | ||||
| #include "../gui/CIntObject.h" | ||||
|  | ||||
| VCMI_LIB_NAMESPACE_BEGIN | ||||
| class CStack; | ||||
| class Rect; | ||||
| VCMI_LIB_NAMESPACE_END | ||||
|  | ||||
| class BattleHero; | ||||
| class CAnimation; | ||||
| class Canvas; | ||||
| class IImage; | ||||
| class BattleInterface; | ||||
|  | ||||
| /// Handles battlefield grid as well as rendering of background layer of battle interface | ||||
| class BattleFieldController : public CIntObject | ||||
| { | ||||
| 	BattleInterface & owner; | ||||
|  | ||||
| 	std::shared_ptr<IImage> background; | ||||
| 	std::shared_ptr<IImage> cellBorder; | ||||
| 	std::shared_ptr<IImage> cellUnitMovementHighlight; | ||||
| 	std::shared_ptr<IImage> cellUnitMaxMovementHighlight; | ||||
| 	std::shared_ptr<IImage> cellShade; | ||||
| 	std::shared_ptr<CAnimation> rangedFullDamageLimitImages; | ||||
| 	std::shared_ptr<CAnimation> shootingRangeLimitImages; | ||||
|  | ||||
| 	std::shared_ptr<CAnimation> attackCursors; | ||||
| 	std::shared_ptr<CAnimation> spellCursors; | ||||
|  | ||||
| 	/// Canvas that contains background, hex grid (if enabled), absolute obstacles and movement range of active stack | ||||
| 	std::unique_ptr<Canvas> backgroundWithHexes; | ||||
|  | ||||
| 	/// direction which will be used to perform attack with current cursor position | ||||
| 	Point currentAttackOriginPoint; | ||||
|  | ||||
| 	/// hex currently under mouse hover | ||||
| 	BattleHex hoveredHex; | ||||
|  | ||||
| 	/// hexes to which currently active stack can move | ||||
| 	BattleHexArray occupiableHexes; | ||||
|  | ||||
| 	/// hexes that when in front of a unit cause it's amount box to move back | ||||
| 	std::array<bool, GameConstants::BFIELD_SIZE> stackCountOutsideHexes; | ||||
|  | ||||
| 	void showHighlightedHex(Canvas & to, std::shared_ptr<IImage> highlight, BattleHex hex, bool darkBorder); | ||||
|  | ||||
| 	BattleHexArray getHighlightedHexesForActiveStack(); | ||||
| 	BattleHexArray getMovementRangeForHoveredStack(); | ||||
| 	BattleHexArray getHighlightedHexesForSpellRange(); | ||||
| 	BattleHexArray getHighlightedHexesForMovementTarget(); | ||||
|  | ||||
| 	// Range limit highlight helpers | ||||
|  | ||||
| 	/// get all hexes within a certain distance of given hex | ||||
| 	BattleHexArray getRangeHexes(BattleHex sourceHex, uint8_t distance); | ||||
|  | ||||
| 	/// get only hexes at the limit of a range | ||||
| 	BattleHexArray getRangeLimitHexes(BattleHex hoveredHex, BattleHexArray hexRange, uint8_t distanceToLimit); | ||||
|  | ||||
| 	/// calculate if a hex is in range limit and return its index in range | ||||
| 	bool IsHexInRangeLimit(BattleHex hex, BattleHexArray & rangeLimitHexes, int * hexIndexInRangeLimit); | ||||
|  | ||||
| 	/// get an array that has for each hex in range, an array with all directions where an outside neighbour hex exists | ||||
| 	std::vector<std::vector<BattleHex::EDir>> getOutsideNeighbourDirectionsForLimitHexes(BattleHexArray rangeHexes, BattleHexArray rangeLimitHexes); | ||||
|  | ||||
| 	/// calculates what image to use as range limit, depending on the direction of neighbours | ||||
| 	/// a mask is used internally to mark the directions of all neighbours | ||||
| 	/// based on this mask the corresponding image is selected | ||||
| 	std::vector<std::shared_ptr<IImage>> calculateRangeLimitHighlightImages(std::vector<std::vector<BattleHex::EDir>> hexesNeighbourDirections, std::shared_ptr<CAnimation> limitImages); | ||||
|  | ||||
| 	/// calculates all hexes for a range limit and what images to be shown as highlight for each of the hexes | ||||
| 	void calculateRangeLimitAndHighlightImages(uint8_t distance, std::shared_ptr<CAnimation> rangeLimitImages, BattleHexArray & rangeLimitHexes, std::vector<std::shared_ptr<IImage>> & rangeLimitHexesHighlights); | ||||
|  | ||||
| 	void showBackground(Canvas & canvas); | ||||
| 	void showBackgroundImage(Canvas & canvas); | ||||
| 	void showBackgroundImageWithHexes(Canvas & canvas); | ||||
| 	void showHighlightedHexes(Canvas & canvas); | ||||
| 	void updateAccessibleHexes(); | ||||
|  | ||||
| 	BattleHex getHexAtPosition(Point hoverPosition); | ||||
|  | ||||
| 	/// Checks whether selected pixel is transparent, uses local coordinates of a hex | ||||
| 	bool isPixelInHex(Point const & position); | ||||
| 	size_t selectBattleCursor(BattleHex myNumber); | ||||
|  | ||||
| 	void gesture(bool on, const Point & initialPosition, const Point & finalPosition) override; | ||||
| 	void gesturePanning(const Point & initialPosition, const Point & currentPosition, const Point & lastUpdateDistance) override; | ||||
| 	void mouseMoved(const Point & cursorPosition, const Point & lastUpdateDistance) override; | ||||
| 	void clickPressed(const Point & cursorPosition) override; | ||||
| 	void showPopupWindow(const Point & cursorPosition) override; | ||||
| 	void activate() override; | ||||
|  | ||||
| 	void showAll(Canvas & to) override; | ||||
| 	void show(Canvas & to) override; | ||||
| 	void tick(uint32_t msPassed) override; | ||||
|  | ||||
| 	bool receiveEvent(const Point & position, int eventType) const override; | ||||
|  | ||||
| public: | ||||
| 	BattleFieldController(BattleInterface & owner); | ||||
|  | ||||
| 	void createHeroes(); | ||||
|  | ||||
| 	void redrawBackgroundWithHexes(); | ||||
| 	void renderBattlefield(Canvas & canvas); | ||||
|  | ||||
| 	/// Returns position of hex relative to owner (BattleInterface) | ||||
| 	Rect hexPositionLocal(BattleHex hex) const; | ||||
|  | ||||
| 	/// Returns position of hex relative to game window | ||||
| 	Rect hexPositionAbsolute(BattleHex hex) const; | ||||
|  | ||||
| 	/// Returns ID of currently hovered hex or BattleHex::INVALID if none | ||||
| 	BattleHex getHoveredHex(); | ||||
|  | ||||
| 	/// Returns the currently hovered stack | ||||
| 	const CStack* getHoveredStack(); | ||||
|  | ||||
| 	/// returns true if selected tile can be attacked in melee by current stack | ||||
| 	bool isTileAttackable(const BattleHex & number) const; | ||||
|  | ||||
| 	/// returns true if stack should render its stack count image in default position - outside own hex | ||||
| 	bool stackCountOutsideHex(const BattleHex & number) const; | ||||
|  | ||||
| 	BattleHex::EDir selectAttackDirection(BattleHex myNumber); | ||||
|  | ||||
| 	BattleHex fromWhichHexAttack(BattleHex myNumber); | ||||
| }; | ||||
|   | ||||
| @@ -1,232 +1,232 @@ | ||||
| /* | ||||
|  * BattleInterface.h, part of VCMI engine | ||||
|  * | ||||
|  * Authors: listed in file AUTHORS in main folder | ||||
|  * | ||||
|  * License: GNU General Public License v2.0 or later | ||||
|  * Full text of license available in license.txt file, in main folder | ||||
|  * | ||||
|  */ | ||||
| #pragma once | ||||
|  | ||||
| #include "BattleConstants.h" | ||||
| #include "../gui/CIntObject.h" | ||||
| #include "../../lib/spells/CSpellHandler.h" //CSpell::TAnimation | ||||
| #include "../ConditionalWait.h" | ||||
|  | ||||
| VCMI_LIB_NAMESPACE_BEGIN | ||||
|  | ||||
| class CCreatureSet; | ||||
| class CGHeroInstance; | ||||
| class CStack; | ||||
| struct BattleResult; | ||||
| struct BattleSpellCast; | ||||
| struct CObstacleInstance; | ||||
| struct SetStackEffect; | ||||
| class BattleAction; | ||||
| class CGTownInstance; | ||||
| struct CatapultAttack; | ||||
| struct BattleTriggerEffect; | ||||
| struct BattleHex; | ||||
| struct InfoAboutHero; | ||||
| class ObstacleChanges; | ||||
| class CPlayerBattleCallback; | ||||
|  | ||||
| VCMI_LIB_NAMESPACE_END | ||||
|  | ||||
| class BattleHero; | ||||
| class Canvas; | ||||
| class BattleResultWindow; | ||||
| class StackQueue; | ||||
| class CPlayerInterface; | ||||
| struct BattleEffect; | ||||
| class IImage; | ||||
| class StackQueue; | ||||
|  | ||||
| class BattleProjectileController; | ||||
| class BattleSiegeController; | ||||
| class BattleObstacleController; | ||||
| class BattleFieldController; | ||||
| class BattleRenderer; | ||||
| class BattleWindow; | ||||
| class BattleStacksController; | ||||
| class BattleActionsController; | ||||
| class BattleEffectsController; | ||||
| class BattleConsole; | ||||
|  | ||||
| /// Small struct which contains information about the id of the attacked stack, the damage dealt,... | ||||
| struct StackAttackedInfo | ||||
| { | ||||
| 	const CStack *defender; | ||||
| 	const CStack *attacker; | ||||
|  | ||||
| 	int64_t  damageDealt; | ||||
| 	uint32_t amountKilled; | ||||
| 	SpellID spellEffect; | ||||
|  | ||||
| 	bool indirectAttack; //if true, stack was attacked indirectly - spell or ranged attack | ||||
| 	bool killed; //if true, stack has been killed | ||||
| 	bool rebirth; //if true, play rebirth animation after all | ||||
| 	bool cloneKilled; | ||||
| 	bool fireShield; | ||||
| }; | ||||
|  | ||||
| struct StackAttackInfo | ||||
| { | ||||
| 	const CStack *attacker; | ||||
| 	const CStack *defender; | ||||
| 	std::vector< const CStack *> secondaryDefender; | ||||
|  | ||||
| 	SpellID spellEffect; | ||||
| 	BattleHex tile; | ||||
|  | ||||
| 	bool indirectAttack; | ||||
| 	bool lucky; | ||||
| 	bool unlucky; | ||||
| 	bool deathBlow; | ||||
| 	bool lifeDrain; | ||||
| }; | ||||
|  | ||||
| /// Main class for battles, responsible for relaying information from server to various battle entities | ||||
| class BattleInterface | ||||
| { | ||||
| 	using AwaitingAnimationAction = std::function<void()>; | ||||
|  | ||||
| 	struct AwaitingAnimationEvents { | ||||
| 		AwaitingAnimationAction action; | ||||
| 		EAnimationEvents event; | ||||
| 	}; | ||||
|  | ||||
| 	/// Conditional variables that are set depending on ongoing animations on the battlefield | ||||
| 	ConditionalWait ongoingAnimationsState; | ||||
|  | ||||
| 	/// List of events that are waiting to be triggered | ||||
| 	std::vector<AwaitingAnimationEvents> awaitingEvents; | ||||
|  | ||||
| 	/// used during tactics mode, points to the interface of player with higher tactics (can be either attacker or defender in hot-seat), valid onloy for human players | ||||
| 	std::shared_ptr<CPlayerInterface> tacticianInterface; | ||||
|  | ||||
| 	/// attacker interface, not null if attacker is human in our vcmiclient | ||||
| 	std::shared_ptr<CPlayerInterface> attackerInt; | ||||
|  | ||||
| 	/// defender interface, not null if attacker is human in our vcmiclient | ||||
| 	std::shared_ptr<CPlayerInterface> defenderInt; | ||||
|  | ||||
| 	/// if set to true, battle is still starting and waiting for intro sound to end / key press from player | ||||
| 	bool battleOpeningDelayActive; | ||||
|  | ||||
| 	/// ID of ongoing battle | ||||
| 	BattleID battleID; | ||||
|  | ||||
| 	void playIntroSoundAndUnlockInterface(); | ||||
| 	void onIntroSoundPlayed(); | ||||
| public: | ||||
| 	/// copy of initial armies (for result window) | ||||
| 	const CCreatureSet *army1; | ||||
| 	const CCreatureSet *army2; | ||||
|  | ||||
| 	std::shared_ptr<BattleWindow> windowObject; | ||||
| 	std::shared_ptr<BattleConsole> console; | ||||
|  | ||||
| 	/// currently active player interface | ||||
| 	std::shared_ptr<CPlayerInterface> curInt; | ||||
|  | ||||
| 	const CGHeroInstance *attackingHeroInstance; | ||||
| 	const CGHeroInstance *defendingHeroInstance; | ||||
|  | ||||
| 	bool tacticsMode; | ||||
| 	ui32 round; | ||||
|  | ||||
| 	std::unique_ptr<BattleProjectileController> projectilesController; | ||||
| 	std::unique_ptr<BattleSiegeController> siegeController; | ||||
| 	std::unique_ptr<BattleObstacleController> obstacleController; | ||||
| 	std::unique_ptr<BattleFieldController> fieldController; | ||||
| 	std::unique_ptr<BattleStacksController> stacksController; | ||||
| 	std::unique_ptr<BattleActionsController> actionsController; | ||||
| 	std::unique_ptr<BattleEffectsController> effectsController; | ||||
|  | ||||
| 	std::shared_ptr<BattleHero> attackingHero; | ||||
| 	std::shared_ptr<BattleHero> defendingHero; | ||||
|  | ||||
| 	bool openingPlaying() const; | ||||
| 	void openingEnd(); | ||||
|  | ||||
| 	bool makingTurn() const; | ||||
|  | ||||
| 	BattleID getBattleID() const; | ||||
| 	std::shared_ptr<CPlayerBattleCallback> getBattle() const; | ||||
|  | ||||
| 	BattleInterface(const BattleID & battleID, const CCreatureSet *army1, const CCreatureSet *army2, const CGHeroInstance *hero1, const CGHeroInstance *hero2, std::shared_ptr<CPlayerInterface> att, std::shared_ptr<CPlayerInterface> defen, std::shared_ptr<CPlayerInterface> spectatorInt = nullptr); | ||||
| 	~BattleInterface(); | ||||
|  | ||||
| 	void trySetActivePlayer( PlayerColor player ); // if in hotseat, will activate interface of chosen player | ||||
| 	void activateStack(); //sets activeStack to stackToActivate etc. //FIXME: No, it's not clear at all | ||||
| 	void requestAutofightingAIToTakeAction(); | ||||
|  | ||||
| 	void giveCommand(EActionType action, BattleHex tile = BattleHex(), SpellID spell = SpellID::NONE); | ||||
| 	void sendCommand(BattleAction command, const CStack * actor = nullptr); | ||||
|  | ||||
| 	const CGHeroInstance *getActiveHero(); //returns hero that can currently cast a spell | ||||
|  | ||||
| 	void showInterface(Canvas & to); | ||||
|  | ||||
| 	void setHeroAnimation(BattleSide side, EHeroAnimType phase); | ||||
|  | ||||
| 	void executeSpellCast(); //called when a hero casts a spell | ||||
|  | ||||
| 	void appendBattleLog(const std::string & newEntry); | ||||
|  | ||||
| 	void redrawBattlefield(); //refresh GUI after changing stack range / grid settings | ||||
| 	CPlayerInterface *getCurrentPlayerInterface() const; | ||||
|  | ||||
| 	void tacticNextStack(const CStack *current); | ||||
| 	void tacticPhaseEnd(); | ||||
|  | ||||
| 	void setBattleQueueVisibility(bool visible); | ||||
| 	void setStickyHeroWindowsVisibility(bool visible); | ||||
| 	void setStickyQuickSpellWindowVisibility(bool visible); | ||||
|  | ||||
| 	void endNetwork(); | ||||
| 	void executeStagedAnimations(); | ||||
| 	void executeAnimationStage( EAnimationEvents event); | ||||
| 	void onAnimationsStarted(); | ||||
| 	void onAnimationsFinished(); | ||||
| 	void waitForAnimations(); | ||||
| 	bool hasAnimations(); | ||||
| 	void checkForAnimations(); | ||||
| 	void addToAnimationStage( EAnimationEvents event, const AwaitingAnimationAction & action); | ||||
|  | ||||
| 	//call-ins | ||||
| 	void startAction(const BattleAction & action); | ||||
| 	void stackReset(const CStack * stack); | ||||
| 	void stackAdded(const CStack * stack); //new stack appeared on battlefield | ||||
| 	void stackRemoved(uint32_t stackID); //stack disappeared from batlefiled | ||||
| 	void stackActivated(const CStack *stack); //active stack has been changed | ||||
| 	void stackMoved(const CStack *stack, const BattleHexArray & destHex, int distance, bool teleport); //stack with id number moved to destHex | ||||
| 	void stacksAreAttacked(std::vector<StackAttackedInfo> attackedInfos); //called when a certain amount of stacks has been attacked | ||||
| 	void stackAttacking(const StackAttackInfo & attackInfo); //called when stack with id ID is attacking something on hex dest | ||||
| 	void newRoundFirst(); | ||||
| 	void newRound(); //called when round is ended; | ||||
| 	void stackIsCatapulting(const CatapultAttack & ca); //called when a stack is attacking walls | ||||
| 	void battleFinished(const BattleResult& br, QueryID queryID); //called when battle is finished - battleresult window should be printed | ||||
| 	void spellCast(const BattleSpellCast *sc); //called when a hero casts a spell | ||||
| 	void battleStacksEffectsSet(const SetStackEffect & sse); //called when a specific effect is set to stacks | ||||
| 	void castThisSpell(SpellID spellID); //called when player has chosen a spell from spellbook | ||||
|  | ||||
| 	void displayBattleLog(const std::vector<MetaString> & battleLog); | ||||
|  | ||||
| 	void displaySpellAnimationQueue(const CSpell * spell, const CSpell::TAnimationQueue & q, BattleHex destinationTile, bool isHit); | ||||
| 	void displaySpellCast(const CSpell * spell, BattleHex destinationTile); //displays spell`s cast animation | ||||
| 	void displaySpellEffect(const CSpell * spell, BattleHex destinationTile); //displays spell`s affected animation | ||||
| 	void displaySpellHit(const CSpell * spell, BattleHex destinationTile); //displays spell`s affected animation | ||||
|  | ||||
| 	void endAction(const BattleAction & action); | ||||
|  | ||||
| 	void obstaclePlaced(const std::vector<std::shared_ptr<const CObstacleInstance>> oi); | ||||
| 	void obstacleRemoved(const std::vector<ObstacleChanges> & obstacles); | ||||
|  | ||||
| 	void gateStateChanged(const EGateState state); | ||||
|  | ||||
| 	const CGHeroInstance *currentHero() const; | ||||
| 	InfoAboutHero enemyHero() const; | ||||
| }; | ||||
| /* | ||||
|  * BattleInterface.h, part of VCMI engine | ||||
|  * | ||||
|  * Authors: listed in file AUTHORS in main folder | ||||
|  * | ||||
|  * License: GNU General Public License v2.0 or later | ||||
|  * Full text of license available in license.txt file, in main folder | ||||
|  * | ||||
|  */ | ||||
| #pragma once | ||||
|  | ||||
| #include "BattleConstants.h" | ||||
| #include "../gui/CIntObject.h" | ||||
| #include "../../lib/spells/CSpellHandler.h" //CSpell::TAnimation | ||||
| #include "../ConditionalWait.h" | ||||
|  | ||||
| VCMI_LIB_NAMESPACE_BEGIN | ||||
|  | ||||
| class CCreatureSet; | ||||
| class CGHeroInstance; | ||||
| class CStack; | ||||
| struct BattleResult; | ||||
| struct BattleSpellCast; | ||||
| struct CObstacleInstance; | ||||
| struct SetStackEffect; | ||||
| class BattleAction; | ||||
| class CGTownInstance; | ||||
| struct CatapultAttack; | ||||
| struct BattleTriggerEffect; | ||||
| struct BattleHex; | ||||
| struct InfoAboutHero; | ||||
| class ObstacleChanges; | ||||
| class CPlayerBattleCallback; | ||||
|  | ||||
| VCMI_LIB_NAMESPACE_END | ||||
|  | ||||
| class BattleHero; | ||||
| class Canvas; | ||||
| class BattleResultWindow; | ||||
| class StackQueue; | ||||
| class CPlayerInterface; | ||||
| struct BattleEffect; | ||||
| class IImage; | ||||
| class StackQueue; | ||||
|  | ||||
| class BattleProjectileController; | ||||
| class BattleSiegeController; | ||||
| class BattleObstacleController; | ||||
| class BattleFieldController; | ||||
| class BattleRenderer; | ||||
| class BattleWindow; | ||||
| class BattleStacksController; | ||||
| class BattleActionsController; | ||||
| class BattleEffectsController; | ||||
| class BattleConsole; | ||||
|  | ||||
| /// Small struct which contains information about the id of the attacked stack, the damage dealt,... | ||||
| struct StackAttackedInfo | ||||
| { | ||||
| 	const CStack *defender; | ||||
| 	const CStack *attacker; | ||||
|  | ||||
| 	int64_t  damageDealt; | ||||
| 	uint32_t amountKilled; | ||||
| 	SpellID spellEffect; | ||||
|  | ||||
| 	bool indirectAttack; //if true, stack was attacked indirectly - spell or ranged attack | ||||
| 	bool killed; //if true, stack has been killed | ||||
| 	bool rebirth; //if true, play rebirth animation after all | ||||
| 	bool cloneKilled; | ||||
| 	bool fireShield; | ||||
| }; | ||||
|  | ||||
| struct StackAttackInfo | ||||
| { | ||||
| 	const CStack *attacker; | ||||
| 	const CStack *defender; | ||||
| 	std::vector< const CStack *> secondaryDefender; | ||||
|  | ||||
| 	SpellID spellEffect; | ||||
| 	BattleHex tile; | ||||
|  | ||||
| 	bool indirectAttack; | ||||
| 	bool lucky; | ||||
| 	bool unlucky; | ||||
| 	bool deathBlow; | ||||
| 	bool lifeDrain; | ||||
| }; | ||||
|  | ||||
| /// Main class for battles, responsible for relaying information from server to various battle entities | ||||
| class BattleInterface | ||||
| { | ||||
| 	using AwaitingAnimationAction = std::function<void()>; | ||||
|  | ||||
| 	struct AwaitingAnimationEvents { | ||||
| 		AwaitingAnimationAction action; | ||||
| 		EAnimationEvents event; | ||||
| 	}; | ||||
|  | ||||
| 	/// Conditional variables that are set depending on ongoing animations on the battlefield | ||||
| 	ConditionalWait ongoingAnimationsState; | ||||
|  | ||||
| 	/// List of events that are waiting to be triggered | ||||
| 	std::vector<AwaitingAnimationEvents> awaitingEvents; | ||||
|  | ||||
| 	/// used during tactics mode, points to the interface of player with higher tactics (can be either attacker or defender in hot-seat), valid onloy for human players | ||||
| 	std::shared_ptr<CPlayerInterface> tacticianInterface; | ||||
|  | ||||
| 	/// attacker interface, not null if attacker is human in our vcmiclient | ||||
| 	std::shared_ptr<CPlayerInterface> attackerInt; | ||||
|  | ||||
| 	/// defender interface, not null if attacker is human in our vcmiclient | ||||
| 	std::shared_ptr<CPlayerInterface> defenderInt; | ||||
|  | ||||
| 	/// if set to true, battle is still starting and waiting for intro sound to end / key press from player | ||||
| 	bool battleOpeningDelayActive; | ||||
|  | ||||
| 	/// ID of ongoing battle | ||||
| 	BattleID battleID; | ||||
|  | ||||
| 	void playIntroSoundAndUnlockInterface(); | ||||
| 	void onIntroSoundPlayed(); | ||||
| public: | ||||
| 	/// copy of initial armies (for result window) | ||||
| 	const CCreatureSet *army1; | ||||
| 	const CCreatureSet *army2; | ||||
|  | ||||
| 	std::shared_ptr<BattleWindow> windowObject; | ||||
| 	std::shared_ptr<BattleConsole> console; | ||||
|  | ||||
| 	/// currently active player interface | ||||
| 	std::shared_ptr<CPlayerInterface> curInt; | ||||
|  | ||||
| 	const CGHeroInstance *attackingHeroInstance; | ||||
| 	const CGHeroInstance *defendingHeroInstance; | ||||
|  | ||||
| 	bool tacticsMode; | ||||
| 	ui32 round; | ||||
|  | ||||
| 	std::unique_ptr<BattleProjectileController> projectilesController; | ||||
| 	std::unique_ptr<BattleSiegeController> siegeController; | ||||
| 	std::unique_ptr<BattleObstacleController> obstacleController; | ||||
| 	std::unique_ptr<BattleFieldController> fieldController; | ||||
| 	std::unique_ptr<BattleStacksController> stacksController; | ||||
| 	std::unique_ptr<BattleActionsController> actionsController; | ||||
| 	std::unique_ptr<BattleEffectsController> effectsController; | ||||
|  | ||||
| 	std::shared_ptr<BattleHero> attackingHero; | ||||
| 	std::shared_ptr<BattleHero> defendingHero; | ||||
|  | ||||
| 	bool openingPlaying() const; | ||||
| 	void openingEnd(); | ||||
|  | ||||
| 	bool makingTurn() const; | ||||
|  | ||||
| 	BattleID getBattleID() const; | ||||
| 	std::shared_ptr<CPlayerBattleCallback> getBattle() const; | ||||
|  | ||||
| 	BattleInterface(const BattleID & battleID, const CCreatureSet *army1, const CCreatureSet *army2, const CGHeroInstance *hero1, const CGHeroInstance *hero2, std::shared_ptr<CPlayerInterface> att, std::shared_ptr<CPlayerInterface> defen, std::shared_ptr<CPlayerInterface> spectatorInt = nullptr); | ||||
| 	~BattleInterface(); | ||||
|  | ||||
| 	void trySetActivePlayer( PlayerColor player ); // if in hotseat, will activate interface of chosen player | ||||
| 	void activateStack(); //sets activeStack to stackToActivate etc. //FIXME: No, it's not clear at all | ||||
| 	void requestAutofightingAIToTakeAction(); | ||||
|  | ||||
| 	void giveCommand(EActionType action, BattleHex tile = BattleHex(), SpellID spell = SpellID::NONE); | ||||
| 	void sendCommand(BattleAction command, const CStack * actor = nullptr); | ||||
|  | ||||
| 	const CGHeroInstance *getActiveHero(); //returns hero that can currently cast a spell | ||||
|  | ||||
| 	void showInterface(Canvas & to); | ||||
|  | ||||
| 	void setHeroAnimation(BattleSide side, EHeroAnimType phase); | ||||
|  | ||||
| 	void executeSpellCast(); //called when a hero casts a spell | ||||
|  | ||||
| 	void appendBattleLog(const std::string & newEntry); | ||||
|  | ||||
| 	void redrawBattlefield(); //refresh GUI after changing stack range / grid settings | ||||
| 	CPlayerInterface *getCurrentPlayerInterface() const; | ||||
|  | ||||
| 	void tacticNextStack(const CStack *current); | ||||
| 	void tacticPhaseEnd(); | ||||
|  | ||||
| 	void setBattleQueueVisibility(bool visible); | ||||
| 	void setStickyHeroWindowsVisibility(bool visible); | ||||
| 	void setStickyQuickSpellWindowVisibility(bool visible); | ||||
|  | ||||
| 	void endNetwork(); | ||||
| 	void executeStagedAnimations(); | ||||
| 	void executeAnimationStage( EAnimationEvents event); | ||||
| 	void onAnimationsStarted(); | ||||
| 	void onAnimationsFinished(); | ||||
| 	void waitForAnimations(); | ||||
| 	bool hasAnimations(); | ||||
| 	void checkForAnimations(); | ||||
| 	void addToAnimationStage( EAnimationEvents event, const AwaitingAnimationAction & action); | ||||
|  | ||||
| 	//call-ins | ||||
| 	void startAction(const BattleAction & action); | ||||
| 	void stackReset(const CStack * stack); | ||||
| 	void stackAdded(const CStack * stack); //new stack appeared on battlefield | ||||
| 	void stackRemoved(uint32_t stackID); //stack disappeared from batlefiled | ||||
| 	void stackActivated(const CStack *stack); //active stack has been changed | ||||
| 	void stackMoved(const CStack *stack, const BattleHexArray & destHex, int distance, bool teleport); //stack with id number moved to destHex | ||||
| 	void stacksAreAttacked(std::vector<StackAttackedInfo> attackedInfos); //called when a certain amount of stacks has been attacked | ||||
| 	void stackAttacking(const StackAttackInfo & attackInfo); //called when stack with id ID is attacking something on hex dest | ||||
| 	void newRoundFirst(); | ||||
| 	void newRound(); //called when round is ended; | ||||
| 	void stackIsCatapulting(const CatapultAttack & ca); //called when a stack is attacking walls | ||||
| 	void battleFinished(const BattleResult& br, QueryID queryID); //called when battle is finished - battleresult window should be printed | ||||
| 	void spellCast(const BattleSpellCast *sc); //called when a hero casts a spell | ||||
| 	void battleStacksEffectsSet(const SetStackEffect & sse); //called when a specific effect is set to stacks | ||||
| 	void castThisSpell(SpellID spellID); //called when player has chosen a spell from spellbook | ||||
|  | ||||
| 	void displayBattleLog(const std::vector<MetaString> & battleLog); | ||||
|  | ||||
| 	void displaySpellAnimationQueue(const CSpell * spell, const CSpell::TAnimationQueue & q, BattleHex destinationTile, bool isHit); | ||||
| 	void displaySpellCast(const CSpell * spell, BattleHex destinationTile); //displays spell`s cast animation | ||||
| 	void displaySpellEffect(const CSpell * spell, BattleHex destinationTile); //displays spell`s affected animation | ||||
| 	void displaySpellHit(const CSpell * spell, BattleHex destinationTile); //displays spell`s affected animation | ||||
|  | ||||
| 	void endAction(const BattleAction & action); | ||||
|  | ||||
| 	void obstaclePlaced(const std::vector<std::shared_ptr<const CObstacleInstance>> oi); | ||||
| 	void obstacleRemoved(const std::vector<ObstacleChanges> & obstacles); | ||||
|  | ||||
| 	void gateStateChanged(const EGateState state); | ||||
|  | ||||
| 	const CGHeroInstance *currentHero() const; | ||||
| 	InfoAboutHero enemyHero() const; | ||||
| }; | ||||
|   | ||||
| @@ -1,149 +1,149 @@ | ||||
| /* | ||||
|  * BattleStacksController.h, part of VCMI engine | ||||
|  * | ||||
|  * Authors: listed in file AUTHORS in main folder | ||||
|  * | ||||
|  * License: GNU General Public License v2.0 or later | ||||
|  * Full text of license available in license.txt file, in main folder | ||||
|  * | ||||
|  */ | ||||
| #pragma once | ||||
|  | ||||
| #include "../render/ColorFilter.h" | ||||
|  | ||||
| VCMI_LIB_NAMESPACE_BEGIN | ||||
|  | ||||
| struct BattleHex; | ||||
| class BattleHexArray; | ||||
| class BattleAction; | ||||
| class CStack; | ||||
| class CSpell; | ||||
| class SpellID; | ||||
| class Point; | ||||
|  | ||||
| VCMI_LIB_NAMESPACE_END | ||||
|  | ||||
| struct StackAttackedInfo; | ||||
| struct StackAttackInfo; | ||||
|  | ||||
| class ColorFilter; | ||||
| class Canvas; | ||||
| class BattleInterface; | ||||
| class BattleAnimation; | ||||
| class CreatureAnimation; | ||||
| class BattleAnimation; | ||||
| class BattleRenderer; | ||||
| class IImage; | ||||
|  | ||||
| struct BattleStackFilterEffect | ||||
| { | ||||
| 	ColorFilter effect; | ||||
| 	const CStack * target; | ||||
| 	const CSpell * source; | ||||
| 	bool persistent; | ||||
| }; | ||||
|  | ||||
| /// Class responsible for handling stacks in battle | ||||
| /// Handles ordering of stacks animation | ||||
| /// As well as rendering of stacks, their amount boxes | ||||
| /// And any other effect applied to stacks | ||||
| class BattleStacksController | ||||
| { | ||||
| 	BattleInterface & owner; | ||||
|  | ||||
| 	std::shared_ptr<IImage> amountNormal; | ||||
| 	std::shared_ptr<IImage> amountNegative; | ||||
| 	std::shared_ptr<IImage> amountPositive; | ||||
| 	std::shared_ptr<IImage> amountEffNeutral; | ||||
|  | ||||
| 	/// currently displayed animations <anim, initialized> | ||||
| 	std::vector<BattleAnimation *> currentAnimations; | ||||
|  | ||||
| 	/// currently active color effects on stacks, in order of their addition (which corresponds to their apply order) | ||||
| 	std::vector<BattleStackFilterEffect> stackFilterEffects; | ||||
|  | ||||
| 	/// animations of creatures from fighting armies (order by BattleInfo's stacks' ID) | ||||
| 	std::map<int32_t, std::shared_ptr<CreatureAnimation>> stackAnimation; | ||||
|  | ||||
| 	/// <creatureID, if false reverse creature's animation> //TODO: move it to battle callback | ||||
| 	std::map<int, bool> stackFacingRight; | ||||
|  | ||||
| 	/// Stacks have amount box hidden due to ongoing animations | ||||
| 	std::set<int> stackAmountBoxHidden; | ||||
|  | ||||
| 	/// currently active stack; nullptr - no one | ||||
| 	const CStack *activeStack; | ||||
|  | ||||
| 	/// stacks or their battle queue images below mouse pointer (multiple stacks possible while spellcasting), used for border animation | ||||
| 	std::vector<const CStack *> mouseHoveredStacks; | ||||
|  | ||||
| 	///when animation is playing, we should wait till the end to make the next stack active; nullptr of none | ||||
| 	const CStack *stackToActivate; | ||||
|  | ||||
| 	/// for giving IDs for animations | ||||
| 	ui32 animIDhelper; | ||||
|  | ||||
| 	bool stackNeedsAmountBox(const CStack * stack) const; | ||||
| 	void showStackAmountBox(Canvas & canvas, const CStack * stack); | ||||
| 	BattleHex getStackCurrentPosition(const CStack * stack) const; | ||||
|  | ||||
| 	std::shared_ptr<IImage> getStackAmountBox(const CStack * stack); | ||||
|  | ||||
| 	void removeExpiredColorFilters(); | ||||
|  | ||||
| 	void initializeBattleAnimations(); | ||||
| 	void tickFrameBattleAnimations(uint32_t msPassed); | ||||
|  | ||||
| 	void updateBattleAnimations(uint32_t msPassed); | ||||
|  | ||||
| 	std::vector<const CStack *> selectHoveredStacks(); | ||||
|  | ||||
| 	bool shouldAttackFacingRight(const CStack * attacker, const CStack * defender); | ||||
|  | ||||
| public: | ||||
| 	BattleStacksController(BattleInterface & owner); | ||||
|  | ||||
| 	bool shouldRotate(const CStack * stack, const BattleHex & oldPos, const BattleHex & nextHex) const; | ||||
| 	bool facingRight(const CStack * stack) const; | ||||
|  | ||||
| 	void stackReset(const CStack * stack); | ||||
| 	void stackAdded(const CStack * stack, bool instant); //new stack appeared on battlefield | ||||
| 	void stackRemoved(uint32_t stackID); //stack disappeared from batlefiled | ||||
| 	void stackActivated(const CStack *stack); //active stack has been changed | ||||
| 	void stackMoved(const CStack *stack, const BattleHexArray & destHex, int distance); //stack with id number moved to destHex | ||||
| 	void stackTeleported(const CStack *stack, const BattleHexArray & destHex, int distance); //stack with id number moved to destHex | ||||
| 	void stacksAreAttacked(std::vector<StackAttackedInfo> attackedInfos); //called when a certain amount of stacks has been attacked | ||||
| 	void stackAttacking(const StackAttackInfo & info); //called when stack with id ID is attacking something on hex dest | ||||
|  | ||||
| 	void startAction(const BattleAction & action); | ||||
| 	void endAction(const BattleAction & action); | ||||
|  | ||||
| 	void deactivateStack(); //copy activeStack to stackToActivate, then set activeStack to nullptr to temporary disable current stack | ||||
|  | ||||
| 	void activateStack(); //copy stackToActivate to activeStack to enable controls of the stack | ||||
|  | ||||
| 	void setActiveStack(const CStack *stack); | ||||
|  | ||||
| 	void showAliveStack(Canvas & canvas, const CStack * stack); | ||||
| 	void showStack(Canvas & canvas, const CStack * stack); | ||||
|  | ||||
| 	void updateHoveredStacks(); | ||||
|  | ||||
| 	void collectRenderableObjects(BattleRenderer & renderer); | ||||
|  | ||||
| 	/// Adds new color filter effect targeting stack | ||||
| 	/// Effect will last as long as stack is affected by specified spell (unless effect is persistent) | ||||
| 	/// If effect from same (target, source) already exists, it will be updated | ||||
| 	void setStackColorFilter(const ColorFilter & effect, const CStack * target, const CSpell *source, bool persistent); | ||||
| 	void addNewAnim(BattleAnimation *anim); //adds new anim to pendingAnims | ||||
|  | ||||
| 	const CStack* getActiveStack() const; | ||||
| 	const std::vector<uint32_t> getHoveredStacksUnitIds() const; | ||||
|  | ||||
| 	void tick(uint32_t msPassed); | ||||
|  | ||||
| 	/// returns position of animation needed to place stack in specific hex | ||||
| 	Point getStackPositionAtHex(BattleHex hexNum, const CStack * creature) const; | ||||
|  | ||||
| 	friend class BattleAnimation; // for exposing pendingAnims/creAnims/creDir to animations | ||||
| }; | ||||
| /* | ||||
|  * BattleStacksController.h, part of VCMI engine | ||||
|  * | ||||
|  * Authors: listed in file AUTHORS in main folder | ||||
|  * | ||||
|  * License: GNU General Public License v2.0 or later | ||||
|  * Full text of license available in license.txt file, in main folder | ||||
|  * | ||||
|  */ | ||||
| #pragma once | ||||
|  | ||||
| #include "../render/ColorFilter.h" | ||||
|  | ||||
| VCMI_LIB_NAMESPACE_BEGIN | ||||
|  | ||||
| struct BattleHex; | ||||
| class BattleHexArray; | ||||
| class BattleAction; | ||||
| class CStack; | ||||
| class CSpell; | ||||
| class SpellID; | ||||
| class Point; | ||||
|  | ||||
| VCMI_LIB_NAMESPACE_END | ||||
|  | ||||
| struct StackAttackedInfo; | ||||
| struct StackAttackInfo; | ||||
|  | ||||
| class ColorFilter; | ||||
| class Canvas; | ||||
| class BattleInterface; | ||||
| class BattleAnimation; | ||||
| class CreatureAnimation; | ||||
| class BattleAnimation; | ||||
| class BattleRenderer; | ||||
| class IImage; | ||||
|  | ||||
| struct BattleStackFilterEffect | ||||
| { | ||||
| 	ColorFilter effect; | ||||
| 	const CStack * target; | ||||
| 	const CSpell * source; | ||||
| 	bool persistent; | ||||
| }; | ||||
|  | ||||
| /// Class responsible for handling stacks in battle | ||||
| /// Handles ordering of stacks animation | ||||
| /// As well as rendering of stacks, their amount boxes | ||||
| /// And any other effect applied to stacks | ||||
| class BattleStacksController | ||||
| { | ||||
| 	BattleInterface & owner; | ||||
|  | ||||
| 	std::shared_ptr<IImage> amountNormal; | ||||
| 	std::shared_ptr<IImage> amountNegative; | ||||
| 	std::shared_ptr<IImage> amountPositive; | ||||
| 	std::shared_ptr<IImage> amountEffNeutral; | ||||
|  | ||||
| 	/// currently displayed animations <anim, initialized> | ||||
| 	std::vector<BattleAnimation *> currentAnimations; | ||||
|  | ||||
| 	/// currently active color effects on stacks, in order of their addition (which corresponds to their apply order) | ||||
| 	std::vector<BattleStackFilterEffect> stackFilterEffects; | ||||
|  | ||||
| 	/// animations of creatures from fighting armies (order by BattleInfo's stacks' ID) | ||||
| 	std::map<int32_t, std::shared_ptr<CreatureAnimation>> stackAnimation; | ||||
|  | ||||
| 	/// <creatureID, if false reverse creature's animation> //TODO: move it to battle callback | ||||
| 	std::map<int, bool> stackFacingRight; | ||||
|  | ||||
| 	/// Stacks have amount box hidden due to ongoing animations | ||||
| 	std::set<int> stackAmountBoxHidden; | ||||
|  | ||||
| 	/// currently active stack; nullptr - no one | ||||
| 	const CStack *activeStack; | ||||
|  | ||||
| 	/// stacks or their battle queue images below mouse pointer (multiple stacks possible while spellcasting), used for border animation | ||||
| 	std::vector<const CStack *> mouseHoveredStacks; | ||||
|  | ||||
| 	///when animation is playing, we should wait till the end to make the next stack active; nullptr of none | ||||
| 	const CStack *stackToActivate; | ||||
|  | ||||
| 	/// for giving IDs for animations | ||||
| 	ui32 animIDhelper; | ||||
|  | ||||
| 	bool stackNeedsAmountBox(const CStack * stack) const; | ||||
| 	void showStackAmountBox(Canvas & canvas, const CStack * stack); | ||||
| 	BattleHex getStackCurrentPosition(const CStack * stack) const; | ||||
|  | ||||
| 	std::shared_ptr<IImage> getStackAmountBox(const CStack * stack); | ||||
|  | ||||
| 	void removeExpiredColorFilters(); | ||||
|  | ||||
| 	void initializeBattleAnimations(); | ||||
| 	void tickFrameBattleAnimations(uint32_t msPassed); | ||||
|  | ||||
| 	void updateBattleAnimations(uint32_t msPassed); | ||||
|  | ||||
| 	std::vector<const CStack *> selectHoveredStacks(); | ||||
|  | ||||
| 	bool shouldAttackFacingRight(const CStack * attacker, const CStack * defender); | ||||
|  | ||||
| public: | ||||
| 	BattleStacksController(BattleInterface & owner); | ||||
|  | ||||
| 	bool shouldRotate(const CStack * stack, const BattleHex & oldPos, const BattleHex & nextHex) const; | ||||
| 	bool facingRight(const CStack * stack) const; | ||||
|  | ||||
| 	void stackReset(const CStack * stack); | ||||
| 	void stackAdded(const CStack * stack, bool instant); //new stack appeared on battlefield | ||||
| 	void stackRemoved(uint32_t stackID); //stack disappeared from batlefiled | ||||
| 	void stackActivated(const CStack *stack); //active stack has been changed | ||||
| 	void stackMoved(const CStack *stack, const BattleHexArray & destHex, int distance); //stack with id number moved to destHex | ||||
| 	void stackTeleported(const CStack *stack, const BattleHexArray & destHex, int distance); //stack with id number moved to destHex | ||||
| 	void stacksAreAttacked(std::vector<StackAttackedInfo> attackedInfos); //called when a certain amount of stacks has been attacked | ||||
| 	void stackAttacking(const StackAttackInfo & info); //called when stack with id ID is attacking something on hex dest | ||||
|  | ||||
| 	void startAction(const BattleAction & action); | ||||
| 	void endAction(const BattleAction & action); | ||||
|  | ||||
| 	void deactivateStack(); //copy activeStack to stackToActivate, then set activeStack to nullptr to temporary disable current stack | ||||
|  | ||||
| 	void activateStack(); //copy stackToActivate to activeStack to enable controls of the stack | ||||
|  | ||||
| 	void setActiveStack(const CStack *stack); | ||||
|  | ||||
| 	void showAliveStack(Canvas & canvas, const CStack * stack); | ||||
| 	void showStack(Canvas & canvas, const CStack * stack); | ||||
|  | ||||
| 	void updateHoveredStacks(); | ||||
|  | ||||
| 	void collectRenderableObjects(BattleRenderer & renderer); | ||||
|  | ||||
| 	/// Adds new color filter effect targeting stack | ||||
| 	/// Effect will last as long as stack is affected by specified spell (unless effect is persistent) | ||||
| 	/// If effect from same (target, source) already exists, it will be updated | ||||
| 	void setStackColorFilter(const ColorFilter & effect, const CStack * target, const CSpell *source, bool persistent); | ||||
| 	void addNewAnim(BattleAnimation *anim); //adds new anim to pendingAnims | ||||
|  | ||||
| 	const CStack* getActiveStack() const; | ||||
| 	const std::vector<uint32_t> getHoveredStacksUnitIds() const; | ||||
|  | ||||
| 	void tick(uint32_t msPassed); | ||||
|  | ||||
| 	/// returns position of animation needed to place stack in specific hex | ||||
| 	Point getStackPositionAtHex(BattleHex hexNum, const CStack * creature) const; | ||||
|  | ||||
| 	friend class BattleAnimation; // for exposing pendingAnims/creAnims/creDir to animations | ||||
| }; | ||||
|   | ||||
| @@ -1,104 +1,104 @@ | ||||
| /* | ||||
|  * RNG.h, part of VCMI engine | ||||
|  * | ||||
|  * Authors: listed in file AUTHORS in main folder | ||||
|  * | ||||
|  * License: GNU General Public License v2.0 or later | ||||
|  * Full text of license available in license.txt file, in main folder | ||||
|  * | ||||
|  */ | ||||
|  | ||||
| #pragma once | ||||
|  | ||||
| VCMI_LIB_NAMESPACE_BEGIN | ||||
|  | ||||
| namespace vstd | ||||
| { | ||||
|  | ||||
| class DLL_LINKAGE RNG | ||||
| { | ||||
| public: | ||||
| 	virtual ~RNG() = default; | ||||
|  | ||||
| 	/// Returns random number in range [lower, upper] | ||||
| 	virtual int nextInt(int lower, int upper) = 0; | ||||
|  | ||||
| 	/// Returns random number in range [lower, upper] | ||||
| 	virtual int64_t nextInt64(int64_t lower, int64_t upper) = 0; | ||||
|  | ||||
| 	/// Returns random number in range [lower, upper] | ||||
| 	virtual double nextDouble(double lower, double upper) = 0; | ||||
|  | ||||
| 	/// Returns random number in range [0, upper] | ||||
| 	virtual int nextInt(int upper) = 0; | ||||
|  | ||||
| 	/// Returns random number in range [0, upper] | ||||
| 	virtual int64_t nextInt64(int64_t upper) = 0; | ||||
|  | ||||
| 	/// Returns random number in range [0, upper] | ||||
| 	virtual double nextDouble(double upper) = 0; | ||||
|  | ||||
| 	/// Generates an integer between 0 and the maximum value it can hold. | ||||
| 	/// Should be only used for seeding other generators | ||||
| 	virtual int nextInt() = 0; | ||||
|  | ||||
| 	/// Returns integer using binomial distribution | ||||
| 	/// returned value is number of successfull coin flips with chance 'coinChance' out of 'coinsCount' attempts | ||||
| 	virtual int nextBinomialInt(int coinsCount, double coinChance) = 0; | ||||
| }; | ||||
|  | ||||
| } | ||||
|  | ||||
| namespace RandomGeneratorUtil | ||||
| { | ||||
| 	template<typename Container> | ||||
| 	auto nextItem(const Container & container, vstd::RNG & rand) -> decltype(std::begin(container)) | ||||
| 	{ | ||||
| 		if(container.empty()) | ||||
| 			throw std::runtime_error("Unable to select random item from empty container!"); | ||||
|  | ||||
| 		return std::next(container.begin(), rand.nextInt64(0, container.size() - 1)); | ||||
| 	} | ||||
|  | ||||
| 	template<typename Container> | ||||
| 	auto nextItem(Container & container, vstd::RNG & rand) -> decltype(std::begin(container)) | ||||
| 	{ | ||||
| 		if(container.empty()) | ||||
| 			throw std::runtime_error("Unable to select random item from empty container!"); | ||||
|  | ||||
| 		return std::next(container.begin(), rand.nextInt64(0, container.size() - 1)); | ||||
| 	} | ||||
|  | ||||
| 	template<typename Container> | ||||
| 	size_t nextItemWeighted(Container & container, vstd::RNG & rand) | ||||
| 	{ | ||||
| 		assert(!container.empty()); | ||||
|  | ||||
| 		int64_t totalWeight = std::accumulate(container.begin(), container.end(), 0); | ||||
| 		assert(totalWeight > 0); | ||||
|  | ||||
| 		int64_t roll = rand.nextInt64(0, totalWeight - 1); | ||||
|  | ||||
| 		for (size_t i = 0; i < container.size(); ++i) | ||||
| 		{ | ||||
| 			roll -= container[i]; | ||||
| 			if(roll < 0) | ||||
| 				return i; | ||||
| 		} | ||||
| 		return container.size() - 1; | ||||
| 	} | ||||
|  | ||||
| 	template<typename Container> | ||||
| 	void randomShuffle(Container & container, vstd::RNG & rand) | ||||
| 	{ | ||||
| 		int64_t n = std::distance(container.begin(), container.end()); | ||||
|  | ||||
| 		for(int64_t i = n - 1; i > 0; --i) | ||||
| 		{ | ||||
| 			auto randIndex = rand.nextInt64(0, i); | ||||
| 			std::swap(*(container.begin() + i), *(container.begin() + randIndex)); | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| VCMI_LIB_NAMESPACE_END | ||||
| /* | ||||
|  * RNG.h, part of VCMI engine | ||||
|  * | ||||
|  * Authors: listed in file AUTHORS in main folder | ||||
|  * | ||||
|  * License: GNU General Public License v2.0 or later | ||||
|  * Full text of license available in license.txt file, in main folder | ||||
|  * | ||||
|  */ | ||||
|  | ||||
| #pragma once | ||||
|  | ||||
| VCMI_LIB_NAMESPACE_BEGIN | ||||
|  | ||||
| namespace vstd | ||||
| { | ||||
|  | ||||
| class DLL_LINKAGE RNG | ||||
| { | ||||
| public: | ||||
| 	virtual ~RNG() = default; | ||||
|  | ||||
| 	/// Returns random number in range [lower, upper] | ||||
| 	virtual int nextInt(int lower, int upper) = 0; | ||||
|  | ||||
| 	/// Returns random number in range [lower, upper] | ||||
| 	virtual int64_t nextInt64(int64_t lower, int64_t upper) = 0; | ||||
|  | ||||
| 	/// Returns random number in range [lower, upper] | ||||
| 	virtual double nextDouble(double lower, double upper) = 0; | ||||
|  | ||||
| 	/// Returns random number in range [0, upper] | ||||
| 	virtual int nextInt(int upper) = 0; | ||||
|  | ||||
| 	/// Returns random number in range [0, upper] | ||||
| 	virtual int64_t nextInt64(int64_t upper) = 0; | ||||
|  | ||||
| 	/// Returns random number in range [0, upper] | ||||
| 	virtual double nextDouble(double upper) = 0; | ||||
|  | ||||
| 	/// Generates an integer between 0 and the maximum value it can hold. | ||||
| 	/// Should be only used for seeding other generators | ||||
| 	virtual int nextInt() = 0; | ||||
|  | ||||
| 	/// Returns integer using binomial distribution | ||||
| 	/// returned value is number of successfull coin flips with chance 'coinChance' out of 'coinsCount' attempts | ||||
| 	virtual int nextBinomialInt(int coinsCount, double coinChance) = 0; | ||||
| }; | ||||
|  | ||||
| } | ||||
|  | ||||
| namespace RandomGeneratorUtil | ||||
| { | ||||
| 	template<typename Container> | ||||
| 	auto nextItem(const Container & container, vstd::RNG & rand) -> decltype(std::begin(container)) | ||||
| 	{ | ||||
| 		if(container.empty()) | ||||
| 			throw std::runtime_error("Unable to select random item from empty container!"); | ||||
|  | ||||
| 		return std::next(container.begin(), rand.nextInt64(0, container.size() - 1)); | ||||
| 	} | ||||
|  | ||||
| 	template<typename Container> | ||||
| 	auto nextItem(Container & container, vstd::RNG & rand) -> decltype(std::begin(container)) | ||||
| 	{ | ||||
| 		if(container.empty()) | ||||
| 			throw std::runtime_error("Unable to select random item from empty container!"); | ||||
|  | ||||
| 		return std::next(container.begin(), rand.nextInt64(0, container.size() - 1)); | ||||
| 	} | ||||
|  | ||||
| 	template<typename Container> | ||||
| 	size_t nextItemWeighted(Container & container, vstd::RNG & rand) | ||||
| 	{ | ||||
| 		assert(!container.empty()); | ||||
|  | ||||
| 		int64_t totalWeight = std::accumulate(container.begin(), container.end(), 0); | ||||
| 		assert(totalWeight > 0); | ||||
|  | ||||
| 		int64_t roll = rand.nextInt64(0, totalWeight - 1); | ||||
|  | ||||
| 		for (size_t i = 0; i < container.size(); ++i) | ||||
| 		{ | ||||
| 			roll -= container[i]; | ||||
| 			if(roll < 0) | ||||
| 				return i; | ||||
| 		} | ||||
| 		return container.size() - 1; | ||||
| 	} | ||||
|  | ||||
| 	template<typename Container> | ||||
| 	void randomShuffle(Container & container, vstd::RNG & rand) | ||||
| 	{ | ||||
| 		int64_t n = std::distance(container.begin(), container.end()); | ||||
|  | ||||
| 		for(int64_t i = n - 1; i > 0; --i) | ||||
| 		{ | ||||
| 			auto randIndex = rand.nextInt64(0, i); | ||||
| 			std::swap(*(container.begin() + i), *(container.begin() + randIndex)); | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| VCMI_LIB_NAMESPACE_END | ||||
|   | ||||
| @@ -1,101 +1,101 @@ | ||||
| /* | ||||
|  * BattleFieldHandler.cpp, part of VCMI engine | ||||
|  * | ||||
|  * Authors: listed in file AUTHORS in main folder | ||||
|  * | ||||
|  * License: GNU General Public License v2.0 or later | ||||
|  * Full text of license available in license.txt file, in main folder | ||||
|  * | ||||
|  */ | ||||
| #include "StdInc.h" | ||||
|  | ||||
| #include <vcmi/Entity.h> | ||||
| #include "BattleFieldHandler.h" | ||||
| #include "json/JsonBonus.h" | ||||
|  | ||||
| VCMI_LIB_NAMESPACE_BEGIN | ||||
|  | ||||
| std::shared_ptr<BattleFieldInfo> BattleFieldHandler::loadFromJson(const std::string & scope, const JsonNode & json, const std::string & identifier, size_t index) | ||||
| { | ||||
| 	assert(identifier.find(':') == std::string::npos); | ||||
|  | ||||
| 	auto info = std::make_shared<BattleFieldInfo>(BattleField(index), identifier); | ||||
|  | ||||
| 	info->modScope = scope; | ||||
| 	info->graphics = ImagePath::fromJson(json["graphics"]); | ||||
| 	info->icon = json["icon"].String(); | ||||
| 	info->name = json["name"].String(); | ||||
| 	for(const auto & b : json["bonuses"].Vector()) | ||||
| 	{ | ||||
| 		auto bonus = JsonUtils::parseBonus(b); | ||||
|  | ||||
| 		bonus->source = BonusSource::TERRAIN_OVERLAY; | ||||
| 		bonus->sid = BonusSourceID(info->getId()); | ||||
| 		bonus->duration = BonusDuration::ONE_BATTLE; | ||||
|  | ||||
| 		info->bonuses.push_back(bonus); | ||||
| 	} | ||||
|  | ||||
| 	info->isSpecial = json["isSpecial"].Bool(); | ||||
| 	for(auto node : json["impassableHexes"].Vector()) | ||||
| 		info->impassableHexes.insert(node.Integer()); | ||||
|  | ||||
| 	info->openingSoundFilename = AudioPath::fromJson(json["openingSound"]); | ||||
| 	info->musicFilename = AudioPath::fromJson(json["music"]); | ||||
|  | ||||
| 	return info; | ||||
| } | ||||
|  | ||||
| std::vector<JsonNode> BattleFieldHandler::loadLegacyData() | ||||
| { | ||||
| 	return std::vector<JsonNode>(); | ||||
| } | ||||
|  | ||||
| const std::vector<std::string> & BattleFieldHandler::getTypeNames() const | ||||
| { | ||||
| 	static const auto types = std::vector<std::string> { "battlefield" }; | ||||
|  | ||||
| 	return types; | ||||
| } | ||||
|  | ||||
| int32_t BattleFieldInfo::getIndex() const | ||||
| { | ||||
| 	return battlefield.getNum(); | ||||
| } | ||||
|  | ||||
| int32_t BattleFieldInfo::getIconIndex() const | ||||
| { | ||||
| 	return iconIndex; | ||||
| } | ||||
|  | ||||
| std::string BattleFieldInfo::getJsonKey() const | ||||
| { | ||||
| 	return modScope + ':' + identifier; | ||||
| } | ||||
|  | ||||
| std::string BattleFieldInfo::getModScope() const | ||||
| { | ||||
| 	return modScope; | ||||
| } | ||||
|  | ||||
| std::string BattleFieldInfo::getNameTextID() const | ||||
| { | ||||
| 	return name; | ||||
| } | ||||
|  | ||||
| std::string BattleFieldInfo::getNameTranslated() const | ||||
| { | ||||
| 	return name; // TODO? | ||||
| } | ||||
|  | ||||
| void BattleFieldInfo::registerIcons(const IconRegistar & cb) const | ||||
| { | ||||
| 	//cb(getIconIndex(), "BATTLEFIELD", icon); | ||||
| } | ||||
|  | ||||
| BattleField BattleFieldInfo::getId() const | ||||
| { | ||||
| 	return battlefield; | ||||
| } | ||||
|  | ||||
| VCMI_LIB_NAMESPACE_END | ||||
| /* | ||||
|  * BattleFieldHandler.cpp, part of VCMI engine | ||||
|  * | ||||
|  * Authors: listed in file AUTHORS in main folder | ||||
|  * | ||||
|  * License: GNU General Public License v2.0 or later | ||||
|  * Full text of license available in license.txt file, in main folder | ||||
|  * | ||||
|  */ | ||||
| #include "StdInc.h" | ||||
|  | ||||
| #include <vcmi/Entity.h> | ||||
| #include "BattleFieldHandler.h" | ||||
| #include "json/JsonBonus.h" | ||||
|  | ||||
| VCMI_LIB_NAMESPACE_BEGIN | ||||
|  | ||||
| std::shared_ptr<BattleFieldInfo> BattleFieldHandler::loadFromJson(const std::string & scope, const JsonNode & json, const std::string & identifier, size_t index) | ||||
| { | ||||
| 	assert(identifier.find(':') == std::string::npos); | ||||
|  | ||||
| 	auto info = std::make_shared<BattleFieldInfo>(BattleField(index), identifier); | ||||
|  | ||||
| 	info->modScope = scope; | ||||
| 	info->graphics = ImagePath::fromJson(json["graphics"]); | ||||
| 	info->icon = json["icon"].String(); | ||||
| 	info->name = json["name"].String(); | ||||
| 	for(const auto & b : json["bonuses"].Vector()) | ||||
| 	{ | ||||
| 		auto bonus = JsonUtils::parseBonus(b); | ||||
|  | ||||
| 		bonus->source = BonusSource::TERRAIN_OVERLAY; | ||||
| 		bonus->sid = BonusSourceID(info->getId()); | ||||
| 		bonus->duration = BonusDuration::ONE_BATTLE; | ||||
|  | ||||
| 		info->bonuses.push_back(bonus); | ||||
| 	} | ||||
|  | ||||
| 	info->isSpecial = json["isSpecial"].Bool(); | ||||
| 	for(auto node : json["impassableHexes"].Vector()) | ||||
| 		info->impassableHexes.insert(node.Integer()); | ||||
|  | ||||
| 	info->openingSoundFilename = AudioPath::fromJson(json["openingSound"]); | ||||
| 	info->musicFilename = AudioPath::fromJson(json["music"]); | ||||
|  | ||||
| 	return info; | ||||
| } | ||||
|  | ||||
| std::vector<JsonNode> BattleFieldHandler::loadLegacyData() | ||||
| { | ||||
| 	return std::vector<JsonNode>(); | ||||
| } | ||||
|  | ||||
| const std::vector<std::string> & BattleFieldHandler::getTypeNames() const | ||||
| { | ||||
| 	static const auto types = std::vector<std::string> { "battlefield" }; | ||||
|  | ||||
| 	return types; | ||||
| } | ||||
|  | ||||
| int32_t BattleFieldInfo::getIndex() const | ||||
| { | ||||
| 	return battlefield.getNum(); | ||||
| } | ||||
|  | ||||
| int32_t BattleFieldInfo::getIconIndex() const | ||||
| { | ||||
| 	return iconIndex; | ||||
| } | ||||
|  | ||||
| std::string BattleFieldInfo::getJsonKey() const | ||||
| { | ||||
| 	return modScope + ':' + identifier; | ||||
| } | ||||
|  | ||||
| std::string BattleFieldInfo::getModScope() const | ||||
| { | ||||
| 	return modScope; | ||||
| } | ||||
|  | ||||
| std::string BattleFieldInfo::getNameTextID() const | ||||
| { | ||||
| 	return name; | ||||
| } | ||||
|  | ||||
| std::string BattleFieldInfo::getNameTranslated() const | ||||
| { | ||||
| 	return name; // TODO? | ||||
| } | ||||
|  | ||||
| void BattleFieldInfo::registerIcons(const IconRegistar & cb) const | ||||
| { | ||||
| 	//cb(getIconIndex(), "BATTLEFIELD", icon); | ||||
| } | ||||
|  | ||||
| BattleField BattleFieldInfo::getId() const | ||||
| { | ||||
| 	return battlefield; | ||||
| } | ||||
|  | ||||
| VCMI_LIB_NAMESPACE_END | ||||
|   | ||||
| @@ -1,80 +1,80 @@ | ||||
| /* | ||||
|  * BattleFieldHandler.h, part of VCMI engine | ||||
|  * | ||||
|  * Authors: listed in file AUTHORS in main folder | ||||
|  * | ||||
|  * License: GNU General Public License v2.0 or later | ||||
|  * Full text of license available in license.txt file, in main folder | ||||
|  * | ||||
|  */ | ||||
| #pragma once | ||||
|  | ||||
| #include <vcmi/EntityService.h> | ||||
| #include <vcmi/Entity.h> | ||||
| #include "bonuses/Bonus.h" | ||||
| #include "GameConstants.h" | ||||
| #include "IHandlerBase.h" | ||||
| #include "battle/BattleHexArray.h" | ||||
| #include "filesystem/ResourcePath.h" | ||||
|  | ||||
| VCMI_LIB_NAMESPACE_BEGIN | ||||
|  | ||||
| class BattleFieldInfo : public EntityT<BattleField> | ||||
| { | ||||
| public: | ||||
| 	BattleField battlefield; | ||||
| 	std::vector<std::shared_ptr<Bonus>> bonuses; | ||||
| 	bool isSpecial; | ||||
| 	ImagePath graphics; | ||||
| 	std::string name; | ||||
| 	std::string modScope; | ||||
| 	std::string identifier; | ||||
| 	std::string icon; | ||||
| 	si32 iconIndex; | ||||
| 	BattleHexArray impassableHexes; | ||||
| 	AudioPath openingSoundFilename; | ||||
| 	AudioPath musicFilename; | ||||
|  | ||||
| 	BattleFieldInfo()  | ||||
| 		: BattleFieldInfo(BattleField::NONE, "") | ||||
| 	{ | ||||
| 	} | ||||
|  | ||||
| 	BattleFieldInfo(BattleField battlefield, std::string identifier): | ||||
| 		isSpecial(false), | ||||
| 		battlefield(battlefield), | ||||
| 		identifier(identifier), | ||||
| 		iconIndex(battlefield.getNum()), | ||||
| 		name(identifier) | ||||
| 	{ | ||||
| 	} | ||||
|  | ||||
| 	int32_t getIndex() const override; | ||||
| 	int32_t getIconIndex() const override; | ||||
| 	std::string getJsonKey() const override; | ||||
| 	std::string getModScope() const override; | ||||
| 	std::string getNameTextID() const override; | ||||
| 	std::string getNameTranslated() const override; | ||||
| 	void registerIcons(const IconRegistar & cb) const override; | ||||
| 	BattleField getId() const override; | ||||
| }; | ||||
|  | ||||
| class DLL_LINKAGE BattleFieldService : public EntityServiceT<BattleField, BattleFieldInfo> | ||||
| { | ||||
| public: | ||||
| }; | ||||
|  | ||||
| class BattleFieldHandler : public CHandlerBase<BattleField, BattleFieldInfo, BattleFieldInfo, BattleFieldService> | ||||
| { | ||||
| public: | ||||
| 	std::shared_ptr<BattleFieldInfo> loadFromJson( | ||||
| 		const std::string & scope, | ||||
| 		const JsonNode & json, | ||||
| 		const std::string & identifier, | ||||
| 		size_t index) override; | ||||
|  | ||||
| 	const std::vector<std::string> & getTypeNames() const override; | ||||
| 	std::vector<JsonNode> loadLegacyData() override; | ||||
| }; | ||||
|  | ||||
| VCMI_LIB_NAMESPACE_END | ||||
| /* | ||||
|  * BattleFieldHandler.h, part of VCMI engine | ||||
|  * | ||||
|  * Authors: listed in file AUTHORS in main folder | ||||
|  * | ||||
|  * License: GNU General Public License v2.0 or later | ||||
|  * Full text of license available in license.txt file, in main folder | ||||
|  * | ||||
|  */ | ||||
| #pragma once | ||||
|  | ||||
| #include <vcmi/EntityService.h> | ||||
| #include <vcmi/Entity.h> | ||||
| #include "bonuses/Bonus.h" | ||||
| #include "GameConstants.h" | ||||
| #include "IHandlerBase.h" | ||||
| #include "battle/BattleHexArray.h" | ||||
| #include "filesystem/ResourcePath.h" | ||||
|  | ||||
| VCMI_LIB_NAMESPACE_BEGIN | ||||
|  | ||||
| class BattleFieldInfo : public EntityT<BattleField> | ||||
| { | ||||
| public: | ||||
| 	BattleField battlefield; | ||||
| 	std::vector<std::shared_ptr<Bonus>> bonuses; | ||||
| 	bool isSpecial; | ||||
| 	ImagePath graphics; | ||||
| 	std::string name; | ||||
| 	std::string modScope; | ||||
| 	std::string identifier; | ||||
| 	std::string icon; | ||||
| 	si32 iconIndex; | ||||
| 	BattleHexArray impassableHexes; | ||||
| 	AudioPath openingSoundFilename; | ||||
| 	AudioPath musicFilename; | ||||
|  | ||||
| 	BattleFieldInfo()  | ||||
| 		: BattleFieldInfo(BattleField::NONE, "") | ||||
| 	{ | ||||
| 	} | ||||
|  | ||||
| 	BattleFieldInfo(BattleField battlefield, std::string identifier): | ||||
| 		isSpecial(false), | ||||
| 		battlefield(battlefield), | ||||
| 		identifier(identifier), | ||||
| 		iconIndex(battlefield.getNum()), | ||||
| 		name(identifier) | ||||
| 	{ | ||||
| 	} | ||||
|  | ||||
| 	int32_t getIndex() const override; | ||||
| 	int32_t getIconIndex() const override; | ||||
| 	std::string getJsonKey() const override; | ||||
| 	std::string getModScope() const override; | ||||
| 	std::string getNameTextID() const override; | ||||
| 	std::string getNameTranslated() const override; | ||||
| 	void registerIcons(const IconRegistar & cb) const override; | ||||
| 	BattleField getId() const override; | ||||
| }; | ||||
|  | ||||
| class DLL_LINKAGE BattleFieldService : public EntityServiceT<BattleField, BattleFieldInfo> | ||||
| { | ||||
| public: | ||||
| }; | ||||
|  | ||||
| class BattleFieldHandler : public CHandlerBase<BattleField, BattleFieldInfo, BattleFieldInfo, BattleFieldService> | ||||
| { | ||||
| public: | ||||
| 	std::shared_ptr<BattleFieldInfo> loadFromJson( | ||||
| 		const std::string & scope, | ||||
| 		const JsonNode & json, | ||||
| 		const std::string & identifier, | ||||
| 		size_t index) override; | ||||
|  | ||||
| 	const std::vector<std::string> & getTypeNames() const override; | ||||
| 	std::vector<JsonNode> loadLegacyData() override; | ||||
| }; | ||||
|  | ||||
| VCMI_LIB_NAMESPACE_END | ||||
|   | ||||
| @@ -1,129 +1,129 @@ | ||||
| /* | ||||
|  * ObstacleHandler.cpp, part of VCMI engine | ||||
|  * | ||||
|  * Authors: listed in file AUTHORS in main folder | ||||
|  * | ||||
|  * License: GNU General Public License v2.0 or later | ||||
|  * Full text of license available in license.txt file, in main folder | ||||
|  * | ||||
|  */ | ||||
| #include "StdInc.h" | ||||
| #include "ObstacleHandler.h" | ||||
| #include "BattleFieldHandler.h" | ||||
| #include "json/JsonNode.h" | ||||
| #include "modding/IdentifierStorage.h" | ||||
| #include "VCMI_Lib.h" | ||||
|  | ||||
| VCMI_LIB_NAMESPACE_BEGIN | ||||
|  | ||||
| int32_t ObstacleInfo::getIndex() const | ||||
| { | ||||
| 	return obstacle.getNum(); | ||||
| } | ||||
|  | ||||
| int32_t ObstacleInfo::getIconIndex() const | ||||
| { | ||||
| 	return iconIndex; | ||||
| } | ||||
|  | ||||
| std::string ObstacleInfo::getJsonKey() const | ||||
| { | ||||
| 	return modScope + ':' + identifier; | ||||
| } | ||||
|  | ||||
| std::string ObstacleInfo::getModScope() const | ||||
| { | ||||
| 	return modScope; | ||||
| } | ||||
|  | ||||
| std::string ObstacleInfo::getNameTranslated() const | ||||
| { | ||||
| 	return identifier; | ||||
| } | ||||
|  | ||||
| std::string ObstacleInfo::getNameTextID() const | ||||
| { | ||||
| 	return identifier; // TODO? | ||||
| } | ||||
|  | ||||
| void ObstacleInfo::registerIcons(const IconRegistar & cb) const | ||||
| { | ||||
| } | ||||
|  | ||||
| Obstacle ObstacleInfo::getId() const | ||||
| { | ||||
| 	return obstacle; | ||||
| } | ||||
|  | ||||
| BattleHexArray ObstacleInfo::getBlocked(BattleHex hex) const | ||||
| { | ||||
| 	if(isAbsoluteObstacle) | ||||
| 	{ | ||||
| 		assert(!hex.isValid()); | ||||
| 		return BattleHexArray(blockedTiles); | ||||
| 	} | ||||
| 	 | ||||
| 	BattleHexArray ret; | ||||
| 	for(int offset : blockedTiles) | ||||
| 	{ | ||||
| 		BattleHex toBlock = hex + offset; | ||||
| 		if((hex.getY() & 1) && !(toBlock.getY() & 1)) | ||||
| 			toBlock += BattleHex::LEFT; | ||||
| 		 | ||||
| 		if(!toBlock.isValid()) | ||||
| 			logGlobal->error("Misplaced obstacle!"); | ||||
| 		else | ||||
| 			ret.insert(toBlock); | ||||
| 	} | ||||
| 	 | ||||
| 	return ret; | ||||
| } | ||||
|  | ||||
| bool ObstacleInfo::isAppropriate(const TerrainId terrainType, const BattleField & battlefield) const | ||||
| { | ||||
| 	const auto * bgInfo = battlefield.getInfo(); | ||||
|  | ||||
| 	if(bgInfo->isSpecial) | ||||
| 		return vstd::contains(allowedSpecialBfields, bgInfo->identifier); | ||||
| 	 | ||||
| 	return vstd::contains(allowedTerrains, terrainType); | ||||
| } | ||||
|  | ||||
| std::shared_ptr<ObstacleInfo> ObstacleHandler::loadFromJson(const std::string & scope, const JsonNode & json, const std::string & identifier, size_t index) | ||||
| { | ||||
| 	assert(identifier.find(':') == std::string::npos); | ||||
|  | ||||
| 	auto info = std::make_shared<ObstacleInfo>(Obstacle(index), identifier); | ||||
| 	 | ||||
| 	info->modScope = scope; | ||||
| 	info->animation = AnimationPath::fromJson(json["animation"]); | ||||
| 	info->width = json["width"].Integer(); | ||||
| 	info->height = json["height"].Integer(); | ||||
| 	for(const auto & t : json["allowedTerrains"].Vector()) | ||||
| 	{ | ||||
| 		VLC->identifiers()->requestIdentifier("terrain", t, [info](int32_t identifier){ | ||||
| 			info->allowedTerrains.emplace_back(identifier); | ||||
| 		}); | ||||
| 	} | ||||
| 	for(const auto & t : json["specialBattlefields"].Vector()) | ||||
|  | ||||
| 		info->allowedSpecialBfields.emplace_back(t.String()); | ||||
| 	info->blockedTiles = json["blockedTiles"].convertTo<std::vector<si16>>(); | ||||
| 	info->isAbsoluteObstacle = json["absolute"].Bool(); | ||||
| 	info->isForegroundObstacle = json["foreground"].Bool(); | ||||
|  | ||||
| 	return info; | ||||
| } | ||||
|  | ||||
| std::vector<JsonNode> ObstacleHandler::loadLegacyData() | ||||
| { | ||||
| 	return {}; | ||||
| } | ||||
|  | ||||
| const std::vector<std::string> & ObstacleHandler::getTypeNames() const | ||||
| { | ||||
| 	static const std::vector<std::string> types = { "obstacle" }; | ||||
| 	return types; | ||||
| } | ||||
|  | ||||
| VCMI_LIB_NAMESPACE_END | ||||
| /* | ||||
|  * ObstacleHandler.cpp, part of VCMI engine | ||||
|  * | ||||
|  * Authors: listed in file AUTHORS in main folder | ||||
|  * | ||||
|  * License: GNU General Public License v2.0 or later | ||||
|  * Full text of license available in license.txt file, in main folder | ||||
|  * | ||||
|  */ | ||||
| #include "StdInc.h" | ||||
| #include "ObstacleHandler.h" | ||||
| #include "BattleFieldHandler.h" | ||||
| #include "json/JsonNode.h" | ||||
| #include "modding/IdentifierStorage.h" | ||||
| #include "VCMI_Lib.h" | ||||
|  | ||||
| VCMI_LIB_NAMESPACE_BEGIN | ||||
|  | ||||
| int32_t ObstacleInfo::getIndex() const | ||||
| { | ||||
| 	return obstacle.getNum(); | ||||
| } | ||||
|  | ||||
| int32_t ObstacleInfo::getIconIndex() const | ||||
| { | ||||
| 	return iconIndex; | ||||
| } | ||||
|  | ||||
| std::string ObstacleInfo::getJsonKey() const | ||||
| { | ||||
| 	return modScope + ':' + identifier; | ||||
| } | ||||
|  | ||||
| std::string ObstacleInfo::getModScope() const | ||||
| { | ||||
| 	return modScope; | ||||
| } | ||||
|  | ||||
| std::string ObstacleInfo::getNameTranslated() const | ||||
| { | ||||
| 	return identifier; | ||||
| } | ||||
|  | ||||
| std::string ObstacleInfo::getNameTextID() const | ||||
| { | ||||
| 	return identifier; // TODO? | ||||
| } | ||||
|  | ||||
| void ObstacleInfo::registerIcons(const IconRegistar & cb) const | ||||
| { | ||||
| } | ||||
|  | ||||
| Obstacle ObstacleInfo::getId() const | ||||
| { | ||||
| 	return obstacle; | ||||
| } | ||||
|  | ||||
| BattleHexArray ObstacleInfo::getBlocked(BattleHex hex) const | ||||
| { | ||||
| 	if(isAbsoluteObstacle) | ||||
| 	{ | ||||
| 		assert(!hex.isValid()); | ||||
| 		return BattleHexArray(blockedTiles); | ||||
| 	} | ||||
| 	 | ||||
| 	BattleHexArray ret; | ||||
| 	for(int offset : blockedTiles) | ||||
| 	{ | ||||
| 		BattleHex toBlock = hex + offset; | ||||
| 		if((hex.getY() & 1) && !(toBlock.getY() & 1)) | ||||
| 			toBlock += BattleHex::LEFT; | ||||
| 		 | ||||
| 		if(!toBlock.isValid()) | ||||
| 			logGlobal->error("Misplaced obstacle!"); | ||||
| 		else | ||||
| 			ret.insert(toBlock); | ||||
| 	} | ||||
| 	 | ||||
| 	return ret; | ||||
| } | ||||
|  | ||||
| bool ObstacleInfo::isAppropriate(const TerrainId terrainType, const BattleField & battlefield) const | ||||
| { | ||||
| 	const auto * bgInfo = battlefield.getInfo(); | ||||
|  | ||||
| 	if(bgInfo->isSpecial) | ||||
| 		return vstd::contains(allowedSpecialBfields, bgInfo->identifier); | ||||
| 	 | ||||
| 	return vstd::contains(allowedTerrains, terrainType); | ||||
| } | ||||
|  | ||||
| std::shared_ptr<ObstacleInfo> ObstacleHandler::loadFromJson(const std::string & scope, const JsonNode & json, const std::string & identifier, size_t index) | ||||
| { | ||||
| 	assert(identifier.find(':') == std::string::npos); | ||||
|  | ||||
| 	auto info = std::make_shared<ObstacleInfo>(Obstacle(index), identifier); | ||||
| 	 | ||||
| 	info->modScope = scope; | ||||
| 	info->animation = AnimationPath::fromJson(json["animation"]); | ||||
| 	info->width = json["width"].Integer(); | ||||
| 	info->height = json["height"].Integer(); | ||||
| 	for(const auto & t : json["allowedTerrains"].Vector()) | ||||
| 	{ | ||||
| 		VLC->identifiers()->requestIdentifier("terrain", t, [info](int32_t identifier){ | ||||
| 			info->allowedTerrains.emplace_back(identifier); | ||||
| 		}); | ||||
| 	} | ||||
| 	for(const auto & t : json["specialBattlefields"].Vector()) | ||||
|  | ||||
| 		info->allowedSpecialBfields.emplace_back(t.String()); | ||||
| 	info->blockedTiles = json["blockedTiles"].convertTo<std::vector<si16>>(); | ||||
| 	info->isAbsoluteObstacle = json["absolute"].Bool(); | ||||
| 	info->isForegroundObstacle = json["foreground"].Bool(); | ||||
|  | ||||
| 	return info; | ||||
| } | ||||
|  | ||||
| std::vector<JsonNode> ObstacleHandler::loadLegacyData() | ||||
| { | ||||
| 	return {}; | ||||
| } | ||||
|  | ||||
| const std::vector<std::string> & ObstacleHandler::getTypeNames() const | ||||
| { | ||||
| 	static const std::vector<std::string> types = { "obstacle" }; | ||||
| 	return types; | ||||
| } | ||||
|  | ||||
| VCMI_LIB_NAMESPACE_END | ||||
|   | ||||
| @@ -1,79 +1,79 @@ | ||||
| /* | ||||
|  * ObstacleHandler.h, part of VCMI engine | ||||
|  * | ||||
|  * Authors: listed in file AUTHORS in main folder | ||||
|  * | ||||
|  * License: GNU General Public License v2.0 or later | ||||
|  * Full text of license available in license.txt file, in main folder | ||||
|  * | ||||
|  */ | ||||
| #pragma once | ||||
|  | ||||
| #include <vcmi/EntityService.h> | ||||
| #include <vcmi/Entity.h> | ||||
| #include "GameConstants.h" | ||||
| #include "IHandlerBase.h" | ||||
| #include "battle/BattleHexArray.h" | ||||
| #include "filesystem/ResourcePath.h" | ||||
|  | ||||
| VCMI_LIB_NAMESPACE_BEGIN | ||||
|  | ||||
| class DLL_LINKAGE ObstacleInfo : public EntityT<Obstacle> | ||||
| { | ||||
| public: | ||||
| 	ObstacleInfo(): obstacle(-1), width(0), height(0), isAbsoluteObstacle(false), iconIndex(0), isForegroundObstacle(false) | ||||
| 	{} | ||||
| 	 | ||||
| 	ObstacleInfo(Obstacle obstacle, std::string identifier) | ||||
| 	: obstacle(obstacle), identifier(identifier), iconIndex(obstacle.getNum()), width(0), height(0), isAbsoluteObstacle(false), isForegroundObstacle(false) | ||||
| 	{ | ||||
| 	} | ||||
| 	 | ||||
| 	Obstacle obstacle; | ||||
| 	si32 iconIndex; | ||||
| 	std::string modScope; | ||||
| 	std::string identifier; | ||||
| 	AudioPath appearSound; | ||||
| 	AnimationPath appearAnimation; | ||||
| 	AnimationPath animation; | ||||
| 	std::vector<TerrainId> allowedTerrains; | ||||
| 	std::vector<std::string> allowedSpecialBfields; | ||||
| 	 | ||||
| 	bool isAbsoluteObstacle; //there may only one such obstacle in battle and its position is always the same | ||||
| 	bool isForegroundObstacle; | ||||
| 	si32 width; //how much space to the right and up is needed to place obstacle (affects only placement algorithm) | ||||
| 	si32 height; | ||||
| 	std::vector<si16> blockedTiles; //offsets relative to obstacle position (that is its left bottom corner) | ||||
| 	 | ||||
| 	int32_t getIndex() const override; | ||||
| 	int32_t getIconIndex() const override; | ||||
| 	std::string getJsonKey() const override; | ||||
| 	std::string getModScope() const override; | ||||
| 	std::string getNameTranslated() const override; | ||||
| 	std::string getNameTextID() const override; | ||||
| 	void registerIcons(const IconRegistar & cb) const override; | ||||
| 	Obstacle getId() const override; | ||||
| 	 | ||||
| 	BattleHexArray getBlocked(BattleHex hex) const; //returns vector of hexes blocked by obstacle when it's placed on hex 'hex' | ||||
| 	 | ||||
| 	bool isAppropriate(const TerrainId terrainType, const BattleField & specialBattlefield) const; | ||||
| }; | ||||
|  | ||||
| class DLL_LINKAGE ObstacleService : public EntityServiceT<Obstacle, ObstacleInfo> | ||||
| { | ||||
| public: | ||||
| }; | ||||
|  | ||||
| class ObstacleHandler: public CHandlerBase<Obstacle, ObstacleInfo, ObstacleInfo, ObstacleService> | ||||
| { | ||||
| public: | ||||
| 	std::shared_ptr<ObstacleInfo> loadFromJson(const std::string & scope, | ||||
| 										const JsonNode & json, | ||||
| 										const std::string & identifier, | ||||
| 										size_t index) override; | ||||
| 	 | ||||
| 	const std::vector<std::string> & getTypeNames() const override; | ||||
| 	std::vector<JsonNode> loadLegacyData() override; | ||||
| }; | ||||
|  | ||||
| VCMI_LIB_NAMESPACE_END | ||||
| /* | ||||
|  * ObstacleHandler.h, part of VCMI engine | ||||
|  * | ||||
|  * Authors: listed in file AUTHORS in main folder | ||||
|  * | ||||
|  * License: GNU General Public License v2.0 or later | ||||
|  * Full text of license available in license.txt file, in main folder | ||||
|  * | ||||
|  */ | ||||
| #pragma once | ||||
|  | ||||
| #include <vcmi/EntityService.h> | ||||
| #include <vcmi/Entity.h> | ||||
| #include "GameConstants.h" | ||||
| #include "IHandlerBase.h" | ||||
| #include "battle/BattleHexArray.h" | ||||
| #include "filesystem/ResourcePath.h" | ||||
|  | ||||
| VCMI_LIB_NAMESPACE_BEGIN | ||||
|  | ||||
| class DLL_LINKAGE ObstacleInfo : public EntityT<Obstacle> | ||||
| { | ||||
| public: | ||||
| 	ObstacleInfo(): obstacle(-1), width(0), height(0), isAbsoluteObstacle(false), iconIndex(0), isForegroundObstacle(false) | ||||
| 	{} | ||||
| 	 | ||||
| 	ObstacleInfo(Obstacle obstacle, std::string identifier) | ||||
| 	: obstacle(obstacle), identifier(identifier), iconIndex(obstacle.getNum()), width(0), height(0), isAbsoluteObstacle(false), isForegroundObstacle(false) | ||||
| 	{ | ||||
| 	} | ||||
| 	 | ||||
| 	Obstacle obstacle; | ||||
| 	si32 iconIndex; | ||||
| 	std::string modScope; | ||||
| 	std::string identifier; | ||||
| 	AudioPath appearSound; | ||||
| 	AnimationPath appearAnimation; | ||||
| 	AnimationPath animation; | ||||
| 	std::vector<TerrainId> allowedTerrains; | ||||
| 	std::vector<std::string> allowedSpecialBfields; | ||||
| 	 | ||||
| 	bool isAbsoluteObstacle; //there may only one such obstacle in battle and its position is always the same | ||||
| 	bool isForegroundObstacle; | ||||
| 	si32 width; //how much space to the right and up is needed to place obstacle (affects only placement algorithm) | ||||
| 	si32 height; | ||||
| 	std::vector<si16> blockedTiles; //offsets relative to obstacle position (that is its left bottom corner) | ||||
| 	 | ||||
| 	int32_t getIndex() const override; | ||||
| 	int32_t getIconIndex() const override; | ||||
| 	std::string getJsonKey() const override; | ||||
| 	std::string getModScope() const override; | ||||
| 	std::string getNameTranslated() const override; | ||||
| 	std::string getNameTextID() const override; | ||||
| 	void registerIcons(const IconRegistar & cb) const override; | ||||
| 	Obstacle getId() const override; | ||||
| 	 | ||||
| 	BattleHexArray getBlocked(BattleHex hex) const; //returns vector of hexes blocked by obstacle when it's placed on hex 'hex' | ||||
| 	 | ||||
| 	bool isAppropriate(const TerrainId terrainType, const BattleField & specialBattlefield) const; | ||||
| }; | ||||
|  | ||||
| class DLL_LINKAGE ObstacleService : public EntityServiceT<Obstacle, ObstacleInfo> | ||||
| { | ||||
| public: | ||||
| }; | ||||
|  | ||||
| class ObstacleHandler: public CHandlerBase<Obstacle, ObstacleInfo, ObstacleInfo, ObstacleService> | ||||
| { | ||||
| public: | ||||
| 	std::shared_ptr<ObstacleInfo> loadFromJson(const std::string & scope, | ||||
| 										const JsonNode & json, | ||||
| 										const std::string & identifier, | ||||
| 										size_t index) override; | ||||
| 	 | ||||
| 	const std::vector<std::string> & getTypeNames() const override; | ||||
| 	std::vector<JsonNode> loadLegacyData() override; | ||||
| }; | ||||
|  | ||||
| VCMI_LIB_NAMESPACE_END | ||||
|   | ||||
| @@ -1,138 +1,138 @@ | ||||
| /* | ||||
|  * BattleHex.cpp, part of VCMI engine | ||||
|  * | ||||
|  * Authors: listed in file AUTHORS in main folder | ||||
|  * | ||||
|  * License: GNU General Public License v2.0 or later | ||||
|  * Full text of license available in license.txt file, in main folder | ||||
|  * | ||||
|  */ | ||||
| #include "StdInc.h" | ||||
| #include "BattleHex.h" | ||||
|  | ||||
| VCMI_LIB_NAMESPACE_BEGIN | ||||
|  | ||||
| BattleHex::BattleHex() : hex(INVALID) {} | ||||
|  | ||||
| BattleHex::BattleHex(si16 _hex) : hex(_hex) {} | ||||
|  | ||||
| BattleHex::BattleHex(si16 x, si16 y) | ||||
| { | ||||
| 	setXY(x, y); | ||||
| } | ||||
|  | ||||
| BattleHex::BattleHex(std::pair<si16, si16> xy) | ||||
| { | ||||
| 	setXY(xy); | ||||
| } | ||||
|  | ||||
| BattleHex::operator si16() const | ||||
| { | ||||
| 	return hex; | ||||
| } | ||||
|  | ||||
| void BattleHex::setX(si16 x) | ||||
| { | ||||
| 	setXY(x, getY()); | ||||
| } | ||||
|  | ||||
| void BattleHex::setY(si16 y) | ||||
| { | ||||
| 	setXY(getX(), y); | ||||
| } | ||||
|  | ||||
| void BattleHex::setXY(si16 x, si16 y, bool hasToBeValid) | ||||
| { | ||||
| 	if(hasToBeValid) | ||||
| 	{ | ||||
| 		if(x < 0 || x >= GameConstants::BFIELD_WIDTH || y < 0 || y >= GameConstants::BFIELD_HEIGHT) | ||||
| 			throw std::runtime_error("Valid hex required"); | ||||
| 	} | ||||
|  | ||||
| 	hex = x + y * GameConstants::BFIELD_WIDTH; | ||||
| } | ||||
|  | ||||
| void BattleHex::setXY(std::pair<si16, si16> xy) | ||||
| { | ||||
| 	setXY(xy.first, xy.second); | ||||
| } | ||||
|  | ||||
| si16 BattleHex::getX() const | ||||
| { | ||||
| 	return hex % GameConstants::BFIELD_WIDTH; | ||||
| } | ||||
|  | ||||
| si16 BattleHex::getY() const | ||||
| { | ||||
| 	return hex / GameConstants::BFIELD_WIDTH; | ||||
| } | ||||
|  | ||||
| std::pair<si16, si16> BattleHex::getXY() const | ||||
| { | ||||
| 	return std::make_pair(getX(), getY()); | ||||
| } | ||||
|  | ||||
| BattleHex & BattleHex::moveInDirection(EDir dir, bool hasToBeValid) | ||||
| { | ||||
| 	si16 x = getX(); | ||||
| 	si16 y = getY(); | ||||
| 	switch(dir) | ||||
| 	{ | ||||
| 	case TOP_LEFT: | ||||
| 		setXY((y%2) ? x-1 : x, y-1, hasToBeValid); | ||||
| 		break; | ||||
| 	case TOP_RIGHT: | ||||
| 		setXY((y%2) ? x : x+1, y-1, hasToBeValid); | ||||
| 		break; | ||||
| 	case RIGHT: | ||||
| 		setXY(x+1, y, hasToBeValid); | ||||
| 		break; | ||||
| 	case BOTTOM_RIGHT: | ||||
| 		setXY((y%2) ? x : x+1, y+1, hasToBeValid); | ||||
| 		break; | ||||
| 	case BOTTOM_LEFT: | ||||
| 		setXY((y%2) ? x-1 : x, y+1, hasToBeValid); | ||||
| 		break; | ||||
| 	case LEFT: | ||||
| 		setXY(x-1, y, hasToBeValid); | ||||
| 		break; | ||||
| 	case NONE: | ||||
| 		break; | ||||
| 	default: | ||||
| 		throw std::runtime_error("Disaster: wrong direction in BattleHex::operator+=!\n"); | ||||
| 		break; | ||||
| 	} | ||||
| 	return *this; | ||||
| } | ||||
|  | ||||
| BattleHex & BattleHex::operator+=(BattleHex::EDir dir) | ||||
| { | ||||
| 	return moveInDirection(dir); | ||||
| } | ||||
|  | ||||
| BattleHex BattleHex::cloneInDirection(BattleHex::EDir dir, bool hasToBeValid) const | ||||
| { | ||||
| 	BattleHex result(hex); | ||||
| 	result.moveInDirection(dir, hasToBeValid); | ||||
| 	return result; | ||||
| } | ||||
|  | ||||
| BattleHex BattleHex::operator+(BattleHex::EDir dir) const | ||||
| { | ||||
| 	return cloneInDirection(dir); | ||||
| } | ||||
|  | ||||
| BattleHex::EDir BattleHex::mutualPosition(BattleHex hex1, BattleHex hex2) | ||||
| { | ||||
| 	for(auto dir : hexagonalDirections()) | ||||
| 		if(hex2 == hex1.cloneInDirection(dir, false)) | ||||
| 			return dir; | ||||
| 	return NONE; | ||||
| } | ||||
|  | ||||
| std::ostream & operator<<(std::ostream & os, const BattleHex & hex) | ||||
| { | ||||
| 	return os << boost::str(boost::format("{BattleHex: x '%d', y '%d', hex '%d'}") % hex.getX() % hex.getY() % hex.hex); | ||||
| } | ||||
|  | ||||
| VCMI_LIB_NAMESPACE_END | ||||
| /* | ||||
|  * BattleHex.cpp, part of VCMI engine | ||||
|  * | ||||
|  * Authors: listed in file AUTHORS in main folder | ||||
|  * | ||||
|  * License: GNU General Public License v2.0 or later | ||||
|  * Full text of license available in license.txt file, in main folder | ||||
|  * | ||||
|  */ | ||||
| #include "StdInc.h" | ||||
| #include "BattleHex.h" | ||||
|  | ||||
| VCMI_LIB_NAMESPACE_BEGIN | ||||
|  | ||||
| BattleHex::BattleHex() : hex(INVALID) {} | ||||
|  | ||||
| BattleHex::BattleHex(si16 _hex) : hex(_hex) {} | ||||
|  | ||||
| BattleHex::BattleHex(si16 x, si16 y) | ||||
| { | ||||
| 	setXY(x, y); | ||||
| } | ||||
|  | ||||
| BattleHex::BattleHex(std::pair<si16, si16> xy) | ||||
| { | ||||
| 	setXY(xy); | ||||
| } | ||||
|  | ||||
| BattleHex::operator si16() const | ||||
| { | ||||
| 	return hex; | ||||
| } | ||||
|  | ||||
| void BattleHex::setX(si16 x) | ||||
| { | ||||
| 	setXY(x, getY()); | ||||
| } | ||||
|  | ||||
| void BattleHex::setY(si16 y) | ||||
| { | ||||
| 	setXY(getX(), y); | ||||
| } | ||||
|  | ||||
| void BattleHex::setXY(si16 x, si16 y, bool hasToBeValid) | ||||
| { | ||||
| 	if(hasToBeValid) | ||||
| 	{ | ||||
| 		if(x < 0 || x >= GameConstants::BFIELD_WIDTH || y < 0 || y >= GameConstants::BFIELD_HEIGHT) | ||||
| 			throw std::runtime_error("Valid hex required"); | ||||
| 	} | ||||
|  | ||||
| 	hex = x + y * GameConstants::BFIELD_WIDTH; | ||||
| } | ||||
|  | ||||
| void BattleHex::setXY(std::pair<si16, si16> xy) | ||||
| { | ||||
| 	setXY(xy.first, xy.second); | ||||
| } | ||||
|  | ||||
| si16 BattleHex::getX() const | ||||
| { | ||||
| 	return hex % GameConstants::BFIELD_WIDTH; | ||||
| } | ||||
|  | ||||
| si16 BattleHex::getY() const | ||||
| { | ||||
| 	return hex / GameConstants::BFIELD_WIDTH; | ||||
| } | ||||
|  | ||||
| std::pair<si16, si16> BattleHex::getXY() const | ||||
| { | ||||
| 	return std::make_pair(getX(), getY()); | ||||
| } | ||||
|  | ||||
| BattleHex & BattleHex::moveInDirection(EDir dir, bool hasToBeValid) | ||||
| { | ||||
| 	si16 x = getX(); | ||||
| 	si16 y = getY(); | ||||
| 	switch(dir) | ||||
| 	{ | ||||
| 	case TOP_LEFT: | ||||
| 		setXY((y%2) ? x-1 : x, y-1, hasToBeValid); | ||||
| 		break; | ||||
| 	case TOP_RIGHT: | ||||
| 		setXY((y%2) ? x : x+1, y-1, hasToBeValid); | ||||
| 		break; | ||||
| 	case RIGHT: | ||||
| 		setXY(x+1, y, hasToBeValid); | ||||
| 		break; | ||||
| 	case BOTTOM_RIGHT: | ||||
| 		setXY((y%2) ? x : x+1, y+1, hasToBeValid); | ||||
| 		break; | ||||
| 	case BOTTOM_LEFT: | ||||
| 		setXY((y%2) ? x-1 : x, y+1, hasToBeValid); | ||||
| 		break; | ||||
| 	case LEFT: | ||||
| 		setXY(x-1, y, hasToBeValid); | ||||
| 		break; | ||||
| 	case NONE: | ||||
| 		break; | ||||
| 	default: | ||||
| 		throw std::runtime_error("Disaster: wrong direction in BattleHex::operator+=!\n"); | ||||
| 		break; | ||||
| 	} | ||||
| 	return *this; | ||||
| } | ||||
|  | ||||
| BattleHex & BattleHex::operator+=(BattleHex::EDir dir) | ||||
| { | ||||
| 	return moveInDirection(dir); | ||||
| } | ||||
|  | ||||
| BattleHex BattleHex::cloneInDirection(BattleHex::EDir dir, bool hasToBeValid) const | ||||
| { | ||||
| 	BattleHex result(hex); | ||||
| 	result.moveInDirection(dir, hasToBeValid); | ||||
| 	return result; | ||||
| } | ||||
|  | ||||
| BattleHex BattleHex::operator+(BattleHex::EDir dir) const | ||||
| { | ||||
| 	return cloneInDirection(dir); | ||||
| } | ||||
|  | ||||
| BattleHex::EDir BattleHex::mutualPosition(BattleHex hex1, BattleHex hex2) | ||||
| { | ||||
| 	for(auto dir : hexagonalDirections()) | ||||
| 		if(hex2 == hex1.cloneInDirection(dir, false)) | ||||
| 			return dir; | ||||
| 	return NONE; | ||||
| } | ||||
|  | ||||
| std::ostream & operator<<(std::ostream & os, const BattleHex & hex) | ||||
| { | ||||
| 	return os << boost::str(boost::format("{BattleHex: x '%d', y '%d', hex '%d'}") % hex.getX() % hex.getY() % hex.hex); | ||||
| } | ||||
|  | ||||
| VCMI_LIB_NAMESPACE_END | ||||
|   | ||||
| @@ -1,127 +1,127 @@ | ||||
| /* | ||||
|  * BattleHex.h, part of VCMI engine | ||||
|  * | ||||
|  * Authors: listed in file AUTHORS in main folder | ||||
|  * | ||||
|  * License: GNU General Public License v2.0 or later | ||||
|  * Full text of license available in license.txt file, in main folder | ||||
|  * | ||||
|  */ | ||||
| #pragma once | ||||
|  | ||||
| #include "BattleSide.h" | ||||
|  | ||||
| VCMI_LIB_NAMESPACE_BEGIN | ||||
|  | ||||
| //TODO: change to enum class | ||||
|  | ||||
| namespace GameConstants | ||||
| { | ||||
| 	const int BFIELD_WIDTH = 17; | ||||
| 	const int BFIELD_HEIGHT = 11; | ||||
| 	const int BFIELD_SIZE = BFIELD_WIDTH * BFIELD_HEIGHT; | ||||
| } | ||||
|  | ||||
| // for battle stacks' positions | ||||
| struct DLL_LINKAGE BattleHex //TODO: decide if this should be changed to class for better code design | ||||
| { | ||||
| 	// helpers for siege | ||||
| 	static constexpr si16 CASTLE_CENTRAL_TOWER = -2; | ||||
| 	static constexpr si16 CASTLE_BOTTOM_TOWER = -3; | ||||
| 	static constexpr si16 CASTLE_UPPER_TOWER = -4; | ||||
|  | ||||
| 	// hexes for interaction with heroes | ||||
| 	static constexpr si16 HERO_ATTACKER = 0; | ||||
| 	static constexpr si16 HERO_DEFENDER = GameConstants::BFIELD_WIDTH - 1; | ||||
|  | ||||
| 	// helpers for rendering | ||||
| 	static constexpr si16 HEX_BEFORE_ALL = std::numeric_limits<si16>::min(); | ||||
| 	static constexpr si16 HEX_AFTER_ALL = std::numeric_limits<si16>::max(); | ||||
|  | ||||
| 	static constexpr si16 DESTRUCTIBLE_WALL_1 = 29; | ||||
| 	static constexpr si16 DESTRUCTIBLE_WALL_2 = 78; | ||||
| 	static constexpr si16 DESTRUCTIBLE_WALL_3 = 130; | ||||
| 	static constexpr si16 DESTRUCTIBLE_WALL_4 = 182; | ||||
| 	static constexpr si16 GATE_BRIDGE = 94; | ||||
| 	static constexpr si16 GATE_OUTER = 95; | ||||
| 	static constexpr si16 GATE_INNER = 96; | ||||
|  | ||||
| 	si16 hex; | ||||
| 	static constexpr si16 INVALID = -1; | ||||
| 	enum EDir | ||||
| 	{ | ||||
| 		NONE = -1, | ||||
|  | ||||
| 		TOP_LEFT, | ||||
| 		TOP_RIGHT, | ||||
| 		RIGHT, | ||||
| 		BOTTOM_RIGHT, | ||||
| 		BOTTOM_LEFT, | ||||
| 		LEFT, | ||||
|  | ||||
| 		//Note: unused by BattleHex class, used by other code | ||||
| 		TOP, | ||||
| 		BOTTOM | ||||
| 	}; | ||||
|  | ||||
| 	BattleHex(); | ||||
| 	BattleHex(si16 _hex); | ||||
| 	BattleHex(si16 x, si16 y); | ||||
| 	BattleHex(std::pair<si16, si16> xy); | ||||
| 	operator si16() const; | ||||
| 	inline bool isValid() const | ||||
| 	{ | ||||
| 		return hex >= 0 && hex < GameConstants::BFIELD_SIZE; | ||||
| 	} | ||||
| 	 | ||||
| 	bool isAvailable() const //valid position not in first or last column | ||||
| 	{ | ||||
| 		return isValid() && getX() > 0 && getX() < GameConstants::BFIELD_WIDTH - 1; | ||||
| 	} | ||||
|  | ||||
| 	void setX(si16 x); | ||||
| 	void setY(si16 y); | ||||
| 	void setXY(si16 x, si16 y, bool hasToBeValid = true); | ||||
| 	void setXY(std::pair<si16, si16> xy); | ||||
| 	si16 getX() const; | ||||
| 	si16 getY() const; | ||||
| 	std::pair<si16, si16> getXY() const; | ||||
| 	BattleHex& moveInDirection(EDir dir, bool hasToBeValid = true); | ||||
| 	BattleHex& operator+=(EDir dir); | ||||
| 	BattleHex cloneInDirection(EDir dir, bool hasToBeValid = true) const; | ||||
| 	BattleHex operator+(EDir dir) const; | ||||
|  | ||||
| 	static EDir mutualPosition(BattleHex hex1, BattleHex hex2); | ||||
| 	static uint8_t getDistance(BattleHex hex1, BattleHex hex2) | ||||
| 	{ | ||||
| 		int y1 = hex1.getY(); | ||||
| 		int y2 = hex2.getY(); | ||||
|  | ||||
| 		// FIXME: why there was * 0.5 instead of / 2? | ||||
| 		int x1 = static_cast<int>(hex1.getX() + y1 / 2); | ||||
| 		int x2 = static_cast<int>(hex2.getX() + y2 / 2); | ||||
|  | ||||
| 		int xDst = x2 - x1; | ||||
| 		int yDst = y2 - y1; | ||||
|  | ||||
| 		if((xDst >= 0 && yDst >= 0) || (xDst < 0 && yDst < 0)) | ||||
| 			return std::max(std::abs(xDst), std::abs(yDst)); | ||||
|  | ||||
| 		return std::abs(xDst) + std::abs(yDst); | ||||
| 	} | ||||
| 	 | ||||
| 	template <typename Handler> | ||||
| 	void serialize(Handler &h) | ||||
| 	{ | ||||
| 		h & hex; | ||||
| 	} | ||||
|  | ||||
| 	//Constexpr defined array with all directions used in battle | ||||
| 	static constexpr auto hexagonalDirections() { | ||||
| 		return std::array<EDir,6>{BattleHex::TOP_LEFT, BattleHex::TOP_RIGHT, BattleHex::RIGHT, BattleHex::BOTTOM_RIGHT, BattleHex::BOTTOM_LEFT, BattleHex::LEFT}; | ||||
| 	} | ||||
| }; | ||||
|  | ||||
| DLL_EXPORT std::ostream & operator<<(std::ostream & os, const BattleHex & hex); | ||||
|  | ||||
| VCMI_LIB_NAMESPACE_END | ||||
| /* | ||||
|  * BattleHex.h, part of VCMI engine | ||||
|  * | ||||
|  * Authors: listed in file AUTHORS in main folder | ||||
|  * | ||||
|  * License: GNU General Public License v2.0 or later | ||||
|  * Full text of license available in license.txt file, in main folder | ||||
|  * | ||||
|  */ | ||||
| #pragma once | ||||
|  | ||||
| #include "BattleSide.h" | ||||
|  | ||||
| VCMI_LIB_NAMESPACE_BEGIN | ||||
|  | ||||
| //TODO: change to enum class | ||||
|  | ||||
| namespace GameConstants | ||||
| { | ||||
| 	const int BFIELD_WIDTH = 17; | ||||
| 	const int BFIELD_HEIGHT = 11; | ||||
| 	const int BFIELD_SIZE = BFIELD_WIDTH * BFIELD_HEIGHT; | ||||
| } | ||||
|  | ||||
| // for battle stacks' positions | ||||
| struct DLL_LINKAGE BattleHex //TODO: decide if this should be changed to class for better code design | ||||
| { | ||||
| 	// helpers for siege | ||||
| 	static constexpr si16 CASTLE_CENTRAL_TOWER = -2; | ||||
| 	static constexpr si16 CASTLE_BOTTOM_TOWER = -3; | ||||
| 	static constexpr si16 CASTLE_UPPER_TOWER = -4; | ||||
|  | ||||
| 	// hexes for interaction with heroes | ||||
| 	static constexpr si16 HERO_ATTACKER = 0; | ||||
| 	static constexpr si16 HERO_DEFENDER = GameConstants::BFIELD_WIDTH - 1; | ||||
|  | ||||
| 	// helpers for rendering | ||||
| 	static constexpr si16 HEX_BEFORE_ALL = std::numeric_limits<si16>::min(); | ||||
| 	static constexpr si16 HEX_AFTER_ALL = std::numeric_limits<si16>::max(); | ||||
|  | ||||
| 	static constexpr si16 DESTRUCTIBLE_WALL_1 = 29; | ||||
| 	static constexpr si16 DESTRUCTIBLE_WALL_2 = 78; | ||||
| 	static constexpr si16 DESTRUCTIBLE_WALL_3 = 130; | ||||
| 	static constexpr si16 DESTRUCTIBLE_WALL_4 = 182; | ||||
| 	static constexpr si16 GATE_BRIDGE = 94; | ||||
| 	static constexpr si16 GATE_OUTER = 95; | ||||
| 	static constexpr si16 GATE_INNER = 96; | ||||
|  | ||||
| 	si16 hex; | ||||
| 	static constexpr si16 INVALID = -1; | ||||
| 	enum EDir | ||||
| 	{ | ||||
| 		NONE = -1, | ||||
|  | ||||
| 		TOP_LEFT, | ||||
| 		TOP_RIGHT, | ||||
| 		RIGHT, | ||||
| 		BOTTOM_RIGHT, | ||||
| 		BOTTOM_LEFT, | ||||
| 		LEFT, | ||||
|  | ||||
| 		//Note: unused by BattleHex class, used by other code | ||||
| 		TOP, | ||||
| 		BOTTOM | ||||
| 	}; | ||||
|  | ||||
| 	BattleHex(); | ||||
| 	BattleHex(si16 _hex); | ||||
| 	BattleHex(si16 x, si16 y); | ||||
| 	BattleHex(std::pair<si16, si16> xy); | ||||
| 	operator si16() const; | ||||
| 	inline bool isValid() const | ||||
| 	{ | ||||
| 		return hex >= 0 && hex < GameConstants::BFIELD_SIZE; | ||||
| 	} | ||||
| 	 | ||||
| 	bool isAvailable() const //valid position not in first or last column | ||||
| 	{ | ||||
| 		return isValid() && getX() > 0 && getX() < GameConstants::BFIELD_WIDTH - 1; | ||||
| 	} | ||||
|  | ||||
| 	void setX(si16 x); | ||||
| 	void setY(si16 y); | ||||
| 	void setXY(si16 x, si16 y, bool hasToBeValid = true); | ||||
| 	void setXY(std::pair<si16, si16> xy); | ||||
| 	si16 getX() const; | ||||
| 	si16 getY() const; | ||||
| 	std::pair<si16, si16> getXY() const; | ||||
| 	BattleHex& moveInDirection(EDir dir, bool hasToBeValid = true); | ||||
| 	BattleHex& operator+=(EDir dir); | ||||
| 	BattleHex cloneInDirection(EDir dir, bool hasToBeValid = true) const; | ||||
| 	BattleHex operator+(EDir dir) const; | ||||
|  | ||||
| 	static EDir mutualPosition(BattleHex hex1, BattleHex hex2); | ||||
| 	static uint8_t getDistance(BattleHex hex1, BattleHex hex2) | ||||
| 	{ | ||||
| 		int y1 = hex1.getY(); | ||||
| 		int y2 = hex2.getY(); | ||||
|  | ||||
| 		// FIXME: why there was * 0.5 instead of / 2? | ||||
| 		int x1 = static_cast<int>(hex1.getX() + y1 / 2); | ||||
| 		int x2 = static_cast<int>(hex2.getX() + y2 / 2); | ||||
|  | ||||
| 		int xDst = x2 - x1; | ||||
| 		int yDst = y2 - y1; | ||||
|  | ||||
| 		if((xDst >= 0 && yDst >= 0) || (xDst < 0 && yDst < 0)) | ||||
| 			return std::max(std::abs(xDst), std::abs(yDst)); | ||||
|  | ||||
| 		return std::abs(xDst) + std::abs(yDst); | ||||
| 	} | ||||
| 	 | ||||
| 	template <typename Handler> | ||||
| 	void serialize(Handler &h) | ||||
| 	{ | ||||
| 		h & hex; | ||||
| 	} | ||||
|  | ||||
| 	//Constexpr defined array with all directions used in battle | ||||
| 	static constexpr auto hexagonalDirections() { | ||||
| 		return std::array<EDir,6>{BattleHex::TOP_LEFT, BattleHex::TOP_RIGHT, BattleHex::RIGHT, BattleHex::BOTTOM_RIGHT, BattleHex::BOTTOM_LEFT, BattleHex::LEFT}; | ||||
| 	} | ||||
| }; | ||||
|  | ||||
| DLL_EXPORT std::ostream & operator<<(std::ostream & os, const BattleHex & hex); | ||||
|  | ||||
| VCMI_LIB_NAMESPACE_END | ||||
|   | ||||
| @@ -1,43 +1,43 @@ | ||||
| /* | ||||
|  * Destination.h, part of VCMI engine | ||||
|  * | ||||
|  * Authors: listed in file AUTHORS in main folder | ||||
|  * | ||||
|  * License: GNU General Public License v2.0 or later | ||||
|  * Full text of license available in license.txt file, in main folder | ||||
|  * | ||||
|  */ | ||||
|  | ||||
| #pragma once | ||||
|  | ||||
| #include "BattleHexArray.h" | ||||
|  | ||||
| VCMI_LIB_NAMESPACE_BEGIN | ||||
|  | ||||
| namespace battle | ||||
| { | ||||
|  | ||||
| class Unit; | ||||
|  | ||||
| class DLL_LINKAGE Destination | ||||
| { | ||||
| public: | ||||
| 	Destination(); | ||||
| 	~Destination() = default; | ||||
| 	explicit Destination(const Unit * destination); | ||||
| 	explicit Destination(const BattleHex & destination); | ||||
| 	explicit Destination(const Unit * destination, const BattleHex & exactHex); | ||||
|  | ||||
| 	Destination(const Destination & other) = default; | ||||
|  | ||||
| 	Destination & operator=(const Destination & other) = default; | ||||
|  | ||||
| 	const Unit * unitValue; | ||||
| 	BattleHex hexValue; | ||||
| }; | ||||
|  | ||||
| using Target = std::vector<Destination>; | ||||
|  | ||||
| } | ||||
|  | ||||
| VCMI_LIB_NAMESPACE_END | ||||
| /* | ||||
|  * Destination.h, part of VCMI engine | ||||
|  * | ||||
|  * Authors: listed in file AUTHORS in main folder | ||||
|  * | ||||
|  * License: GNU General Public License v2.0 or later | ||||
|  * Full text of license available in license.txt file, in main folder | ||||
|  * | ||||
|  */ | ||||
|  | ||||
| #pragma once | ||||
|  | ||||
| #include "BattleHexArray.h" | ||||
|  | ||||
| VCMI_LIB_NAMESPACE_BEGIN | ||||
|  | ||||
| namespace battle | ||||
| { | ||||
|  | ||||
| class Unit; | ||||
|  | ||||
| class DLL_LINKAGE Destination | ||||
| { | ||||
| public: | ||||
| 	Destination(); | ||||
| 	~Destination() = default; | ||||
| 	explicit Destination(const Unit * destination); | ||||
| 	explicit Destination(const BattleHex & destination); | ||||
| 	explicit Destination(const Unit * destination, const BattleHex & exactHex); | ||||
|  | ||||
| 	Destination(const Destination & other) = default; | ||||
|  | ||||
| 	Destination & operator=(const Destination & other) = default; | ||||
|  | ||||
| 	const Unit * unitValue; | ||||
| 	BattleHex hexValue; | ||||
| }; | ||||
|  | ||||
| using Target = std::vector<Destination>; | ||||
|  | ||||
| } | ||||
|  | ||||
| VCMI_LIB_NAMESPACE_END | ||||
|   | ||||
| @@ -1,89 +1,89 @@ | ||||
| /* | ||||
|  * IBattleInfoCallback.h, part of VCMI engine | ||||
|  * | ||||
|  * Authors: listed in file AUTHORS in main folder | ||||
|  * | ||||
|  * License: GNU General Public License v2.0 or later | ||||
|  * Full text of license available in license.txt file, in main folder | ||||
|  * | ||||
|  */ | ||||
|  | ||||
| #pragma once | ||||
|  | ||||
| #include "GameConstants.h" | ||||
| #include "BattleHexArray.h" | ||||
|  | ||||
| #include <vcmi/Entity.h> | ||||
|  | ||||
| #define RETURN_IF_NOT_BATTLE(...) do { if(!duringBattle()) {logGlobal->error("%s called when no battle!", __FUNCTION__); return __VA_ARGS__; } } while (false) | ||||
|  | ||||
| VCMI_LIB_NAMESPACE_BEGIN | ||||
|  | ||||
| struct CObstacleInstance; | ||||
| class BattleField; | ||||
| class IBattleInfo; | ||||
|  | ||||
| namespace battle | ||||
| { | ||||
| 	class IUnitInfo; | ||||
| 	class Unit; | ||||
| 	using Units = std::vector<const Unit *>; | ||||
| 	using UnitFilter = std::function<bool(const Unit *)>; | ||||
| } | ||||
|  | ||||
| struct DamageRange | ||||
| { | ||||
| 	int64_t min = 0; | ||||
| 	int64_t max = 0; | ||||
| }; | ||||
|  | ||||
| struct DamageEstimation | ||||
| { | ||||
| 	DamageRange damage; | ||||
| 	DamageRange kills; | ||||
| }; | ||||
|  | ||||
| #if SCRIPTING_ENABLED | ||||
| namespace scripting | ||||
| { | ||||
| 	class Pool; | ||||
| } | ||||
| #endif | ||||
|  | ||||
| class DLL_LINKAGE IBattleInfoCallback : public IConstBonusProvider | ||||
| { | ||||
| public: | ||||
| #if SCRIPTING_ENABLED | ||||
| 	virtual scripting::Pool * getContextPool() const = 0; | ||||
| #endif | ||||
| 	virtual ~IBattleInfoCallback() = default; | ||||
|  | ||||
| 	virtual const IBattleInfo * getBattle() const = 0; | ||||
| 	virtual std::optional<PlayerColor> getPlayerID() const = 0; | ||||
|  | ||||
| 	virtual TerrainId battleTerrainType() const = 0; | ||||
| 	virtual BattleField battleGetBattlefieldType() const = 0; | ||||
|  | ||||
| 	///return none if battle is ongoing; otherwise the victorious side (0/1) or 2 if it is a draw | ||||
| 	virtual std::optional<BattleSide> battleIsFinished() const = 0; | ||||
|  | ||||
| 	virtual si8 battleTacticDist() const = 0; //returns tactic distance in current tactics phase; 0 if not in tactics phase | ||||
| 	virtual BattleSide battleGetTacticsSide() const = 0; //returns which side is in tactics phase, undefined if none (?) | ||||
|  | ||||
| 	virtual uint32_t battleNextUnitId() const = 0; | ||||
|  | ||||
| 	virtual battle::Units battleGetUnitsIf(const battle::UnitFilter & predicate) const = 0; | ||||
|  | ||||
| 	virtual const battle::Unit * battleGetUnitByID(uint32_t ID) const = 0; | ||||
| 	virtual const battle::Unit * battleGetUnitByPos(BattleHex pos, bool onlyAlive = true) const = 0; | ||||
|  | ||||
| 	virtual const battle::Unit * battleActiveUnit() const = 0; | ||||
|  | ||||
| 	//blocking obstacles makes tile inaccessible, others cause special effects (like Land Mines, Moat, Quicksands) | ||||
| 	virtual std::vector<std::shared_ptr<const CObstacleInstance>> battleGetAllObstaclesOnPos(BattleHex tile, bool onlyBlocking = true) const = 0; | ||||
| 	virtual std::vector<std::shared_ptr<const CObstacleInstance>> getAllAffectedObstaclesByStack(const battle::Unit * unit, const BattleHexArray & passed) const = 0; | ||||
| }; | ||||
|  | ||||
|  | ||||
|  | ||||
| VCMI_LIB_NAMESPACE_END | ||||
| /* | ||||
|  * IBattleInfoCallback.h, part of VCMI engine | ||||
|  * | ||||
|  * Authors: listed in file AUTHORS in main folder | ||||
|  * | ||||
|  * License: GNU General Public License v2.0 or later | ||||
|  * Full text of license available in license.txt file, in main folder | ||||
|  * | ||||
|  */ | ||||
|  | ||||
| #pragma once | ||||
|  | ||||
| #include "GameConstants.h" | ||||
| #include "BattleHexArray.h" | ||||
|  | ||||
| #include <vcmi/Entity.h> | ||||
|  | ||||
| #define RETURN_IF_NOT_BATTLE(...) do { if(!duringBattle()) {logGlobal->error("%s called when no battle!", __FUNCTION__); return __VA_ARGS__; } } while (false) | ||||
|  | ||||
| VCMI_LIB_NAMESPACE_BEGIN | ||||
|  | ||||
| struct CObstacleInstance; | ||||
| class BattleField; | ||||
| class IBattleInfo; | ||||
|  | ||||
| namespace battle | ||||
| { | ||||
| 	class IUnitInfo; | ||||
| 	class Unit; | ||||
| 	using Units = std::vector<const Unit *>; | ||||
| 	using UnitFilter = std::function<bool(const Unit *)>; | ||||
| } | ||||
|  | ||||
| struct DamageRange | ||||
| { | ||||
| 	int64_t min = 0; | ||||
| 	int64_t max = 0; | ||||
| }; | ||||
|  | ||||
| struct DamageEstimation | ||||
| { | ||||
| 	DamageRange damage; | ||||
| 	DamageRange kills; | ||||
| }; | ||||
|  | ||||
| #if SCRIPTING_ENABLED | ||||
| namespace scripting | ||||
| { | ||||
| 	class Pool; | ||||
| } | ||||
| #endif | ||||
|  | ||||
| class DLL_LINKAGE IBattleInfoCallback : public IConstBonusProvider | ||||
| { | ||||
| public: | ||||
| #if SCRIPTING_ENABLED | ||||
| 	virtual scripting::Pool * getContextPool() const = 0; | ||||
| #endif | ||||
| 	virtual ~IBattleInfoCallback() = default; | ||||
|  | ||||
| 	virtual const IBattleInfo * getBattle() const = 0; | ||||
| 	virtual std::optional<PlayerColor> getPlayerID() const = 0; | ||||
|  | ||||
| 	virtual TerrainId battleTerrainType() const = 0; | ||||
| 	virtual BattleField battleGetBattlefieldType() const = 0; | ||||
|  | ||||
| 	///return none if battle is ongoing; otherwise the victorious side (0/1) or 2 if it is a draw | ||||
| 	virtual std::optional<BattleSide> battleIsFinished() const = 0; | ||||
|  | ||||
| 	virtual si8 battleTacticDist() const = 0; //returns tactic distance in current tactics phase; 0 if not in tactics phase | ||||
| 	virtual BattleSide battleGetTacticsSide() const = 0; //returns which side is in tactics phase, undefined if none (?) | ||||
|  | ||||
| 	virtual uint32_t battleNextUnitId() const = 0; | ||||
|  | ||||
| 	virtual battle::Units battleGetUnitsIf(const battle::UnitFilter & predicate) const = 0; | ||||
|  | ||||
| 	virtual const battle::Unit * battleGetUnitByID(uint32_t ID) const = 0; | ||||
| 	virtual const battle::Unit * battleGetUnitByPos(BattleHex pos, bool onlyAlive = true) const = 0; | ||||
|  | ||||
| 	virtual const battle::Unit * battleActiveUnit() const = 0; | ||||
|  | ||||
| 	//blocking obstacles makes tile inaccessible, others cause special effects (like Land Mines, Moat, Quicksands) | ||||
| 	virtual std::vector<std::shared_ptr<const CObstacleInstance>> battleGetAllObstaclesOnPos(BattleHex tile, bool onlyBlocking = true) const = 0; | ||||
| 	virtual std::vector<std::shared_ptr<const CObstacleInstance>> getAllAffectedObstaclesByStack(const battle::Unit * unit, const BattleHexArray & passed) const = 0; | ||||
| }; | ||||
|  | ||||
|  | ||||
|  | ||||
| VCMI_LIB_NAMESPACE_END | ||||
|   | ||||
| @@ -1,89 +1,89 @@ | ||||
| /* | ||||
|  * ReachabilityInfo.cpp, part of VCMI engine | ||||
|  * | ||||
|  * Authors: listed in file AUTHORS in main folder | ||||
|  * | ||||
|  * License: GNU General Public License v2.0 or later | ||||
|  * Full text of license available in license.txt file, in main folder | ||||
|  * | ||||
|  */ | ||||
|  | ||||
| #include "StdInc.h" | ||||
| #include "ReachabilityInfo.h" | ||||
| #include "Unit.h" | ||||
|  | ||||
| VCMI_LIB_NAMESPACE_BEGIN | ||||
|  | ||||
| ReachabilityInfo::Parameters::Parameters(const battle::Unit * Stack, BattleHex StartPosition): | ||||
| 	perspective(static_cast<BattleSide>(Stack->unitSide())), | ||||
| 	startPosition(StartPosition), | ||||
| 	doubleWide(Stack->doubleWide()), | ||||
| 	side(Stack->unitSide()), | ||||
| 	flying(Stack->hasBonusOfType(BonusType::FLYING)) | ||||
| { | ||||
| 	knownAccessible = battle::Unit::getHexes(startPosition, doubleWide, side); | ||||
| } | ||||
|  | ||||
| ReachabilityInfo::ReachabilityInfo() | ||||
| { | ||||
| 	distances.fill(INFINITE_DIST); | ||||
| 	predecessors.fill(BattleHex::INVALID); | ||||
| } | ||||
|  | ||||
| bool ReachabilityInfo::isReachable(BattleHex hex) const | ||||
| { | ||||
| 	return distances[hex] < INFINITE_DIST; | ||||
| } | ||||
|  | ||||
| uint32_t ReachabilityInfo::distToNearestNeighbour( | ||||
| 	const BattleHexArray & targetHexes, | ||||
| 	BattleHex * chosenHex) const | ||||
| { | ||||
| 	uint32_t ret = 1000000; | ||||
|  | ||||
| 	for(auto targetHex : targetHexes) | ||||
| 	{ | ||||
| 		for(auto & n : BattleHexArray::neighbouringTilesCache[targetHex]) | ||||
| 		{ | ||||
| 			if(distances[n] < ret) | ||||
| 			{ | ||||
| 				ret = distances[n]; | ||||
| 				if(chosenHex) | ||||
| 					*chosenHex = n; | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	return ret; | ||||
| } | ||||
|  | ||||
| uint32_t ReachabilityInfo::distToNearestNeighbour( | ||||
| 	const battle::Unit * attacker, | ||||
| 	const battle::Unit * defender, | ||||
| 	BattleHex * chosenHex) const | ||||
| { | ||||
| 	auto attackableHexes = defender->getHexes(); | ||||
|  | ||||
| 	if(attacker->doubleWide()) | ||||
| 	{ | ||||
| 		if(defender->doubleWide()) | ||||
| 		{ | ||||
| 			// It can be back to back attack  o==o  or head to head  =oo=. | ||||
| 			// In case of back-to-back the distance between heads (unit positions) may be up to 3 tiles | ||||
| 			attackableHexes.merge(battle::Unit::getHexes(defender->occupiedHex(), true, defender->unitSide())); | ||||
| 		} | ||||
| 		else | ||||
| 		{ | ||||
| 			attackableHexes.merge(battle::Unit::getHexes(defender->getPosition(), true, defender->unitSide())); | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	vstd::erase_if(attackableHexes, [defender](BattleHex h) -> bool | ||||
| 		{ | ||||
| 			return h.getY() != defender->getPosition().getY() || !h.isAvailable(); | ||||
| 		}); | ||||
|  | ||||
| 	return distToNearestNeighbour(attackableHexes, chosenHex); | ||||
| } | ||||
|  | ||||
| VCMI_LIB_NAMESPACE_END | ||||
| /* | ||||
|  * ReachabilityInfo.cpp, part of VCMI engine | ||||
|  * | ||||
|  * Authors: listed in file AUTHORS in main folder | ||||
|  * | ||||
|  * License: GNU General Public License v2.0 or later | ||||
|  * Full text of license available in license.txt file, in main folder | ||||
|  * | ||||
|  */ | ||||
|  | ||||
| #include "StdInc.h" | ||||
| #include "ReachabilityInfo.h" | ||||
| #include "Unit.h" | ||||
|  | ||||
| VCMI_LIB_NAMESPACE_BEGIN | ||||
|  | ||||
| ReachabilityInfo::Parameters::Parameters(const battle::Unit * Stack, BattleHex StartPosition): | ||||
| 	perspective(static_cast<BattleSide>(Stack->unitSide())), | ||||
| 	startPosition(StartPosition), | ||||
| 	doubleWide(Stack->doubleWide()), | ||||
| 	side(Stack->unitSide()), | ||||
| 	flying(Stack->hasBonusOfType(BonusType::FLYING)) | ||||
| { | ||||
| 	knownAccessible = battle::Unit::getHexes(startPosition, doubleWide, side); | ||||
| } | ||||
|  | ||||
| ReachabilityInfo::ReachabilityInfo() | ||||
| { | ||||
| 	distances.fill(INFINITE_DIST); | ||||
| 	predecessors.fill(BattleHex::INVALID); | ||||
| } | ||||
|  | ||||
| bool ReachabilityInfo::isReachable(BattleHex hex) const | ||||
| { | ||||
| 	return distances[hex] < INFINITE_DIST; | ||||
| } | ||||
|  | ||||
| uint32_t ReachabilityInfo::distToNearestNeighbour( | ||||
| 	const BattleHexArray & targetHexes, | ||||
| 	BattleHex * chosenHex) const | ||||
| { | ||||
| 	uint32_t ret = 1000000; | ||||
|  | ||||
| 	for(auto targetHex : targetHexes) | ||||
| 	{ | ||||
| 		for(auto & n : BattleHexArray::neighbouringTilesCache[targetHex]) | ||||
| 		{ | ||||
| 			if(distances[n] < ret) | ||||
| 			{ | ||||
| 				ret = distances[n]; | ||||
| 				if(chosenHex) | ||||
| 					*chosenHex = n; | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	return ret; | ||||
| } | ||||
|  | ||||
| uint32_t ReachabilityInfo::distToNearestNeighbour( | ||||
| 	const battle::Unit * attacker, | ||||
| 	const battle::Unit * defender, | ||||
| 	BattleHex * chosenHex) const | ||||
| { | ||||
| 	auto attackableHexes = defender->getHexes(); | ||||
|  | ||||
| 	if(attacker->doubleWide()) | ||||
| 	{ | ||||
| 		if(defender->doubleWide()) | ||||
| 		{ | ||||
| 			// It can be back to back attack  o==o  or head to head  =oo=. | ||||
| 			// In case of back-to-back the distance between heads (unit positions) may be up to 3 tiles | ||||
| 			attackableHexes.merge(battle::Unit::getHexes(defender->occupiedHex(), true, defender->unitSide())); | ||||
| 		} | ||||
| 		else | ||||
| 		{ | ||||
| 			attackableHexes.merge(battle::Unit::getHexes(defender->getPosition(), true, defender->unitSide())); | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	vstd::erase_if(attackableHexes, [defender](BattleHex h) -> bool | ||||
| 		{ | ||||
| 			return h.getY() != defender->getPosition().getY() || !h.isAvailable(); | ||||
| 		}); | ||||
|  | ||||
| 	return distToNearestNeighbour(attackableHexes, chosenHex); | ||||
| } | ||||
|  | ||||
| VCMI_LIB_NAMESPACE_END | ||||
|   | ||||
| @@ -220,8 +220,8 @@ ILimiter::EDecision UnitOnHexLimiter::limit(const BonusLimitationContext &contex | ||||
| 		return ILimiter::EDecision::DISCARD; | ||||
|  | ||||
| 	auto accept = false; | ||||
| 	for (const auto & hex : stack->getHexes()) | ||||
| 		accept |= !!applicableHexes.count(hex); | ||||
| 	for (auto hex : stack->getHexes()) | ||||
| 		accept |= applicableHexes.contains(hex); | ||||
|  | ||||
| 	return accept ? ILimiter::EDecision::ACCEPT : ILimiter::EDecision::DISCARD; | ||||
| } | ||||
| @@ -236,7 +236,7 @@ JsonNode UnitOnHexLimiter::toJsonNode() const | ||||
| 	JsonNode root; | ||||
|  | ||||
| 	root["type"].String() = "UNIT_ON_HEXES"; | ||||
| 	for(const auto & hex : applicableHexes) | ||||
| 	for(auto hex : applicableHexes) | ||||
| 		root["parameters"].Vector().emplace_back(hex); | ||||
|  | ||||
| 	return root; | ||||
|   | ||||
| @@ -1,243 +1,243 @@ | ||||
| /* | ||||
|  * CGPathNode.h, part of VCMI engine | ||||
|  * | ||||
|  * Authors: listed in file AUTHORS in main folder | ||||
|  * | ||||
|  * License: GNU General Public License v2.0 or later | ||||
|  * Full text of license available in license.txt file, in main folder | ||||
|  * | ||||
|  */ | ||||
| #pragma once | ||||
|  | ||||
| #include "../GameConstants.h" | ||||
| #include "../int3.h" | ||||
|  | ||||
| #include <boost/heap/fibonacci_heap.hpp> | ||||
|  | ||||
| VCMI_LIB_NAMESPACE_BEGIN | ||||
|  | ||||
| class CGHeroInstance; | ||||
| class CGObjectInstance; | ||||
| class CGameState; | ||||
| class CPathfinderHelper; | ||||
| struct TerrainTile; | ||||
|  | ||||
| template<typename N> | ||||
| struct DLL_LINKAGE NodeComparer | ||||
| { | ||||
| 	STRONG_INLINE | ||||
| 	bool operator()(const N * lhs, const N * rhs) const | ||||
| 	{ | ||||
| 		return lhs->getCost() > rhs->getCost(); | ||||
| 	} | ||||
| }; | ||||
|  | ||||
| enum class EPathAccessibility : ui8 | ||||
| { | ||||
| 	NOT_SET, | ||||
| 	ACCESSIBLE, //tile can be entered and passed | ||||
| 	VISITABLE, //tile can be entered as the last tile in path | ||||
| 	GUARDED,  //tile can be entered, but is in zone of control of nearby monster (may also contain visitable object, if any) | ||||
| 	BLOCKVIS,  //visitable from neighbouring tile but not passable | ||||
| 	FLYABLE, //can only be accessed in air layer | ||||
| 	BLOCKED //tile can be neither entered nor visited | ||||
| }; | ||||
|  | ||||
| enum class EPathNodeAction : ui8 | ||||
| { | ||||
| 	UNKNOWN, | ||||
| 	EMBARK, | ||||
| 	DISEMBARK, | ||||
| 	NORMAL, | ||||
| 	BATTLE, | ||||
| 	VISIT, | ||||
| 	BLOCKING_VISIT, | ||||
| 	TELEPORT_NORMAL, | ||||
| 	TELEPORT_BLOCKING_VISIT, | ||||
| 	TELEPORT_BATTLE | ||||
| }; | ||||
|  | ||||
| struct DLL_LINKAGE CGPathNode | ||||
| { | ||||
| 	using TFibHeap = boost::heap::fibonacci_heap<CGPathNode *, boost::heap::compare<NodeComparer<CGPathNode>>>; | ||||
| 	using ELayer = EPathfindingLayer; | ||||
|  | ||||
| 	TFibHeap::handle_type pqHandle; | ||||
| 	TFibHeap * pq; | ||||
| 	CGPathNode * theNodeBefore; | ||||
|  | ||||
| 	int3 coord; //coordinates | ||||
| 	ELayer layer; | ||||
|  | ||||
| 	float cost; //total cost of the path to this tile measured in turns with fractions | ||||
| 	int moveRemains; //remaining movement points after hero reaches the tile | ||||
| 	ui8 turns; //how many turns we have to wait before reaching the tile - 0 means current turn | ||||
| 	EPathAccessibility accessible; | ||||
| 	EPathNodeAction action; | ||||
| 	bool locked; | ||||
|  | ||||
| 	CGPathNode() | ||||
| 		: coord(-1), | ||||
| 		layer(ELayer::WRONG), | ||||
| 		pqHandle(nullptr) | ||||
| 	{ | ||||
| 		reset(); | ||||
| 	} | ||||
|  | ||||
| 	STRONG_INLINE | ||||
| 	void reset() | ||||
| 	{ | ||||
| 		locked = false; | ||||
| 		accessible = EPathAccessibility::NOT_SET; | ||||
| 		moveRemains = 0; | ||||
| 		cost = std::numeric_limits<float>::max(); | ||||
| 		turns = 255; | ||||
| 		theNodeBefore = nullptr; | ||||
| 		pq = nullptr; | ||||
| 		action = EPathNodeAction::UNKNOWN; | ||||
| 	} | ||||
|  | ||||
| 	STRONG_INLINE | ||||
| 	bool inPQ() const | ||||
| 	{ | ||||
| 		return pq != nullptr; | ||||
| 	} | ||||
|  | ||||
| 	STRONG_INLINE | ||||
| 	float getCost() const | ||||
| 	{ | ||||
| 		return cost; | ||||
| 	} | ||||
|  | ||||
| 	STRONG_INLINE | ||||
| 	void setCost(float value) | ||||
| 	{ | ||||
| 		if(vstd::isAlmostEqual(value, cost)) | ||||
| 			return; | ||||
|  | ||||
| 		bool getUpNode = value < cost; | ||||
| 		cost = value; | ||||
| 		// If the node is in the heap, update the heap. | ||||
| 		if(inPQ()) | ||||
| 		{ | ||||
| 			if(getUpNode) | ||||
| 			{ | ||||
| 				pq->increase(this->pqHandle); | ||||
| 			} | ||||
| 			else | ||||
| 			{ | ||||
| 				pq->decrease(this->pqHandle); | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	STRONG_INLINE | ||||
| 	void update(const int3 & Coord, const ELayer Layer, const EPathAccessibility Accessible) | ||||
| 	{ | ||||
| 		if(layer == ELayer::WRONG) | ||||
| 		{ | ||||
| 			coord = Coord; | ||||
| 			layer = Layer; | ||||
| 		} | ||||
| 		else | ||||
| 		{ | ||||
| 			reset(); | ||||
| 		} | ||||
|  | ||||
| 		accessible = Accessible; | ||||
| 	} | ||||
|  | ||||
| 	STRONG_INLINE | ||||
| 	bool reachable() const | ||||
| 	{ | ||||
| 		return turns < 255; | ||||
| 	} | ||||
|  | ||||
| 	bool isTeleportAction() const | ||||
| 	{ | ||||
| 		if (action != EPathNodeAction::TELEPORT_NORMAL && | ||||
| 			action != EPathNodeAction::TELEPORT_BLOCKING_VISIT && | ||||
| 			action != EPathNodeAction::TELEPORT_BATTLE) | ||||
| 		{ | ||||
| 			return false; | ||||
| 		} | ||||
|  | ||||
| 		return true; | ||||
| 	} | ||||
| }; | ||||
|  | ||||
| struct DLL_LINKAGE CGPath | ||||
| { | ||||
| 	std::vector<CGPathNode> nodes; //just get node by node | ||||
|  | ||||
| 	/// Starting position of path, matches location of hero | ||||
| 	const CGPathNode & currNode() const; | ||||
| 	/// First node in path, this is where hero will move next | ||||
| 	const CGPathNode & nextNode() const; | ||||
| 	/// Last node in path, this is what hero wants to reach in the end | ||||
| 	const CGPathNode & lastNode() const; | ||||
|  | ||||
| 	int3 startPos() const; // start point | ||||
| 	int3 endPos() const; //destination point | ||||
| }; | ||||
|  | ||||
| struct DLL_LINKAGE CPathsInfo | ||||
| { | ||||
| 	using ELayer = EPathfindingLayer; | ||||
|  | ||||
| 	const CGHeroInstance * hero; | ||||
| 	int3 hpos; | ||||
| 	int3 sizes; | ||||
| 	boost::multi_array<CGPathNode, 4> nodes; //[layer][level][w][h] | ||||
|  | ||||
| 	CPathsInfo(const int3 & Sizes, const CGHeroInstance * hero_); | ||||
| 	~CPathsInfo(); | ||||
| 	const CGPathNode * getPathInfo(const int3 & tile) const; | ||||
| 	bool getPath(CGPath & out, const int3 & dst) const; | ||||
| 	const CGPathNode * getNode(const int3 & coord) const; | ||||
|  | ||||
| 	STRONG_INLINE | ||||
| 	CGPathNode * getNode(const int3 & coord, const ELayer layer) | ||||
| 	{ | ||||
| 		return &nodes[layer.getNum()][coord.z][coord.x][coord.y]; | ||||
| 	} | ||||
| }; | ||||
|  | ||||
| struct DLL_LINKAGE PathNodeInfo | ||||
| { | ||||
| 	CGPathNode * node; | ||||
| 	const CGObjectInstance * nodeObject; | ||||
| 	const CGHeroInstance * nodeHero; | ||||
| 	const TerrainTile * tile; | ||||
| 	int3 coord; | ||||
| 	bool guarded; | ||||
| 	PlayerRelations objectRelations; | ||||
| 	PlayerRelations heroRelations; | ||||
| 	bool isInitialPosition; | ||||
|  | ||||
| 	PathNodeInfo(); | ||||
|  | ||||
| 	virtual void setNode(CGameState * gs, CGPathNode * n); | ||||
|  | ||||
| 	void updateInfo(CPathfinderHelper * hlp, CGameState * gs); | ||||
|  | ||||
| 	bool isNodeObjectVisitable() const; | ||||
| }; | ||||
|  | ||||
| struct DLL_LINKAGE CDestinationNodeInfo : public PathNodeInfo | ||||
| { | ||||
| 	EPathNodeAction action; | ||||
| 	int turn; | ||||
| 	int movementLeft; | ||||
| 	float cost; //same as CGPathNode::cost | ||||
| 	bool blocked; | ||||
| 	bool isGuardianTile; | ||||
|  | ||||
| 	CDestinationNodeInfo(); | ||||
|  | ||||
| 	void setNode(CGameState * gs, CGPathNode * n) override; | ||||
|  | ||||
| 	virtual bool isBetterWay() const; | ||||
| }; | ||||
|  | ||||
| VCMI_LIB_NAMESPACE_END | ||||
| /* | ||||
|  * CGPathNode.h, part of VCMI engine | ||||
|  * | ||||
|  * Authors: listed in file AUTHORS in main folder | ||||
|  * | ||||
|  * License: GNU General Public License v2.0 or later | ||||
|  * Full text of license available in license.txt file, in main folder | ||||
|  * | ||||
|  */ | ||||
| #pragma once | ||||
|  | ||||
| #include "../GameConstants.h" | ||||
| #include "../int3.h" | ||||
|  | ||||
| #include <boost/heap/fibonacci_heap.hpp> | ||||
|  | ||||
| VCMI_LIB_NAMESPACE_BEGIN | ||||
|  | ||||
| class CGHeroInstance; | ||||
| class CGObjectInstance; | ||||
| class CGameState; | ||||
| class CPathfinderHelper; | ||||
| struct TerrainTile; | ||||
|  | ||||
| template<typename N> | ||||
| struct DLL_LINKAGE NodeComparer | ||||
| { | ||||
| 	STRONG_INLINE | ||||
| 	bool operator()(const N * lhs, const N * rhs) const | ||||
| 	{ | ||||
| 		return lhs->getCost() > rhs->getCost(); | ||||
| 	} | ||||
| }; | ||||
|  | ||||
| enum class EPathAccessibility : ui8 | ||||
| { | ||||
| 	NOT_SET, | ||||
| 	ACCESSIBLE, //tile can be entered and passed | ||||
| 	VISITABLE, //tile can be entered as the last tile in path | ||||
| 	GUARDED,  //tile can be entered, but is in zone of control of nearby monster (may also contain visitable object, if any) | ||||
| 	BLOCKVIS,  //visitable from neighboring tile but not passable | ||||
| 	FLYABLE, //can only be accessed in air layer | ||||
| 	BLOCKED //tile can be neither entered nor visited | ||||
| }; | ||||
|  | ||||
| enum class EPathNodeAction : ui8 | ||||
| { | ||||
| 	UNKNOWN, | ||||
| 	EMBARK, | ||||
| 	DISEMBARK, | ||||
| 	NORMAL, | ||||
| 	BATTLE, | ||||
| 	VISIT, | ||||
| 	BLOCKING_VISIT, | ||||
| 	TELEPORT_NORMAL, | ||||
| 	TELEPORT_BLOCKING_VISIT, | ||||
| 	TELEPORT_BATTLE | ||||
| }; | ||||
|  | ||||
| struct DLL_LINKAGE CGPathNode | ||||
| { | ||||
| 	using TFibHeap = boost::heap::fibonacci_heap<CGPathNode *, boost::heap::compare<NodeComparer<CGPathNode>>>; | ||||
| 	using ELayer = EPathfindingLayer; | ||||
|  | ||||
| 	TFibHeap::handle_type pqHandle; | ||||
| 	TFibHeap * pq; | ||||
| 	CGPathNode * theNodeBefore; | ||||
|  | ||||
| 	int3 coord; //coordinates | ||||
| 	ELayer layer; | ||||
|  | ||||
| 	float cost; //total cost of the path to this tile measured in turns with fractions | ||||
| 	int moveRemains; //remaining movement points after hero reaches the tile | ||||
| 	ui8 turns; //how many turns we have to wait before reaching the tile - 0 means current turn | ||||
| 	EPathAccessibility accessible; | ||||
| 	EPathNodeAction action; | ||||
| 	bool locked; | ||||
|  | ||||
| 	CGPathNode() | ||||
| 		: coord(-1), | ||||
| 		layer(ELayer::WRONG), | ||||
| 		pqHandle(nullptr) | ||||
| 	{ | ||||
| 		reset(); | ||||
| 	} | ||||
|  | ||||
| 	STRONG_INLINE | ||||
| 	void reset() | ||||
| 	{ | ||||
| 		locked = false; | ||||
| 		accessible = EPathAccessibility::NOT_SET; | ||||
| 		moveRemains = 0; | ||||
| 		cost = std::numeric_limits<float>::max(); | ||||
| 		turns = 255; | ||||
| 		theNodeBefore = nullptr; | ||||
| 		pq = nullptr; | ||||
| 		action = EPathNodeAction::UNKNOWN; | ||||
| 	} | ||||
|  | ||||
| 	STRONG_INLINE | ||||
| 	bool inPQ() const | ||||
| 	{ | ||||
| 		return pq != nullptr; | ||||
| 	} | ||||
|  | ||||
| 	STRONG_INLINE | ||||
| 	float getCost() const | ||||
| 	{ | ||||
| 		return cost; | ||||
| 	} | ||||
|  | ||||
| 	STRONG_INLINE | ||||
| 	void setCost(float value) | ||||
| 	{ | ||||
| 		if(vstd::isAlmostEqual(value, cost)) | ||||
| 			return; | ||||
|  | ||||
| 		bool getUpNode = value < cost; | ||||
| 		cost = value; | ||||
| 		// If the node is in the heap, update the heap. | ||||
| 		if(inPQ()) | ||||
| 		{ | ||||
| 			if(getUpNode) | ||||
| 			{ | ||||
| 				pq->increase(this->pqHandle); | ||||
| 			} | ||||
| 			else | ||||
| 			{ | ||||
| 				pq->decrease(this->pqHandle); | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	STRONG_INLINE | ||||
| 	void update(const int3 & Coord, const ELayer Layer, const EPathAccessibility Accessible) | ||||
| 	{ | ||||
| 		if(layer == ELayer::WRONG) | ||||
| 		{ | ||||
| 			coord = Coord; | ||||
| 			layer = Layer; | ||||
| 		} | ||||
| 		else | ||||
| 		{ | ||||
| 			reset(); | ||||
| 		} | ||||
|  | ||||
| 		accessible = Accessible; | ||||
| 	} | ||||
|  | ||||
| 	STRONG_INLINE | ||||
| 	bool reachable() const | ||||
| 	{ | ||||
| 		return turns < 255; | ||||
| 	} | ||||
|  | ||||
| 	bool isTeleportAction() const | ||||
| 	{ | ||||
| 		if (action != EPathNodeAction::TELEPORT_NORMAL && | ||||
| 			action != EPathNodeAction::TELEPORT_BLOCKING_VISIT && | ||||
| 			action != EPathNodeAction::TELEPORT_BATTLE) | ||||
| 		{ | ||||
| 			return false; | ||||
| 		} | ||||
|  | ||||
| 		return true; | ||||
| 	} | ||||
| }; | ||||
|  | ||||
| struct DLL_LINKAGE CGPath | ||||
| { | ||||
| 	std::vector<CGPathNode> nodes; //just get node by node | ||||
|  | ||||
| 	/// Starting position of path, matches location of hero | ||||
| 	const CGPathNode & currNode() const; | ||||
| 	/// First node in path, this is where hero will move next | ||||
| 	const CGPathNode & nextNode() const; | ||||
| 	/// Last node in path, this is what hero wants to reach in the end | ||||
| 	const CGPathNode & lastNode() const; | ||||
|  | ||||
| 	int3 startPos() const; // start point | ||||
| 	int3 endPos() const; //destination point | ||||
| }; | ||||
|  | ||||
| struct DLL_LINKAGE CPathsInfo | ||||
| { | ||||
| 	using ELayer = EPathfindingLayer; | ||||
|  | ||||
| 	const CGHeroInstance * hero; | ||||
| 	int3 hpos; | ||||
| 	int3 sizes; | ||||
| 	boost::multi_array<CGPathNode, 4> nodes; //[layer][level][w][h] | ||||
|  | ||||
| 	CPathsInfo(const int3 & Sizes, const CGHeroInstance * hero_); | ||||
| 	~CPathsInfo(); | ||||
| 	const CGPathNode * getPathInfo(const int3 & tile) const; | ||||
| 	bool getPath(CGPath & out, const int3 & dst) const; | ||||
| 	const CGPathNode * getNode(const int3 & coord) const; | ||||
|  | ||||
| 	STRONG_INLINE | ||||
| 	CGPathNode * getNode(const int3 & coord, const ELayer layer) | ||||
| 	{ | ||||
| 		return &nodes[layer.getNum()][coord.z][coord.x][coord.y]; | ||||
| 	} | ||||
| }; | ||||
|  | ||||
| struct DLL_LINKAGE PathNodeInfo | ||||
| { | ||||
| 	CGPathNode * node; | ||||
| 	const CGObjectInstance * nodeObject; | ||||
| 	const CGHeroInstance * nodeHero; | ||||
| 	const TerrainTile * tile; | ||||
| 	int3 coord; | ||||
| 	bool guarded; | ||||
| 	PlayerRelations objectRelations; | ||||
| 	PlayerRelations heroRelations; | ||||
| 	bool isInitialPosition; | ||||
|  | ||||
| 	PathNodeInfo(); | ||||
|  | ||||
| 	virtual void setNode(CGameState * gs, CGPathNode * n); | ||||
|  | ||||
| 	void updateInfo(CPathfinderHelper * hlp, CGameState * gs); | ||||
|  | ||||
| 	bool isNodeObjectVisitable() const; | ||||
| }; | ||||
|  | ||||
| struct DLL_LINKAGE CDestinationNodeInfo : public PathNodeInfo | ||||
| { | ||||
| 	EPathNodeAction action; | ||||
| 	int turn; | ||||
| 	int movementLeft; | ||||
| 	float cost; //same as CGPathNode::cost | ||||
| 	bool blocked; | ||||
| 	bool isGuardianTile; | ||||
|  | ||||
| 	CDestinationNodeInfo(); | ||||
|  | ||||
| 	void setNode(CGameState * gs, CGPathNode * n) override; | ||||
|  | ||||
| 	virtual bool isBetterWay() const; | ||||
| }; | ||||
|  | ||||
| VCMI_LIB_NAMESPACE_END | ||||
|   | ||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -117,6 +117,7 @@ Path Path::search(const Tileset & dst, bool straight, std::function<float(const | ||||
| 					return; | ||||
| 				 | ||||
| 				float movementCost = moveCostFunction(currentNode, pos); | ||||
|  | ||||
| 				float distance = distances[currentNode] + movementCost; //we prefer to use already free paths | ||||
| 				int bestDistanceSoFar = std::numeric_limits<int>::max(); | ||||
| 				auto it = distances.find(pos); | ||||
|   | ||||
| @@ -172,4 +172,4 @@ bool ObstaclePlacer::isProhibited(const rmg::Area & objArea) const | ||||
| 	return false; | ||||
| } | ||||
|  | ||||
| VCMI_LIB_NAMESPACE_END | ||||
| VCMI_LIB_NAMESPACE_END | ||||
|   | ||||
| @@ -13,7 +13,6 @@ | ||||
| #include "SerializerReflection.h" | ||||
| #include "ESerializationVersion.h" | ||||
| #include "../mapObjects/CGHeroInstance.h" | ||||
| #include "../battle/BattleHexArray.h" | ||||
|  | ||||
| VCMI_LIB_NAMESPACE_BEGIN | ||||
|  | ||||
| @@ -443,19 +442,6 @@ public: | ||||
| 			load(data[key]); | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	//void load(BattleHexArray & data) | ||||
| 	//{ | ||||
| 	//	uint32_t length = readAndCheckLength(); | ||||
| 	//	data.clear(); | ||||
| 	//	BattleHex hex; | ||||
| 	//	for(uint32_t i = 0; i < length; i++) | ||||
| 	//	{ | ||||
| 	//		load(hex); | ||||
| 	//		data.insert(hex); | ||||
| 	//	} | ||||
| 	//} | ||||
|  | ||||
| 	void load(std::string &data) | ||||
| 	{ | ||||
| 		if (hasFeature(Version::COMPACT_STRING_SERIALIZATION)) | ||||
|   | ||||
| @@ -15,7 +15,6 @@ | ||||
| #include "ESerializationVersion.h" | ||||
| #include "Serializeable.h" | ||||
| #include "../mapObjects/CArmedInstance.h" | ||||
| #include "../battle/BattleHexArray.h" | ||||
|  | ||||
| VCMI_LIB_NAMESPACE_BEGIN | ||||
|  | ||||
|   | ||||
| @@ -1,86 +1,86 @@ | ||||
| /* | ||||
|  * BattleSpellMechanics.h, part of VCMI engine | ||||
|  * | ||||
|  * Authors: listed in file AUTHORS in main folder | ||||
|  * | ||||
|  * License: GNU General Public License v2.0 or later | ||||
|  * Full text of license available in license.txt file, in main folder | ||||
|  * | ||||
|  */ | ||||
|  | ||||
| #pragma once | ||||
|  | ||||
| #include "ISpellMechanics.h" | ||||
|  | ||||
| #include "effects/Effects.h" | ||||
|  | ||||
| VCMI_LIB_NAMESPACE_BEGIN | ||||
|  | ||||
| struct BattleSpellCast; | ||||
|  | ||||
| namespace spells | ||||
| { | ||||
|  | ||||
| class BattleSpellMechanics : public BaseMechanics | ||||
| { | ||||
| public: | ||||
| 	BattleSpellMechanics(const IBattleCast * event, std::shared_ptr<effects::Effects> effects_, std::shared_ptr<IReceptiveCheck> targetCondition_); | ||||
| 	virtual ~BattleSpellMechanics(); | ||||
|  | ||||
| 	// TODO: ??? (what's the difference compared to cast?) | ||||
| 	void applyEffects(ServerCallback * server, const Target & targets, bool indirect, bool ignoreImmunity) const override; | ||||
|  | ||||
| 	/// Returns false if spell can not be cast at all, e.g. due to not having any possible target on battlefield | ||||
| 	bool canBeCast(Problem & problem) const override; | ||||
|  | ||||
| 	/// Returns false if spell can not be cast at specified target | ||||
| 	bool canBeCastAt(const Target & target, Problem & problem) const override; | ||||
|  | ||||
| 	// TODO: ??? (what's the difference compared to applyEffects?) | ||||
| 	void cast(ServerCallback * server, const Target & target) override final; | ||||
| 	// TODO: ??? (what's the difference compared to cast?) | ||||
| 	void castEval(ServerCallback * server, const Target & target) override final; | ||||
|  | ||||
| 	/// Returns list of affected stack using currently configured target | ||||
| 	std::vector<const CStack *> getAffectedStacks(const Target & target) const override final; | ||||
|  | ||||
| 	/// Returns list of target types that can be targeted by spell | ||||
| 	std::vector<AimType> getTargetTypes() const override final; | ||||
|  | ||||
| 	/// Returns vector of all possible destinations for specified aim type | ||||
| 	/// index - ??? | ||||
| 	/// current - ??? | ||||
| 	std::vector<Destination> getPossibleDestinations(size_t index, AimType aimType, const Target & current, bool fast) const override final; | ||||
|  | ||||
| 	/// Returns true if spell can be cast on unit | ||||
| 	bool isReceptive(const battle::Unit * target) const override; | ||||
|  | ||||
| 	/// Returns list of hexes that are affected by spell assuming cast at centralHex | ||||
| 	BattleHexArray rangeInHexes(BattleHex centralHex) const override; | ||||
|  | ||||
| 	const Spell * getSpell() const override; | ||||
|  | ||||
| 	bool counteringSelector(const Bonus * bonus) const; | ||||
|  | ||||
| private: | ||||
| 	std::shared_ptr<effects::Effects> effects; | ||||
| 	std::shared_ptr<IReceptiveCheck> targetCondition; | ||||
|  | ||||
| 	std::vector<const battle::Unit *> affectedUnits; | ||||
| 	effects::Effects::EffectsToApply effectsToApply; | ||||
|  | ||||
| 	void beforeCast(BattleSpellCast & sc, vstd::RNG & rng, const Target & target); | ||||
|  | ||||
| 	std::set<const battle::Unit *> collectTargets() const; | ||||
|  | ||||
| 	void doRemoveEffects(ServerCallback * server, const std::vector<const battle::Unit *> & targets, const CSelector & selector); | ||||
|  | ||||
| 	BattleHexArray spellRangeInHexes(BattleHex centralHex) const; | ||||
|  | ||||
| 	Target transformSpellTarget(const Target & aimPoint) const; | ||||
| }; | ||||
|  | ||||
| } | ||||
|  | ||||
|  | ||||
| VCMI_LIB_NAMESPACE_END | ||||
| /* | ||||
|  * BattleSpellMechanics.h, part of VCMI engine | ||||
|  * | ||||
|  * Authors: listed in file AUTHORS in main folder | ||||
|  * | ||||
|  * License: GNU General Public License v2.0 or later | ||||
|  * Full text of license available in license.txt file, in main folder | ||||
|  * | ||||
|  */ | ||||
|  | ||||
| #pragma once | ||||
|  | ||||
| #include "ISpellMechanics.h" | ||||
|  | ||||
| #include "effects/Effects.h" | ||||
|  | ||||
| VCMI_LIB_NAMESPACE_BEGIN | ||||
|  | ||||
| struct BattleSpellCast; | ||||
|  | ||||
| namespace spells | ||||
| { | ||||
|  | ||||
| class BattleSpellMechanics : public BaseMechanics | ||||
| { | ||||
| public: | ||||
| 	BattleSpellMechanics(const IBattleCast * event, std::shared_ptr<effects::Effects> effects_, std::shared_ptr<IReceptiveCheck> targetCondition_); | ||||
| 	virtual ~BattleSpellMechanics(); | ||||
|  | ||||
| 	// TODO: ??? (what's the difference compared to cast?) | ||||
| 	void applyEffects(ServerCallback * server, const Target & targets, bool indirect, bool ignoreImmunity) const override; | ||||
|  | ||||
| 	/// Returns false if spell can not be cast at all, e.g. due to not having any possible target on battlefield | ||||
| 	bool canBeCast(Problem & problem) const override; | ||||
|  | ||||
| 	/// Returns false if spell can not be cast at specified target | ||||
| 	bool canBeCastAt(const Target & target, Problem & problem) const override; | ||||
|  | ||||
| 	// TODO: ??? (what's the difference compared to applyEffects?) | ||||
| 	void cast(ServerCallback * server, const Target & target) override final; | ||||
| 	// TODO: ??? (what's the difference compared to cast?) | ||||
| 	void castEval(ServerCallback * server, const Target & target) override final; | ||||
|  | ||||
| 	/// Returns list of affected stack using currently configured target | ||||
| 	std::vector<const CStack *> getAffectedStacks(const Target & target) const override final; | ||||
|  | ||||
| 	/// Returns list of target types that can be targeted by spell | ||||
| 	std::vector<AimType> getTargetTypes() const override final; | ||||
|  | ||||
| 	/// Returns vector of all possible destinations for specified aim type | ||||
| 	/// index - ??? | ||||
| 	/// current - ??? | ||||
| 	std::vector<Destination> getPossibleDestinations(size_t index, AimType aimType, const Target & current, bool fast) const override final; | ||||
|  | ||||
| 	/// Returns true if spell can be cast on unit | ||||
| 	bool isReceptive(const battle::Unit * target) const override; | ||||
|  | ||||
| 	/// Returns list of hexes that are affected by spell assuming cast at centralHex | ||||
| 	BattleHexArray rangeInHexes(BattleHex centralHex) const override; | ||||
|  | ||||
| 	const Spell * getSpell() const override; | ||||
|  | ||||
| 	bool counteringSelector(const Bonus * bonus) const; | ||||
|  | ||||
| private: | ||||
| 	std::shared_ptr<effects::Effects> effects; | ||||
| 	std::shared_ptr<IReceptiveCheck> targetCondition; | ||||
|  | ||||
| 	std::vector<const battle::Unit *> affectedUnits; | ||||
| 	effects::Effects::EffectsToApply effectsToApply; | ||||
|  | ||||
| 	void beforeCast(BattleSpellCast & sc, vstd::RNG & rng, const Target & target); | ||||
|  | ||||
| 	std::set<const battle::Unit *> collectTargets() const; | ||||
|  | ||||
| 	void doRemoveEffects(ServerCallback * server, const std::vector<const battle::Unit *> & targets, const CSelector & selector); | ||||
|  | ||||
| 	BattleHexArray spellRangeInHexes(BattleHex centralHex) const; | ||||
|  | ||||
| 	Target transformSpellTarget(const Target & aimPoint) const; | ||||
| }; | ||||
|  | ||||
| } | ||||
|  | ||||
|  | ||||
| VCMI_LIB_NAMESPACE_END | ||||
|   | ||||
| @@ -1,368 +1,368 @@ | ||||
| /* | ||||
|  * ISpellMechanics.h, part of VCMI engine | ||||
|  * | ||||
|  * Authors: listed in file AUTHORS in main folder | ||||
|  * | ||||
|  * License: GNU General Public License v2.0 or later | ||||
|  * Full text of license available in license.txt file, in main folder | ||||
|  * | ||||
|  */ | ||||
|  | ||||
| #pragma once | ||||
|  | ||||
| #include <vcmi/spells/Magic.h> | ||||
| #include <vcmi/ServerCallback.h> | ||||
|  | ||||
| #include "../battle/Destination.h" | ||||
| #include "../int3.h" | ||||
| #include "../GameConstants.h" | ||||
| #include "../bonuses/Bonus.h" | ||||
|  | ||||
| VCMI_LIB_NAMESPACE_BEGIN | ||||
|  | ||||
| struct Query; | ||||
| class IBattleState; | ||||
| class CreatureService; | ||||
| class CMap; | ||||
| class CGameInfoCallback; | ||||
| class CBattleInfoCallback; | ||||
| class JsonNode; | ||||
| class CStack; | ||||
| class CGObjectInstance; | ||||
| class CGHeroInstance; | ||||
|  | ||||
| namespace spells | ||||
| { | ||||
| class Service; | ||||
| } | ||||
|  | ||||
| namespace vstd | ||||
| { | ||||
| 	class RNG; | ||||
| } | ||||
|  | ||||
| #if SCRIPTING_ENABLED | ||||
| namespace scripting | ||||
| { | ||||
| 	class Service; | ||||
| } | ||||
| #endif | ||||
|  | ||||
|  | ||||
| ///callback to be provided by server | ||||
| class DLL_LINKAGE SpellCastEnvironment : public ServerCallback | ||||
| { | ||||
| public: | ||||
| 	virtual ~SpellCastEnvironment() = default; | ||||
|  | ||||
| 	virtual const CMap * getMap() const = 0; | ||||
| 	virtual const CGameInfoCallback * getCb() const = 0; | ||||
|  | ||||
| 	virtual void createBoat(const int3 & visitablePosition, BoatId type, PlayerColor initiator) = 0; | ||||
| 	virtual bool moveHero(ObjectInstanceID hid, int3 dst, EMovementMode mode) = 0;	//TODO: remove | ||||
|  | ||||
| 	virtual void genericQuery(Query * request, PlayerColor color, std::function<void(std::optional<int32_t>)> callback) = 0;//TODO: type safety on query, use generic query packet when implemented | ||||
| }; | ||||
|  | ||||
| namespace spells | ||||
| { | ||||
|  | ||||
| class DLL_LINKAGE IBattleCast | ||||
| { | ||||
| public: | ||||
| 	using Value = int32_t; | ||||
| 	using Value64 = int64_t; | ||||
|  | ||||
| 	using OptionalValue = std::optional<Value>; | ||||
| 	using OptionalValue64 = std::optional<Value64>; | ||||
|  | ||||
| 	virtual const CSpell * getSpell() const = 0; | ||||
| 	virtual Mode getMode() const = 0; | ||||
| 	virtual const Caster * getCaster() const = 0; | ||||
| 	virtual const CBattleInfoCallback * getBattle() const = 0; | ||||
|  | ||||
| 	virtual OptionalValue getSpellLevel() const = 0; | ||||
|  | ||||
| 	virtual OptionalValue getEffectPower() const = 0; | ||||
| 	virtual OptionalValue getEffectDuration() const = 0; | ||||
|  | ||||
| 	virtual OptionalValue64 getEffectValue() const = 0; | ||||
|  | ||||
| 	virtual boost::logic::tribool isSmart() const = 0; | ||||
| 	virtual boost::logic::tribool isMassive() const = 0; | ||||
| }; | ||||
|  | ||||
| ///all parameters of particular cast event | ||||
| class DLL_LINKAGE BattleCast : public IBattleCast | ||||
| { | ||||
| public: | ||||
| 	boost::logic::tribool smart; | ||||
| 	boost::logic::tribool massive; | ||||
|  | ||||
| 	//normal constructor | ||||
| 	BattleCast(const CBattleInfoCallback * cb_, const Caster * caster_, const Mode mode_, const CSpell * spell_); | ||||
|  | ||||
| 	//magic mirror constructor | ||||
| 	BattleCast(const BattleCast & orig, const Caster * caster_); | ||||
|  | ||||
| 	virtual ~BattleCast(); | ||||
|  | ||||
| 	///IBattleCast | ||||
| 	const CSpell * getSpell() const override; | ||||
| 	Mode getMode() const override; | ||||
| 	const Caster * getCaster() const override; | ||||
| 	const CBattleInfoCallback * getBattle() const override; | ||||
|  | ||||
| 	OptionalValue getSpellLevel() const override; | ||||
|  | ||||
| 	OptionalValue getEffectPower() const override; | ||||
| 	OptionalValue getEffectDuration() const override; | ||||
|  | ||||
| 	OptionalValue64 getEffectValue() const override; | ||||
|  | ||||
| 	boost::logic::tribool isSmart() const override; | ||||
| 	boost::logic::tribool isMassive() const override; | ||||
|  | ||||
| 	void setSpellLevel(Value value); | ||||
|  | ||||
| 	void setEffectPower(Value value); | ||||
| 	void setEffectDuration(Value value); | ||||
|  | ||||
| 	void setEffectValue(Value64 value); | ||||
|  | ||||
| 	///only apply effects to specified targets | ||||
| 	void applyEffects(ServerCallback * server, const Target & target, bool indirect = false, bool ignoreImmunity = false) const; | ||||
|  | ||||
| 	///normal cast | ||||
| 	void cast(ServerCallback * server, Target target); | ||||
|  | ||||
| 	///cast evaluation | ||||
| 	void castEval(ServerCallback * server, Target target); | ||||
|  | ||||
| 	///cast with silent check for permitted cast | ||||
| 	bool castIfPossible(ServerCallback * server, Target target); | ||||
|  | ||||
| 	std::vector<Target> findPotentialTargets(bool fast = false) const; | ||||
|  | ||||
| private: | ||||
| 	///spell school level | ||||
| 	OptionalValue magicSkillLevel; | ||||
|  | ||||
| 	///actual spell-power affecting effect values | ||||
| 	OptionalValue effectPower; | ||||
| 	///actual spell-power affecting effect duration | ||||
| 	OptionalValue effectDuration; | ||||
|  | ||||
| 	///for Archangel-like casting | ||||
| 	OptionalValue64 effectValue; | ||||
|  | ||||
| 	Mode mode; | ||||
| 	const CSpell * spell; | ||||
| 	const CBattleInfoCallback * cb; | ||||
| 	const Caster * caster; | ||||
| }; | ||||
|  | ||||
| class DLL_LINKAGE ISpellMechanicsFactory | ||||
| { | ||||
| public: | ||||
| 	virtual ~ISpellMechanicsFactory(); | ||||
|  | ||||
| 	virtual std::unique_ptr<Mechanics> create(const IBattleCast * event) const = 0; | ||||
|  | ||||
| 	static std::unique_ptr<ISpellMechanicsFactory> get(const CSpell * s); | ||||
|  | ||||
| protected: | ||||
| 	const CSpell * spell; | ||||
|  | ||||
| 	ISpellMechanicsFactory(const CSpell * s); | ||||
| }; | ||||
|  | ||||
| class DLL_LINKAGE Mechanics | ||||
| { | ||||
| public: | ||||
| 	virtual ~Mechanics(); | ||||
|  | ||||
| 	virtual bool adaptProblem(ESpellCastProblem source, Problem & target) const = 0; | ||||
| 	virtual bool adaptGenericProblem(Problem & target) const = 0; | ||||
|  | ||||
| 	virtual BattleHexArray rangeInHexes(BattleHex centralHex) const = 0; | ||||
| 	virtual std::vector<const CStack *> getAffectedStacks(const Target & target) const = 0; | ||||
|  | ||||
| 	virtual bool canBeCast(Problem & problem) const = 0; | ||||
| 	virtual bool canBeCastAt(const Target & target, Problem & problem) const = 0; | ||||
|  | ||||
| 	virtual void applyEffects(ServerCallback * server, const Target & targets, bool indirect, bool ignoreImmunity) const = 0; | ||||
|  | ||||
| 	virtual void cast(ServerCallback * server, const Target & target) = 0; | ||||
|  | ||||
| 	virtual void castEval(ServerCallback * server, const Target & target) = 0; | ||||
|  | ||||
| 	virtual bool isReceptive(const battle::Unit * target) const = 0; | ||||
|  | ||||
| 	virtual std::vector<AimType> getTargetTypes() const = 0; | ||||
|  | ||||
| 	virtual std::vector<Destination> getPossibleDestinations(size_t index, AimType aimType, const Target & current, bool fast = false) const = 0; | ||||
|  | ||||
| 	virtual const Spell * getSpell() const = 0; | ||||
|  | ||||
| 	//Cast event facade | ||||
|  | ||||
| 	virtual IBattleCast::Value getEffectLevel() const = 0; | ||||
| 	virtual IBattleCast::Value getRangeLevel() const = 0; | ||||
|  | ||||
| 	virtual IBattleCast::Value getEffectPower() const = 0; | ||||
| 	virtual IBattleCast::Value getEffectDuration() const = 0; | ||||
|  | ||||
| 	virtual IBattleCast::Value64 getEffectValue() const = 0; | ||||
|  | ||||
| 	virtual PlayerColor getCasterColor() const = 0; | ||||
|  | ||||
| 	//Spell facade | ||||
| 	virtual int32_t getSpellIndex() const = 0; | ||||
| 	virtual SpellID getSpellId() const = 0; | ||||
| 	virtual std::string getSpellName() const = 0; | ||||
| 	virtual int32_t getSpellLevel() const = 0; | ||||
|  | ||||
| 	virtual bool isSmart() const = 0; | ||||
| 	virtual bool isMassive() const = 0; | ||||
| 	virtual bool alwaysHitFirstTarget() const = 0; | ||||
| 	virtual bool requiresClearTiles() const = 0; | ||||
|  | ||||
| 	virtual bool isNegativeSpell() const = 0; | ||||
| 	virtual bool isPositiveSpell() const = 0; | ||||
| 	virtual bool isMagicalEffect() const = 0; | ||||
|  | ||||
| 	virtual int64_t adjustEffectValue(const battle::Unit * target) const = 0; | ||||
| 	virtual int64_t applySpellBonus(int64_t value, const battle::Unit * target) const = 0; | ||||
| 	virtual int64_t applySpecificSpellBonus(int64_t value) const = 0; | ||||
| 	virtual int64_t calculateRawEffectValue(int32_t basePowerMultiplier, int32_t levelPowerMultiplier) const = 0; | ||||
|  | ||||
| 	//Battle facade | ||||
| 	virtual bool ownerMatches(const battle::Unit * unit) const = 0; | ||||
| 	virtual bool ownerMatches(const battle::Unit * unit, const boost::logic::tribool positivness) const = 0; | ||||
|  | ||||
| 	//Global environment facade | ||||
| 	virtual const CreatureService * creatures() const = 0; | ||||
| #if SCRIPTING_ENABLED | ||||
| 	virtual const scripting::Service * scripts() const = 0; | ||||
| #endif | ||||
| 	virtual const Service * spells() const = 0; | ||||
|  | ||||
| 	virtual const CBattleInfoCallback * battle() const = 0; | ||||
|  | ||||
| 	const Caster * caster; | ||||
|  | ||||
| 	BattleSide casterSide; | ||||
|  | ||||
| protected: | ||||
| 	Mechanics(); | ||||
| }; | ||||
|  | ||||
| class DLL_LINKAGE BaseMechanics : public Mechanics | ||||
| { | ||||
| public: | ||||
| 	virtual ~BaseMechanics(); | ||||
|  | ||||
| 	bool adaptProblem(ESpellCastProblem source, Problem & target) const override; | ||||
| 	bool adaptGenericProblem(Problem & target) const override; | ||||
|  | ||||
| 	int32_t getSpellIndex() const override; | ||||
| 	SpellID getSpellId() const override; | ||||
| 	std::string getSpellName() const override; | ||||
| 	int32_t getSpellLevel() const override; | ||||
|  | ||||
| 	IBattleCast::Value getEffectLevel() const override; | ||||
| 	IBattleCast::Value getRangeLevel() const override; | ||||
|  | ||||
| 	IBattleCast::Value getEffectPower() const override; | ||||
| 	IBattleCast::Value getEffectDuration() const override; | ||||
|  | ||||
| 	IBattleCast::Value64 getEffectValue() const override; | ||||
|  | ||||
| 	PlayerColor getCasterColor() const override; | ||||
|  | ||||
| 	bool isSmart() const override; | ||||
| 	bool isMassive() const override; | ||||
| 	bool requiresClearTiles() const override; | ||||
| 	bool alwaysHitFirstTarget() const override; | ||||
|  | ||||
| 	bool isNegativeSpell() const override; | ||||
| 	bool isPositiveSpell() const override; | ||||
| 	bool isMagicalEffect() const override; | ||||
|  | ||||
| 	int64_t adjustEffectValue(const battle::Unit * target) const override; | ||||
| 	int64_t applySpellBonus(int64_t value, const battle::Unit * target) const override; | ||||
| 	int64_t applySpecificSpellBonus(int64_t value) const override; | ||||
| 	int64_t calculateRawEffectValue(int32_t basePowerMultiplier, int32_t levelPowerMultiplier) const override; | ||||
|  | ||||
| 	bool ownerMatches(const battle::Unit * unit) const override; | ||||
| 	bool ownerMatches(const battle::Unit * unit, const boost::logic::tribool positivness) const override; | ||||
|  | ||||
| 	std::vector<AimType> getTargetTypes() const override; | ||||
|  | ||||
| 	const CreatureService * creatures() const override; | ||||
| #if SCRIPTING_ENABLED | ||||
| 	const scripting::Service * scripts() const override; | ||||
| #endif | ||||
| 	const Service * spells() const override; | ||||
|  | ||||
| 	const CBattleInfoCallback * battle() const override; | ||||
|  | ||||
| protected: | ||||
| 	const CSpell * owner; | ||||
| 	Mode mode; | ||||
|  | ||||
| 	BaseMechanics(const IBattleCast * event); | ||||
|  | ||||
| private: | ||||
|     IBattleCast::Value rangeLevel; | ||||
| 	IBattleCast::Value effectLevel; | ||||
|  | ||||
| 	///actual spell-power affecting effect values | ||||
| 	IBattleCast::Value effectPower; | ||||
| 	///actual spell-power affecting effect duration | ||||
| 	IBattleCast::Value effectDuration; | ||||
|  | ||||
| 	///raw damage/heal amount | ||||
| 	IBattleCast::Value64 effectValue; | ||||
|  | ||||
| 	boost::logic::tribool smart; | ||||
| 	boost::logic::tribool massive; | ||||
|  | ||||
| 	const CBattleInfoCallback * cb; | ||||
| }; | ||||
|  | ||||
| class DLL_LINKAGE IReceptiveCheck | ||||
| { | ||||
| public: | ||||
| 	virtual ~IReceptiveCheck() = default; | ||||
|  | ||||
| 	virtual bool isReceptive(const Mechanics * m, const battle::Unit * target) const = 0; | ||||
| }; | ||||
|  | ||||
| }// namespace spells | ||||
|  | ||||
| class DLL_LINKAGE AdventureSpellCastParameters | ||||
| { | ||||
| public: | ||||
| 	const spells::Caster * caster; | ||||
| 	int3 pos; | ||||
| }; | ||||
|  | ||||
| class DLL_LINKAGE IAdventureSpellMechanics | ||||
| { | ||||
| public: | ||||
| 	IAdventureSpellMechanics(const CSpell * s); | ||||
| 	virtual ~IAdventureSpellMechanics() = default; | ||||
|  | ||||
| 	virtual bool canBeCast(spells::Problem & problem, const CGameInfoCallback * cb, const spells::Caster * caster) const = 0; | ||||
| 	virtual bool canBeCastAt(spells::Problem & problem, const CGameInfoCallback * cb, const spells::Caster * caster, const int3 & pos) const = 0; | ||||
|  | ||||
| 	virtual bool adventureCast(SpellCastEnvironment * env, const AdventureSpellCastParameters & parameters) const = 0; | ||||
|  | ||||
| 	static std::unique_ptr<IAdventureSpellMechanics> createMechanics(const CSpell * s); | ||||
| protected: | ||||
| 	const CSpell * owner; | ||||
| }; | ||||
|  | ||||
| VCMI_LIB_NAMESPACE_END | ||||
| /* | ||||
|  * ISpellMechanics.h, part of VCMI engine | ||||
|  * | ||||
|  * Authors: listed in file AUTHORS in main folder | ||||
|  * | ||||
|  * License: GNU General Public License v2.0 or later | ||||
|  * Full text of license available in license.txt file, in main folder | ||||
|  * | ||||
|  */ | ||||
|  | ||||
| #pragma once | ||||
|  | ||||
| #include <vcmi/spells/Magic.h> | ||||
| #include <vcmi/ServerCallback.h> | ||||
|  | ||||
| #include "../battle/Destination.h" | ||||
| #include "../int3.h" | ||||
| #include "../GameConstants.h" | ||||
| #include "../bonuses/Bonus.h" | ||||
|  | ||||
| VCMI_LIB_NAMESPACE_BEGIN | ||||
|  | ||||
| struct Query; | ||||
| class IBattleState; | ||||
| class CreatureService; | ||||
| class CMap; | ||||
| class CGameInfoCallback; | ||||
| class CBattleInfoCallback; | ||||
| class JsonNode; | ||||
| class CStack; | ||||
| class CGObjectInstance; | ||||
| class CGHeroInstance; | ||||
|  | ||||
| namespace spells | ||||
| { | ||||
| class Service; | ||||
| } | ||||
|  | ||||
| namespace vstd | ||||
| { | ||||
| 	class RNG; | ||||
| } | ||||
|  | ||||
| #if SCRIPTING_ENABLED | ||||
| namespace scripting | ||||
| { | ||||
| 	class Service; | ||||
| } | ||||
| #endif | ||||
|  | ||||
|  | ||||
| ///callback to be provided by server | ||||
| class DLL_LINKAGE SpellCastEnvironment : public ServerCallback | ||||
| { | ||||
| public: | ||||
| 	virtual ~SpellCastEnvironment() = default; | ||||
|  | ||||
| 	virtual const CMap * getMap() const = 0; | ||||
| 	virtual const CGameInfoCallback * getCb() const = 0; | ||||
|  | ||||
| 	virtual void createBoat(const int3 & visitablePosition, BoatId type, PlayerColor initiator) = 0; | ||||
| 	virtual bool moveHero(ObjectInstanceID hid, int3 dst, EMovementMode mode) = 0;	//TODO: remove | ||||
|  | ||||
| 	virtual void genericQuery(Query * request, PlayerColor color, std::function<void(std::optional<int32_t>)> callback) = 0;//TODO: type safety on query, use generic query packet when implemented | ||||
| }; | ||||
|  | ||||
| namespace spells | ||||
| { | ||||
|  | ||||
| class DLL_LINKAGE IBattleCast | ||||
| { | ||||
| public: | ||||
| 	using Value = int32_t; | ||||
| 	using Value64 = int64_t; | ||||
|  | ||||
| 	using OptionalValue = std::optional<Value>; | ||||
| 	using OptionalValue64 = std::optional<Value64>; | ||||
|  | ||||
| 	virtual const CSpell * getSpell() const = 0; | ||||
| 	virtual Mode getMode() const = 0; | ||||
| 	virtual const Caster * getCaster() const = 0; | ||||
| 	virtual const CBattleInfoCallback * getBattle() const = 0; | ||||
|  | ||||
| 	virtual OptionalValue getSpellLevel() const = 0; | ||||
|  | ||||
| 	virtual OptionalValue getEffectPower() const = 0; | ||||
| 	virtual OptionalValue getEffectDuration() const = 0; | ||||
|  | ||||
| 	virtual OptionalValue64 getEffectValue() const = 0; | ||||
|  | ||||
| 	virtual boost::logic::tribool isSmart() const = 0; | ||||
| 	virtual boost::logic::tribool isMassive() const = 0; | ||||
| }; | ||||
|  | ||||
| ///all parameters of particular cast event | ||||
| class DLL_LINKAGE BattleCast : public IBattleCast | ||||
| { | ||||
| public: | ||||
| 	boost::logic::tribool smart; | ||||
| 	boost::logic::tribool massive; | ||||
|  | ||||
| 	//normal constructor | ||||
| 	BattleCast(const CBattleInfoCallback * cb_, const Caster * caster_, const Mode mode_, const CSpell * spell_); | ||||
|  | ||||
| 	//magic mirror constructor | ||||
| 	BattleCast(const BattleCast & orig, const Caster * caster_); | ||||
|  | ||||
| 	virtual ~BattleCast(); | ||||
|  | ||||
| 	///IBattleCast | ||||
| 	const CSpell * getSpell() const override; | ||||
| 	Mode getMode() const override; | ||||
| 	const Caster * getCaster() const override; | ||||
| 	const CBattleInfoCallback * getBattle() const override; | ||||
|  | ||||
| 	OptionalValue getSpellLevel() const override; | ||||
|  | ||||
| 	OptionalValue getEffectPower() const override; | ||||
| 	OptionalValue getEffectDuration() const override; | ||||
|  | ||||
| 	OptionalValue64 getEffectValue() const override; | ||||
|  | ||||
| 	boost::logic::tribool isSmart() const override; | ||||
| 	boost::logic::tribool isMassive() const override; | ||||
|  | ||||
| 	void setSpellLevel(Value value); | ||||
|  | ||||
| 	void setEffectPower(Value value); | ||||
| 	void setEffectDuration(Value value); | ||||
|  | ||||
| 	void setEffectValue(Value64 value); | ||||
|  | ||||
| 	///only apply effects to specified targets | ||||
| 	void applyEffects(ServerCallback * server, const Target & target, bool indirect = false, bool ignoreImmunity = false) const; | ||||
|  | ||||
| 	///normal cast | ||||
| 	void cast(ServerCallback * server, Target target); | ||||
|  | ||||
| 	///cast evaluation | ||||
| 	void castEval(ServerCallback * server, Target target); | ||||
|  | ||||
| 	///cast with silent check for permitted cast | ||||
| 	bool castIfPossible(ServerCallback * server, Target target); | ||||
|  | ||||
| 	std::vector<Target> findPotentialTargets(bool fast = false) const; | ||||
|  | ||||
| private: | ||||
| 	///spell school level | ||||
| 	OptionalValue magicSkillLevel; | ||||
|  | ||||
| 	///actual spell-power affecting effect values | ||||
| 	OptionalValue effectPower; | ||||
| 	///actual spell-power affecting effect duration | ||||
| 	OptionalValue effectDuration; | ||||
|  | ||||
| 	///for Archangel-like casting | ||||
| 	OptionalValue64 effectValue; | ||||
|  | ||||
| 	Mode mode; | ||||
| 	const CSpell * spell; | ||||
| 	const CBattleInfoCallback * cb; | ||||
| 	const Caster * caster; | ||||
| }; | ||||
|  | ||||
| class DLL_LINKAGE ISpellMechanicsFactory | ||||
| { | ||||
| public: | ||||
| 	virtual ~ISpellMechanicsFactory(); | ||||
|  | ||||
| 	virtual std::unique_ptr<Mechanics> create(const IBattleCast * event) const = 0; | ||||
|  | ||||
| 	static std::unique_ptr<ISpellMechanicsFactory> get(const CSpell * s); | ||||
|  | ||||
| protected: | ||||
| 	const CSpell * spell; | ||||
|  | ||||
| 	ISpellMechanicsFactory(const CSpell * s); | ||||
| }; | ||||
|  | ||||
| class DLL_LINKAGE Mechanics | ||||
| { | ||||
| public: | ||||
| 	virtual ~Mechanics(); | ||||
|  | ||||
| 	virtual bool adaptProblem(ESpellCastProblem source, Problem & target) const = 0; | ||||
| 	virtual bool adaptGenericProblem(Problem & target) const = 0; | ||||
|  | ||||
| 	virtual BattleHexArray rangeInHexes(BattleHex centralHex) const = 0; | ||||
| 	virtual std::vector<const CStack *> getAffectedStacks(const Target & target) const = 0; | ||||
|  | ||||
| 	virtual bool canBeCast(Problem & problem) const = 0; | ||||
| 	virtual bool canBeCastAt(const Target & target, Problem & problem) const = 0; | ||||
|  | ||||
| 	virtual void applyEffects(ServerCallback * server, const Target & targets, bool indirect, bool ignoreImmunity) const = 0; | ||||
|  | ||||
| 	virtual void cast(ServerCallback * server, const Target & target) = 0; | ||||
|  | ||||
| 	virtual void castEval(ServerCallback * server, const Target & target) = 0; | ||||
|  | ||||
| 	virtual bool isReceptive(const battle::Unit * target) const = 0; | ||||
|  | ||||
| 	virtual std::vector<AimType> getTargetTypes() const = 0; | ||||
|  | ||||
| 	virtual std::vector<Destination> getPossibleDestinations(size_t index, AimType aimType, const Target & current, bool fast = false) const = 0; | ||||
|  | ||||
| 	virtual const Spell * getSpell() const = 0; | ||||
|  | ||||
| 	//Cast event facade | ||||
|  | ||||
| 	virtual IBattleCast::Value getEffectLevel() const = 0; | ||||
| 	virtual IBattleCast::Value getRangeLevel() const = 0; | ||||
|  | ||||
| 	virtual IBattleCast::Value getEffectPower() const = 0; | ||||
| 	virtual IBattleCast::Value getEffectDuration() const = 0; | ||||
|  | ||||
| 	virtual IBattleCast::Value64 getEffectValue() const = 0; | ||||
|  | ||||
| 	virtual PlayerColor getCasterColor() const = 0; | ||||
|  | ||||
| 	//Spell facade | ||||
| 	virtual int32_t getSpellIndex() const = 0; | ||||
| 	virtual SpellID getSpellId() const = 0; | ||||
| 	virtual std::string getSpellName() const = 0; | ||||
| 	virtual int32_t getSpellLevel() const = 0; | ||||
|  | ||||
| 	virtual bool isSmart() const = 0; | ||||
| 	virtual bool isMassive() const = 0; | ||||
| 	virtual bool alwaysHitFirstTarget() const = 0; | ||||
| 	virtual bool requiresClearTiles() const = 0; | ||||
|  | ||||
| 	virtual bool isNegativeSpell() const = 0; | ||||
| 	virtual bool isPositiveSpell() const = 0; | ||||
| 	virtual bool isMagicalEffect() const = 0; | ||||
|  | ||||
| 	virtual int64_t adjustEffectValue(const battle::Unit * target) const = 0; | ||||
| 	virtual int64_t applySpellBonus(int64_t value, const battle::Unit * target) const = 0; | ||||
| 	virtual int64_t applySpecificSpellBonus(int64_t value) const = 0; | ||||
| 	virtual int64_t calculateRawEffectValue(int32_t basePowerMultiplier, int32_t levelPowerMultiplier) const = 0; | ||||
|  | ||||
| 	//Battle facade | ||||
| 	virtual bool ownerMatches(const battle::Unit * unit) const = 0; | ||||
| 	virtual bool ownerMatches(const battle::Unit * unit, const boost::logic::tribool positivness) const = 0; | ||||
|  | ||||
| 	//Global environment facade | ||||
| 	virtual const CreatureService * creatures() const = 0; | ||||
| #if SCRIPTING_ENABLED | ||||
| 	virtual const scripting::Service * scripts() const = 0; | ||||
| #endif | ||||
| 	virtual const Service * spells() const = 0; | ||||
|  | ||||
| 	virtual const CBattleInfoCallback * battle() const = 0; | ||||
|  | ||||
| 	const Caster * caster; | ||||
|  | ||||
| 	BattleSide casterSide; | ||||
|  | ||||
| protected: | ||||
| 	Mechanics(); | ||||
| }; | ||||
|  | ||||
| class DLL_LINKAGE BaseMechanics : public Mechanics | ||||
| { | ||||
| public: | ||||
| 	virtual ~BaseMechanics(); | ||||
|  | ||||
| 	bool adaptProblem(ESpellCastProblem source, Problem & target) const override; | ||||
| 	bool adaptGenericProblem(Problem & target) const override; | ||||
|  | ||||
| 	int32_t getSpellIndex() const override; | ||||
| 	SpellID getSpellId() const override; | ||||
| 	std::string getSpellName() const override; | ||||
| 	int32_t getSpellLevel() const override; | ||||
|  | ||||
| 	IBattleCast::Value getEffectLevel() const override; | ||||
| 	IBattleCast::Value getRangeLevel() const override; | ||||
|  | ||||
| 	IBattleCast::Value getEffectPower() const override; | ||||
| 	IBattleCast::Value getEffectDuration() const override; | ||||
|  | ||||
| 	IBattleCast::Value64 getEffectValue() const override; | ||||
|  | ||||
| 	PlayerColor getCasterColor() const override; | ||||
|  | ||||
| 	bool isSmart() const override; | ||||
| 	bool isMassive() const override; | ||||
| 	bool requiresClearTiles() const override; | ||||
| 	bool alwaysHitFirstTarget() const override; | ||||
|  | ||||
| 	bool isNegativeSpell() const override; | ||||
| 	bool isPositiveSpell() const override; | ||||
| 	bool isMagicalEffect() const override; | ||||
|  | ||||
| 	int64_t adjustEffectValue(const battle::Unit * target) const override; | ||||
| 	int64_t applySpellBonus(int64_t value, const battle::Unit * target) const override; | ||||
| 	int64_t applySpecificSpellBonus(int64_t value) const override; | ||||
| 	int64_t calculateRawEffectValue(int32_t basePowerMultiplier, int32_t levelPowerMultiplier) const override; | ||||
|  | ||||
| 	bool ownerMatches(const battle::Unit * unit) const override; | ||||
| 	bool ownerMatches(const battle::Unit * unit, const boost::logic::tribool positivness) const override; | ||||
|  | ||||
| 	std::vector<AimType> getTargetTypes() const override; | ||||
|  | ||||
| 	const CreatureService * creatures() const override; | ||||
| #if SCRIPTING_ENABLED | ||||
| 	const scripting::Service * scripts() const override; | ||||
| #endif | ||||
| 	const Service * spells() const override; | ||||
|  | ||||
| 	const CBattleInfoCallback * battle() const override; | ||||
|  | ||||
| protected: | ||||
| 	const CSpell * owner; | ||||
| 	Mode mode; | ||||
|  | ||||
| 	BaseMechanics(const IBattleCast * event); | ||||
|  | ||||
| private: | ||||
|     IBattleCast::Value rangeLevel; | ||||
| 	IBattleCast::Value effectLevel; | ||||
|  | ||||
| 	///actual spell-power affecting effect values | ||||
| 	IBattleCast::Value effectPower; | ||||
| 	///actual spell-power affecting effect duration | ||||
| 	IBattleCast::Value effectDuration; | ||||
|  | ||||
| 	///raw damage/heal amount | ||||
| 	IBattleCast::Value64 effectValue; | ||||
|  | ||||
| 	boost::logic::tribool smart; | ||||
| 	boost::logic::tribool massive; | ||||
|  | ||||
| 	const CBattleInfoCallback * cb; | ||||
| }; | ||||
|  | ||||
| class DLL_LINKAGE IReceptiveCheck | ||||
| { | ||||
| public: | ||||
| 	virtual ~IReceptiveCheck() = default; | ||||
|  | ||||
| 	virtual bool isReceptive(const Mechanics * m, const battle::Unit * target) const = 0; | ||||
| }; | ||||
|  | ||||
| }// namespace spells | ||||
|  | ||||
| class DLL_LINKAGE AdventureSpellCastParameters | ||||
| { | ||||
| public: | ||||
| 	const spells::Caster * caster; | ||||
| 	int3 pos; | ||||
| }; | ||||
|  | ||||
| class DLL_LINKAGE IAdventureSpellMechanics | ||||
| { | ||||
| public: | ||||
| 	IAdventureSpellMechanics(const CSpell * s); | ||||
| 	virtual ~IAdventureSpellMechanics() = default; | ||||
|  | ||||
| 	virtual bool canBeCast(spells::Problem & problem, const CGameInfoCallback * cb, const spells::Caster * caster) const = 0; | ||||
| 	virtual bool canBeCastAt(spells::Problem & problem, const CGameInfoCallback * cb, const spells::Caster * caster, const int3 & pos) const = 0; | ||||
|  | ||||
| 	virtual bool adventureCast(SpellCastEnvironment * env, const AdventureSpellCastParameters & parameters) const = 0; | ||||
|  | ||||
| 	static std::unique_ptr<IAdventureSpellMechanics> createMechanics(const CSpell * s); | ||||
| protected: | ||||
| 	const CSpell * owner; | ||||
| }; | ||||
|  | ||||
| VCMI_LIB_NAMESPACE_END | ||||
|   | ||||
| @@ -1,83 +1,83 @@ | ||||
| /* | ||||
|  * Effect.h, part of VCMI engine | ||||
|  * | ||||
|  * Authors: listed in file AUTHORS in main folder | ||||
|  * | ||||
|  * License: GNU General Public License v2.0 or later | ||||
|  * Full text of license available in license.txt file, in main folder | ||||
|  * | ||||
|  */ | ||||
|  | ||||
| #pragma once | ||||
|  | ||||
| #include <vcmi/spells/Magic.h> | ||||
|  | ||||
| VCMI_LIB_NAMESPACE_BEGIN | ||||
|  | ||||
| struct BattleHex; | ||||
| class BattleHexArray; | ||||
| class CBattleInfoCallback; | ||||
| class JsonSerializeFormat; | ||||
| class ServerCallback; | ||||
|  | ||||
| namespace vstd | ||||
| { | ||||
| 	class RNG; | ||||
| } | ||||
|  | ||||
| namespace spells | ||||
| { | ||||
| using EffectTarget = Target; | ||||
|  | ||||
| namespace effects | ||||
| { | ||||
| using RNG = vstd::RNG; | ||||
| class Effects; | ||||
| class Effect; | ||||
| class Registry; | ||||
|  | ||||
| using TargetType = spells::AimType; | ||||
|  | ||||
| class DLL_LINKAGE Effect | ||||
| { | ||||
| public: | ||||
| 	bool indirect = false; | ||||
| 	bool optional = false; | ||||
|  | ||||
| 	std::string name; | ||||
|  | ||||
| 	virtual ~Effect() = default; //Required for child classes | ||||
|  | ||||
| 	// TODO: document me | ||||
| 	virtual void adjustTargetTypes(std::vector<TargetType> & types) const = 0; | ||||
|  | ||||
| 	/// Generates list of hexes affected by spell, if spell were to cast at specified target | ||||
| 	virtual void adjustAffectedHexes(BattleHexArray & hexes, const Mechanics * m, const Target & spellTarget) const = 0; | ||||
|  | ||||
| 	/// Returns whether effect has any valid targets on the battlefield | ||||
| 	virtual bool applicable(Problem & problem, const Mechanics * m) const; | ||||
|  | ||||
| 	/// Returns whether effect is valid and can be applied onto selected target | ||||
| 	virtual bool applicable(Problem & problem, const Mechanics * m, const EffectTarget & target) const; | ||||
|  | ||||
| 	virtual void apply(ServerCallback * server, const Mechanics * m, const EffectTarget & target) const = 0; | ||||
|  | ||||
| 	/// Processes input target and generates subset-result that contains only valid targets | ||||
| 	virtual EffectTarget filterTarget(const Mechanics * m, const EffectTarget & target) const = 0; | ||||
|  | ||||
| 	// TODO: document me | ||||
| 	virtual EffectTarget transformTarget(const Mechanics * m, const Target & aimPoint, const Target & spellTarget) const = 0; | ||||
|  | ||||
| 	/// Serializes (or deserializes) parameters of Effect | ||||
| 	void serializeJson(JsonSerializeFormat & handler); | ||||
|  | ||||
| 	static std::shared_ptr<Effect> create(const Registry * registry, const std::string & type); | ||||
|  | ||||
| protected: | ||||
| 	virtual void serializeJsonEffect(JsonSerializeFormat & handler) = 0; | ||||
| }; | ||||
|  | ||||
| } | ||||
| } | ||||
|  | ||||
| VCMI_LIB_NAMESPACE_END | ||||
| /* | ||||
|  * Effect.h, part of VCMI engine | ||||
|  * | ||||
|  * Authors: listed in file AUTHORS in main folder | ||||
|  * | ||||
|  * License: GNU General Public License v2.0 or later | ||||
|  * Full text of license available in license.txt file, in main folder | ||||
|  * | ||||
|  */ | ||||
|  | ||||
| #pragma once | ||||
|  | ||||
| #include <vcmi/spells/Magic.h> | ||||
|  | ||||
| VCMI_LIB_NAMESPACE_BEGIN | ||||
|  | ||||
| struct BattleHex; | ||||
| class BattleHexArray; | ||||
| class CBattleInfoCallback; | ||||
| class JsonSerializeFormat; | ||||
| class ServerCallback; | ||||
|  | ||||
| namespace vstd | ||||
| { | ||||
| 	class RNG; | ||||
| } | ||||
|  | ||||
| namespace spells | ||||
| { | ||||
| using EffectTarget = Target; | ||||
|  | ||||
| namespace effects | ||||
| { | ||||
| using RNG = vstd::RNG; | ||||
| class Effects; | ||||
| class Effect; | ||||
| class Registry; | ||||
|  | ||||
| using TargetType = spells::AimType; | ||||
|  | ||||
| class DLL_LINKAGE Effect | ||||
| { | ||||
| public: | ||||
| 	bool indirect = false; | ||||
| 	bool optional = false; | ||||
|  | ||||
| 	std::string name; | ||||
|  | ||||
| 	virtual ~Effect() = default; //Required for child classes | ||||
|  | ||||
| 	// TODO: document me | ||||
| 	virtual void adjustTargetTypes(std::vector<TargetType> & types) const = 0; | ||||
|  | ||||
| 	/// Generates list of hexes affected by spell, if spell were to cast at specified target | ||||
| 	virtual void adjustAffectedHexes(BattleHexArray & hexes, const Mechanics * m, const Target & spellTarget) const = 0; | ||||
|  | ||||
| 	/// Returns whether effect has any valid targets on the battlefield | ||||
| 	virtual bool applicable(Problem & problem, const Mechanics * m) const; | ||||
|  | ||||
| 	/// Returns whether effect is valid and can be applied onto selected target | ||||
| 	virtual bool applicable(Problem & problem, const Mechanics * m, const EffectTarget & target) const; | ||||
|  | ||||
| 	virtual void apply(ServerCallback * server, const Mechanics * m, const EffectTarget & target) const = 0; | ||||
|  | ||||
| 	/// Processes input target and generates subset-result that contains only valid targets | ||||
| 	virtual EffectTarget filterTarget(const Mechanics * m, const EffectTarget & target) const = 0; | ||||
|  | ||||
| 	// TODO: document me | ||||
| 	virtual EffectTarget transformTarget(const Mechanics * m, const Target & aimPoint, const Target & spellTarget) const = 0; | ||||
|  | ||||
| 	/// Serializes (or deserializes) parameters of Effect | ||||
| 	void serializeJson(JsonSerializeFormat & handler); | ||||
|  | ||||
| 	static std::shared_ptr<Effect> create(const Registry * registry, const std::string & type); | ||||
|  | ||||
| protected: | ||||
| 	virtual void serializeJsonEffect(JsonSerializeFormat & handler) = 0; | ||||
| }; | ||||
|  | ||||
| } | ||||
| } | ||||
|  | ||||
| VCMI_LIB_NAMESPACE_END | ||||
|   | ||||
| @@ -1,52 +1,52 @@ | ||||
| /* | ||||
|  * LocationEffect.cpp, part of VCMI engine | ||||
|  * | ||||
|  * Authors: listed in file AUTHORS in main folder | ||||
|  * | ||||
|  * License: GNU General Public License v2.0 or later | ||||
|  * Full text of license available in license.txt file, in main folder | ||||
|  * | ||||
|  */ | ||||
| #include "StdInc.h" | ||||
|  | ||||
| #include "LocationEffect.h" | ||||
| #include "../ISpellMechanics.h" | ||||
|  | ||||
| VCMI_LIB_NAMESPACE_BEGIN | ||||
|  | ||||
| namespace spells | ||||
| { | ||||
| namespace effects | ||||
| { | ||||
|  | ||||
| void LocationEffect::adjustTargetTypes(std::vector<TargetType> & types) const | ||||
| { | ||||
|  | ||||
| } | ||||
|  | ||||
| void LocationEffect::adjustAffectedHexes(BattleHexArray & hexes, const Mechanics * m, const Target & spellTarget) const | ||||
| { | ||||
| 	for(const auto & destnation : spellTarget) | ||||
| 		hexes.insert(destnation.hexValue); | ||||
| } | ||||
|  | ||||
| EffectTarget LocationEffect::filterTarget(const Mechanics * m, const EffectTarget & target) const | ||||
| { | ||||
| 	EffectTarget res; | ||||
| 	vstd::copy_if(target, std::back_inserter(res), [](const Destination & d) | ||||
| 	{ | ||||
| 		return !d.unitValue && (d.hexValue.isValid()); | ||||
| 	}); | ||||
| 	return res; | ||||
| } | ||||
|  | ||||
| EffectTarget LocationEffect::transformTarget(const Mechanics * m, const Target & aimPoint, const Target & spellTarget) const | ||||
| { | ||||
| 	//by default effect covers exactly spell range | ||||
| 	return EffectTarget(spellTarget); | ||||
| } | ||||
|  | ||||
| } | ||||
| } | ||||
|  | ||||
| VCMI_LIB_NAMESPACE_END | ||||
| /* | ||||
|  * LocationEffect.cpp, part of VCMI engine | ||||
|  * | ||||
|  * Authors: listed in file AUTHORS in main folder | ||||
|  * | ||||
|  * License: GNU General Public License v2.0 or later | ||||
|  * Full text of license available in license.txt file, in main folder | ||||
|  * | ||||
|  */ | ||||
| #include "StdInc.h" | ||||
|  | ||||
| #include "LocationEffect.h" | ||||
| #include "../ISpellMechanics.h" | ||||
|  | ||||
| VCMI_LIB_NAMESPACE_BEGIN | ||||
|  | ||||
| namespace spells | ||||
| { | ||||
| namespace effects | ||||
| { | ||||
|  | ||||
| void LocationEffect::adjustTargetTypes(std::vector<TargetType> & types) const | ||||
| { | ||||
|  | ||||
| } | ||||
|  | ||||
| void LocationEffect::adjustAffectedHexes(BattleHexArray & hexes, const Mechanics * m, const Target & spellTarget) const | ||||
| { | ||||
| 	for(const auto & destnation : spellTarget) | ||||
| 		hexes.insert(destnation.hexValue); | ||||
| } | ||||
|  | ||||
| EffectTarget LocationEffect::filterTarget(const Mechanics * m, const EffectTarget & target) const | ||||
| { | ||||
| 	EffectTarget res; | ||||
| 	vstd::copy_if(target, std::back_inserter(res), [](const Destination & d) | ||||
| 	{ | ||||
| 		return !d.unitValue && (d.hexValue.isValid()); | ||||
| 	}); | ||||
| 	return res; | ||||
| } | ||||
|  | ||||
| EffectTarget LocationEffect::transformTarget(const Mechanics * m, const Target & aimPoint, const Target & spellTarget) const | ||||
| { | ||||
| 	//by default effect covers exactly spell range | ||||
| 	return EffectTarget(spellTarget); | ||||
| } | ||||
|  | ||||
| } | ||||
| } | ||||
|  | ||||
| VCMI_LIB_NAMESPACE_END | ||||
|   | ||||
| @@ -1,41 +1,41 @@ | ||||
| /* | ||||
|  * LocationEffect.h, part of VCMI engine | ||||
|  * | ||||
|  * Authors: listed in file AUTHORS in main folder | ||||
|  * | ||||
|  * License: GNU General Public License v2.0 or later | ||||
|  * Full text of license available in license.txt file, in main folder | ||||
|  * | ||||
|  */ | ||||
|  | ||||
| #pragma once | ||||
|  | ||||
| #include "Effect.h" | ||||
|  | ||||
| VCMI_LIB_NAMESPACE_BEGIN | ||||
|  | ||||
|  | ||||
| namespace spells | ||||
| { | ||||
| namespace effects | ||||
| { | ||||
|  | ||||
| class LocationEffect : public Effect | ||||
| { | ||||
| public: | ||||
| 	void adjustTargetTypes(std::vector<TargetType> & types) const override; | ||||
|  | ||||
| 	void adjustAffectedHexes(BattleHexArray & hexes, const Mechanics * m, const Target & spellTarget) const override; | ||||
|  | ||||
| 	EffectTarget filterTarget(const Mechanics * m, const EffectTarget & target) const override; | ||||
|  | ||||
| 	EffectTarget transformTarget(const Mechanics * m, const Target & aimPoint, const Target & spellTarget) const override; | ||||
| protected: | ||||
|  | ||||
| private: | ||||
| }; | ||||
|  | ||||
| } | ||||
| } | ||||
|  | ||||
| VCMI_LIB_NAMESPACE_END | ||||
| /* | ||||
|  * LocationEffect.h, part of VCMI engine | ||||
|  * | ||||
|  * Authors: listed in file AUTHORS in main folder | ||||
|  * | ||||
|  * License: GNU General Public License v2.0 or later | ||||
|  * Full text of license available in license.txt file, in main folder | ||||
|  * | ||||
|  */ | ||||
|  | ||||
| #pragma once | ||||
|  | ||||
| #include "Effect.h" | ||||
|  | ||||
| VCMI_LIB_NAMESPACE_BEGIN | ||||
|  | ||||
|  | ||||
| namespace spells | ||||
| { | ||||
| namespace effects | ||||
| { | ||||
|  | ||||
| class LocationEffect : public Effect | ||||
| { | ||||
| public: | ||||
| 	void adjustTargetTypes(std::vector<TargetType> & types) const override; | ||||
|  | ||||
| 	void adjustAffectedHexes(BattleHexArray & hexes, const Mechanics * m, const Target & spellTarget) const override; | ||||
|  | ||||
| 	EffectTarget filterTarget(const Mechanics * m, const EffectTarget & target) const override; | ||||
|  | ||||
| 	EffectTarget transformTarget(const Mechanics * m, const Target & aimPoint, const Target & spellTarget) const override; | ||||
| protected: | ||||
|  | ||||
| private: | ||||
| }; | ||||
|  | ||||
| } | ||||
| } | ||||
|  | ||||
| VCMI_LIB_NAMESPACE_END | ||||
|   | ||||
| @@ -1,43 +1,43 @@ | ||||
| /* | ||||
|  * Moat.h, part of VCMI engine | ||||
|  * | ||||
|  * Authors: listed in file AUTHORS in main folder | ||||
|  * | ||||
|  * License: GNU General Public License v2.0 or later | ||||
|  * Full text of license available in license.txt file, in main folder | ||||
|  * | ||||
|  */ | ||||
|  | ||||
| #pragma once | ||||
|  | ||||
| #include "Obstacle.h" | ||||
|  | ||||
| VCMI_LIB_NAMESPACE_BEGIN | ||||
|  | ||||
| struct Bonus; | ||||
|  | ||||
| namespace spells | ||||
| { | ||||
| namespace effects | ||||
| { | ||||
|  | ||||
| class Moat : public Obstacle | ||||
| { | ||||
| private: | ||||
| 	ObstacleSideOptions sideOptions; //Defender only | ||||
| 	std::vector<BattleHexArray> moatHexes; //Determine number of moat patches and hexes | ||||
| 	std::vector<std::shared_ptr<Bonus>> bonus; //For battle-wide bonuses | ||||
| 	bool dispellable; //For Tower landmines | ||||
| 	int moatDamage; // Minimal moat damage | ||||
| public: | ||||
| 	void apply(ServerCallback * server, const Mechanics * m, const EffectTarget & target) const override; | ||||
| protected: | ||||
| 	void serializeJsonEffect(JsonSerializeFormat & handler) override; | ||||
| 	void placeObstacles(ServerCallback * server, const Mechanics * m, const EffectTarget & target) const override; | ||||
| 	void convertBonus(const Mechanics * m, std::vector<Bonus> & converted) const; | ||||
| }; | ||||
|  | ||||
| } | ||||
| } | ||||
|  | ||||
| VCMI_LIB_NAMESPACE_END | ||||
| /* | ||||
|  * Moat.h, part of VCMI engine | ||||
|  * | ||||
|  * Authors: listed in file AUTHORS in main folder | ||||
|  * | ||||
|  * License: GNU General Public License v2.0 or later | ||||
|  * Full text of license available in license.txt file, in main folder | ||||
|  * | ||||
|  */ | ||||
|  | ||||
| #pragma once | ||||
|  | ||||
| #include "Obstacle.h" | ||||
|  | ||||
| VCMI_LIB_NAMESPACE_BEGIN | ||||
|  | ||||
| struct Bonus; | ||||
|  | ||||
| namespace spells | ||||
| { | ||||
| namespace effects | ||||
| { | ||||
|  | ||||
| class Moat : public Obstacle | ||||
| { | ||||
| private: | ||||
| 	ObstacleSideOptions sideOptions; //Defender only | ||||
| 	std::vector<BattleHexArray> moatHexes; //Determine number of moat patches and hexes | ||||
| 	std::vector<std::shared_ptr<Bonus>> bonus; //For battle-wide bonuses | ||||
| 	bool dispellable; //For Tower landmines | ||||
| 	int moatDamage; // Minimal moat damage | ||||
| public: | ||||
| 	void apply(ServerCallback * server, const Mechanics * m, const EffectTarget & target) const override; | ||||
| protected: | ||||
| 	void serializeJsonEffect(JsonSerializeFormat & handler) override; | ||||
| 	void placeObstacles(ServerCallback * server, const Mechanics * m, const EffectTarget & target) const override; | ||||
| 	void convertBonus(const Mechanics * m, std::vector<Bonus> & converted) const; | ||||
| }; | ||||
|  | ||||
| } | ||||
| } | ||||
|  | ||||
| VCMI_LIB_NAMESPACE_END | ||||
|   | ||||
| @@ -1,78 +1,78 @@ | ||||
| /* | ||||
|  * Obstacle.h, part of VCMI engine | ||||
|  * | ||||
|  * Authors: listed in file AUTHORS in main folder | ||||
|  * | ||||
|  * License: GNU General Public License v2.0 or later | ||||
|  * Full text of license available in license.txt file, in main folder | ||||
|  * | ||||
|  */ | ||||
|  | ||||
| #pragma once | ||||
|  | ||||
| #include "LocationEffect.h" | ||||
| #include "../../GameConstants.h" | ||||
| #include "../../battle/BattleHexArray.h" | ||||
| #include "../../battle/CObstacleInstance.h" | ||||
|  | ||||
| VCMI_LIB_NAMESPACE_BEGIN | ||||
|  | ||||
| namespace spells | ||||
| { | ||||
| namespace effects | ||||
| { | ||||
|  | ||||
| class ObstacleSideOptions | ||||
| { | ||||
| public: | ||||
| 	using RelativeShape = std::vector<std::vector<BattleHex::EDir>>; | ||||
|  | ||||
| 	RelativeShape shape; //shape of single obstacle relative to obstacle position | ||||
| 	RelativeShape range; //position of obstacles relative to effect destination | ||||
|  | ||||
| 	AudioPath appearSound; | ||||
| 	AnimationPath appearAnimation; | ||||
| 	AnimationPath animation; | ||||
|  | ||||
| 	int offsetY = 0; | ||||
|  | ||||
| 	void serializeJson(JsonSerializeFormat & handler); | ||||
| }; | ||||
|  | ||||
| class Obstacle : public LocationEffect | ||||
| { | ||||
| public: | ||||
| 	void adjustAffectedHexes(BattleHexArray & hexes, const Mechanics * m, const Target & spellTarget) const override; | ||||
|  | ||||
| 	bool applicable(Problem & problem, const Mechanics * m) const override; | ||||
| 	bool applicable(Problem & problem, const Mechanics * m, const EffectTarget & target) const override; | ||||
|  | ||||
| 	EffectTarget transformTarget(const Mechanics * m, const Target & aimPoint, const Target & spellTarget) const override; | ||||
|  | ||||
| 	void apply(ServerCallback * server, const Mechanics * m, const EffectTarget & target) const override; | ||||
|  | ||||
| protected: | ||||
| 	void serializeJsonEffect(JsonSerializeFormat & handler) override; | ||||
| 	virtual void placeObstacles(ServerCallback * server, const Mechanics * m, const EffectTarget & target) const; | ||||
|  | ||||
| 	bool hidden = false; | ||||
| 	bool trigger = false; | ||||
| 	bool trap = false; | ||||
| 	bool removeOnTrigger = false; | ||||
| 	bool hideNative = false; | ||||
| 	SpellID triggerAbility; | ||||
| private: | ||||
| 	int32_t patchCount = 0; //random patches to place, for massive spells should be >= 1, for non-massive ones if >= 1, then place only this number inside a target (like H5 landMine) | ||||
| 	bool passable = false; | ||||
| 	int32_t turnsRemaining = -1; | ||||
|  | ||||
| 	BattleSideArray<ObstacleSideOptions> sideOptions; | ||||
|  | ||||
| 	static bool isHexAvailable(const CBattleInfoCallback * cb, const BattleHex & hex, const bool mustBeClear); | ||||
| 	static bool noRoomToPlace(Problem & problem, const Mechanics * m); | ||||
| }; | ||||
|  | ||||
| } | ||||
| } | ||||
|  | ||||
| VCMI_LIB_NAMESPACE_END | ||||
| /* | ||||
|  * Obstacle.h, part of VCMI engine | ||||
|  * | ||||
|  * Authors: listed in file AUTHORS in main folder | ||||
|  * | ||||
|  * License: GNU General Public License v2.0 or later | ||||
|  * Full text of license available in license.txt file, in main folder | ||||
|  * | ||||
|  */ | ||||
|  | ||||
| #pragma once | ||||
|  | ||||
| #include "LocationEffect.h" | ||||
| #include "../../GameConstants.h" | ||||
| #include "../../battle/BattleHexArray.h" | ||||
| #include "../../battle/CObstacleInstance.h" | ||||
|  | ||||
| VCMI_LIB_NAMESPACE_BEGIN | ||||
|  | ||||
| namespace spells | ||||
| { | ||||
| namespace effects | ||||
| { | ||||
|  | ||||
| class ObstacleSideOptions | ||||
| { | ||||
| public: | ||||
| 	using RelativeShape = std::vector<std::vector<BattleHex::EDir>>; | ||||
|  | ||||
| 	RelativeShape shape; //shape of single obstacle relative to obstacle position | ||||
| 	RelativeShape range; //position of obstacles relative to effect destination | ||||
|  | ||||
| 	AudioPath appearSound; | ||||
| 	AnimationPath appearAnimation; | ||||
| 	AnimationPath animation; | ||||
|  | ||||
| 	int offsetY = 0; | ||||
|  | ||||
| 	void serializeJson(JsonSerializeFormat & handler); | ||||
| }; | ||||
|  | ||||
| class Obstacle : public LocationEffect | ||||
| { | ||||
| public: | ||||
| 	void adjustAffectedHexes(BattleHexArray & hexes, const Mechanics * m, const Target & spellTarget) const override; | ||||
|  | ||||
| 	bool applicable(Problem & problem, const Mechanics * m) const override; | ||||
| 	bool applicable(Problem & problem, const Mechanics * m, const EffectTarget & target) const override; | ||||
|  | ||||
| 	EffectTarget transformTarget(const Mechanics * m, const Target & aimPoint, const Target & spellTarget) const override; | ||||
|  | ||||
| 	void apply(ServerCallback * server, const Mechanics * m, const EffectTarget & target) const override; | ||||
|  | ||||
| protected: | ||||
| 	void serializeJsonEffect(JsonSerializeFormat & handler) override; | ||||
| 	virtual void placeObstacles(ServerCallback * server, const Mechanics * m, const EffectTarget & target) const; | ||||
|  | ||||
| 	bool hidden = false; | ||||
| 	bool trigger = false; | ||||
| 	bool trap = false; | ||||
| 	bool removeOnTrigger = false; | ||||
| 	bool hideNative = false; | ||||
| 	SpellID triggerAbility; | ||||
| private: | ||||
| 	int32_t patchCount = 0; //random patches to place, for massive spells should be >= 1, for non-massive ones if >= 1, then place only this number inside a target (like H5 landMine) | ||||
| 	bool passable = false; | ||||
| 	int32_t turnsRemaining = -1; | ||||
|  | ||||
| 	BattleSideArray<ObstacleSideOptions> sideOptions; | ||||
|  | ||||
| 	static bool isHexAvailable(const CBattleInfoCallback * cb, const BattleHex & hex, const bool mustBeClear); | ||||
| 	static bool noRoomToPlace(Problem & problem, const Mechanics * m); | ||||
| }; | ||||
|  | ||||
| } | ||||
| } | ||||
|  | ||||
| VCMI_LIB_NAMESPACE_END | ||||
|   | ||||
| @@ -1,55 +1,55 @@ | ||||
| /* | ||||
|  * Summon.h, part of VCMI engine | ||||
|  * | ||||
|  * Authors: listed in file AUTHORS in main folder | ||||
|  * | ||||
|  * License: GNU General Public License v2.0 or later | ||||
|  * Full text of license available in license.txt file, in main folder | ||||
|  * | ||||
|  */ | ||||
|  | ||||
| #pragma once | ||||
|  | ||||
| #include "Effect.h" | ||||
| #include "../../GameConstants.h" | ||||
|  | ||||
| VCMI_LIB_NAMESPACE_BEGIN | ||||
|  | ||||
| namespace spells | ||||
| { | ||||
| namespace effects | ||||
| { | ||||
|  | ||||
| class Summon : public Effect | ||||
| { | ||||
| public: | ||||
| 	void adjustAffectedHexes(BattleHexArray & hexes, const Mechanics * m, const Target & spellTarget) const override; | ||||
| 	void adjustTargetTypes(std::vector<TargetType> & types) const override; | ||||
|  | ||||
| 	bool applicable(Problem & problem, const Mechanics * m) const override; | ||||
|  | ||||
| 	void apply(ServerCallback * server, const Mechanics * m, const EffectTarget & target) const override; | ||||
|  | ||||
| 	EffectTarget filterTarget(const Mechanics * m, const EffectTarget & target) const override; | ||||
|  | ||||
| 	EffectTarget transformTarget(const Mechanics * m, const Target & aimPoint, const Target & spellTarget) const override; | ||||
|  | ||||
| protected: | ||||
| 	void serializeJsonEffect(JsonSerializeFormat & handler) override final; | ||||
|  | ||||
| private: | ||||
| 	int32_t summonedCreatureAmount(const Mechanics * m) const; | ||||
| 	int32_t summonedCreatureHealth(const Mechanics * m, const battle::Unit * unit) const; | ||||
|  | ||||
| 	CreatureID creature; | ||||
|  | ||||
| 	bool permanent = false; | ||||
| 	bool exclusive = true; | ||||
| 	bool summonByHealth = false; | ||||
| 	bool summonSameUnit = false; | ||||
| }; | ||||
|  | ||||
| } | ||||
| } | ||||
|  | ||||
| VCMI_LIB_NAMESPACE_END | ||||
| /* | ||||
|  * Summon.h, part of VCMI engine | ||||
|  * | ||||
|  * Authors: listed in file AUTHORS in main folder | ||||
|  * | ||||
|  * License: GNU General Public License v2.0 or later | ||||
|  * Full text of license available in license.txt file, in main folder | ||||
|  * | ||||
|  */ | ||||
|  | ||||
| #pragma once | ||||
|  | ||||
| #include "Effect.h" | ||||
| #include "../../GameConstants.h" | ||||
|  | ||||
| VCMI_LIB_NAMESPACE_BEGIN | ||||
|  | ||||
| namespace spells | ||||
| { | ||||
| namespace effects | ||||
| { | ||||
|  | ||||
| class Summon : public Effect | ||||
| { | ||||
| public: | ||||
| 	void adjustAffectedHexes(BattleHexArray & hexes, const Mechanics * m, const Target & spellTarget) const override; | ||||
| 	void adjustTargetTypes(std::vector<TargetType> & types) const override; | ||||
|  | ||||
| 	bool applicable(Problem & problem, const Mechanics * m) const override; | ||||
|  | ||||
| 	void apply(ServerCallback * server, const Mechanics * m, const EffectTarget & target) const override; | ||||
|  | ||||
| 	EffectTarget filterTarget(const Mechanics * m, const EffectTarget & target) const override; | ||||
|  | ||||
| 	EffectTarget transformTarget(const Mechanics * m, const Target & aimPoint, const Target & spellTarget) const override; | ||||
|  | ||||
| protected: | ||||
| 	void serializeJsonEffect(JsonSerializeFormat & handler) override final; | ||||
|  | ||||
| private: | ||||
| 	int32_t summonedCreatureAmount(const Mechanics * m) const; | ||||
| 	int32_t summonedCreatureHealth(const Mechanics * m, const battle::Unit * unit) const; | ||||
|  | ||||
| 	CreatureID creature; | ||||
|  | ||||
| 	bool permanent = false; | ||||
| 	bool exclusive = true; | ||||
| 	bool summonByHealth = false; | ||||
| 	bool summonSameUnit = false; | ||||
| }; | ||||
|  | ||||
| } | ||||
| } | ||||
|  | ||||
| VCMI_LIB_NAMESPACE_END | ||||
|   | ||||
| @@ -1,61 +1,61 @@ | ||||
| /* | ||||
|  * UnitEffect.h, part of VCMI engine | ||||
|  * | ||||
|  * Authors: listed in file AUTHORS in main folder | ||||
|  * | ||||
|  * License: GNU General Public License v2.0 or later | ||||
|  * Full text of license available in license.txt file, in main folder | ||||
|  * | ||||
|  */ | ||||
|  | ||||
| #pragma once | ||||
|  | ||||
| #include "Effect.h" | ||||
|  | ||||
| VCMI_LIB_NAMESPACE_BEGIN | ||||
|  | ||||
| namespace spells | ||||
| { | ||||
| namespace effects | ||||
| { | ||||
|  | ||||
| class UnitEffect : public Effect | ||||
| { | ||||
| public: | ||||
| 	void adjustTargetTypes(std::vector<TargetType> & types) const override; | ||||
|  | ||||
| 	void adjustAffectedHexes(BattleHexArray & hexes, const Mechanics * m, const Target & spellTarget) const override; | ||||
|  | ||||
| 	bool applicable(Problem & problem, const Mechanics * m) const override; | ||||
| 	bool applicable(Problem & problem, const Mechanics * m, const EffectTarget & target) const override; | ||||
|  | ||||
| 	EffectTarget filterTarget(const Mechanics * m, const EffectTarget & target) const override; | ||||
|  | ||||
| 	EffectTarget transformTarget(const Mechanics * m, const Target & aimPoint, const Target & spellTarget) const override; | ||||
|  | ||||
|     bool getStackFilter(const Mechanics * m, bool alwaysSmart, const battle::Unit * s) const; | ||||
|  | ||||
|     virtual bool eraseByImmunityFilter(const Mechanics * m, const battle::Unit * s) const; | ||||
|  | ||||
| protected: | ||||
| 	int32_t chainLength = 0; | ||||
| 	double chainFactor = 0.0; | ||||
|  | ||||
| 	virtual bool isReceptive(const Mechanics * m, const battle::Unit * unit) const; | ||||
| 	virtual bool isSmartTarget(const Mechanics * m, const battle::Unit * unit, bool alwaysSmart) const; | ||||
| 	virtual bool isValidTarget(const Mechanics * m, const battle::Unit * unit) const; | ||||
|  | ||||
| 	void serializeJsonEffect(JsonSerializeFormat & handler) override final; | ||||
| 	virtual void serializeJsonUnitEffect(JsonSerializeFormat & handler) = 0; | ||||
|  | ||||
| private: | ||||
| 	bool ignoreImmunity = false; | ||||
|  | ||||
| 	EffectTarget transformTargetByRange(const Mechanics * m, const Target & aimPoint, const Target & spellTarget) const; | ||||
| 	EffectTarget transformTargetByChain(const Mechanics * m, const Target & aimPoint, const Target & spellTarget) const; | ||||
| }; | ||||
|  | ||||
| } | ||||
| } | ||||
|  | ||||
| VCMI_LIB_NAMESPACE_END | ||||
| /* | ||||
|  * UnitEffect.h, part of VCMI engine | ||||
|  * | ||||
|  * Authors: listed in file AUTHORS in main folder | ||||
|  * | ||||
|  * License: GNU General Public License v2.0 or later | ||||
|  * Full text of license available in license.txt file, in main folder | ||||
|  * | ||||
|  */ | ||||
|  | ||||
| #pragma once | ||||
|  | ||||
| #include "Effect.h" | ||||
|  | ||||
| VCMI_LIB_NAMESPACE_BEGIN | ||||
|  | ||||
| namespace spells | ||||
| { | ||||
| namespace effects | ||||
| { | ||||
|  | ||||
| class UnitEffect : public Effect | ||||
| { | ||||
| public: | ||||
| 	void adjustTargetTypes(std::vector<TargetType> & types) const override; | ||||
|  | ||||
| 	void adjustAffectedHexes(BattleHexArray & hexes, const Mechanics * m, const Target & spellTarget) const override; | ||||
|  | ||||
| 	bool applicable(Problem & problem, const Mechanics * m) const override; | ||||
| 	bool applicable(Problem & problem, const Mechanics * m, const EffectTarget & target) const override; | ||||
|  | ||||
| 	EffectTarget filterTarget(const Mechanics * m, const EffectTarget & target) const override; | ||||
|  | ||||
| 	EffectTarget transformTarget(const Mechanics * m, const Target & aimPoint, const Target & spellTarget) const override; | ||||
|  | ||||
|     bool getStackFilter(const Mechanics * m, bool alwaysSmart, const battle::Unit * s) const; | ||||
|  | ||||
|     virtual bool eraseByImmunityFilter(const Mechanics * m, const battle::Unit * s) const; | ||||
|  | ||||
| protected: | ||||
| 	int32_t chainLength = 0; | ||||
| 	double chainFactor = 0.0; | ||||
|  | ||||
| 	virtual bool isReceptive(const Mechanics * m, const battle::Unit * unit) const; | ||||
| 	virtual bool isSmartTarget(const Mechanics * m, const battle::Unit * unit, bool alwaysSmart) const; | ||||
| 	virtual bool isValidTarget(const Mechanics * m, const battle::Unit * unit) const; | ||||
|  | ||||
| 	void serializeJsonEffect(JsonSerializeFormat & handler) override final; | ||||
| 	virtual void serializeJsonUnitEffect(JsonSerializeFormat & handler) = 0; | ||||
|  | ||||
| private: | ||||
| 	bool ignoreImmunity = false; | ||||
|  | ||||
| 	EffectTarget transformTargetByRange(const Mechanics * m, const Target & aimPoint, const Target & spellTarget) const; | ||||
| 	EffectTarget transformTargetByChain(const Mechanics * m, const Target & aimPoint, const Target & spellTarget) const; | ||||
| }; | ||||
|  | ||||
| } | ||||
| } | ||||
|  | ||||
| VCMI_LIB_NAMESPACE_END | ||||
|   | ||||
| @@ -1,62 +1,62 @@ | ||||
| /* | ||||
|  * BattleFlowProcessor.h, part of VCMI engine | ||||
|  * | ||||
|  * Authors: listed in file AUTHORS in main folder | ||||
|  * | ||||
|  * License: GNU General Public License v2.0 or later | ||||
|  * Full text of license available in license.txt file, in main folder | ||||
|  * | ||||
|  */ | ||||
| #pragma once | ||||
|  | ||||
| #include "../lib/battle/BattleSide.h" | ||||
|  | ||||
| VCMI_LIB_NAMESPACE_BEGIN | ||||
| class CStack; | ||||
| struct BattleHex; | ||||
| class BattleHexArray; | ||||
| class BattleAction; | ||||
| class CBattleInfoCallback; | ||||
| struct CObstacleInstance; | ||||
| namespace battle | ||||
| { | ||||
| class Unit; | ||||
| } | ||||
| VCMI_LIB_NAMESPACE_END | ||||
|  | ||||
| class CGameHandler; | ||||
| class BattleProcessor; | ||||
|  | ||||
| /// Controls flow of battles - battle startup actions and switching to next stack or next round after actions | ||||
| class BattleFlowProcessor : boost::noncopyable | ||||
| { | ||||
| 	BattleProcessor * owner; | ||||
| 	CGameHandler * gameHandler; | ||||
|  | ||||
| 	const CStack * getNextStack(const CBattleInfoCallback & battle); | ||||
|  | ||||
| 	bool rollGoodMorale(const CBattleInfoCallback & battle, const CStack * stack); | ||||
| 	bool tryMakeAutomaticAction(const CBattleInfoCallback & battle, const CStack * stack); | ||||
|  | ||||
| 	void summonGuardiansHelper(const CBattleInfoCallback & battle, BattleHexArray & output, const BattleHex & targetPosition, BattleSide side, bool targetIsTwoHex); | ||||
| 	void trySummonGuardians(const CBattleInfoCallback & battle, const CStack * stack); | ||||
| 	void tryPlaceMoats(const CBattleInfoCallback & battle); | ||||
| 	void castOpeningSpells(const CBattleInfoCallback & battle); | ||||
| 	void activateNextStack(const CBattleInfoCallback & battle); | ||||
| 	void startNextRound(const CBattleInfoCallback & battle, bool isFirstRound); | ||||
|  | ||||
| 	void stackEnchantedTrigger(const CBattleInfoCallback & battle, const CStack * stack); | ||||
| 	void removeObstacle(const CBattleInfoCallback & battle, const CObstacleInstance & obstacle); | ||||
| 	void stackTurnTrigger(const CBattleInfoCallback & battle, const CStack * stack); | ||||
| 	void setActiveStack(const CBattleInfoCallback & battle, const battle::Unit * stack); | ||||
|  | ||||
| 	void makeStackDoNothing(const CBattleInfoCallback & battle, const CStack * next); | ||||
| 	bool makeAutomaticAction(const CBattleInfoCallback & battle, const CStack * stack, BattleAction & ba); //used when action is taken by stack without volition of player (eg. unguided catapult attack) | ||||
|  | ||||
| public: | ||||
| 	explicit BattleFlowProcessor(BattleProcessor * owner, CGameHandler * newGameHandler); | ||||
|  | ||||
| 	void onBattleStarted(const CBattleInfoCallback & battle); | ||||
| 	void onTacticsEnded(const CBattleInfoCallback & battle); | ||||
| 	void onActionMade(const CBattleInfoCallback & battle, const BattleAction & ba); | ||||
| }; | ||||
| /* | ||||
|  * BattleFlowProcessor.h, part of VCMI engine | ||||
|  * | ||||
|  * Authors: listed in file AUTHORS in main folder | ||||
|  * | ||||
|  * License: GNU General Public License v2.0 or later | ||||
|  * Full text of license available in license.txt file, in main folder | ||||
|  * | ||||
|  */ | ||||
| #pragma once | ||||
|  | ||||
| #include "../lib/battle/BattleSide.h" | ||||
|  | ||||
| VCMI_LIB_NAMESPACE_BEGIN | ||||
| class CStack; | ||||
| struct BattleHex; | ||||
| class BattleHexArray; | ||||
| class BattleAction; | ||||
| class CBattleInfoCallback; | ||||
| struct CObstacleInstance; | ||||
| namespace battle | ||||
| { | ||||
| class Unit; | ||||
| } | ||||
| VCMI_LIB_NAMESPACE_END | ||||
|  | ||||
| class CGameHandler; | ||||
| class BattleProcessor; | ||||
|  | ||||
| /// Controls flow of battles - battle startup actions and switching to next stack or next round after actions | ||||
| class BattleFlowProcessor : boost::noncopyable | ||||
| { | ||||
| 	BattleProcessor * owner; | ||||
| 	CGameHandler * gameHandler; | ||||
|  | ||||
| 	const CStack * getNextStack(const CBattleInfoCallback & battle); | ||||
|  | ||||
| 	bool rollGoodMorale(const CBattleInfoCallback & battle, const CStack * stack); | ||||
| 	bool tryMakeAutomaticAction(const CBattleInfoCallback & battle, const CStack * stack); | ||||
|  | ||||
| 	void summonGuardiansHelper(const CBattleInfoCallback & battle, BattleHexArray & output, const BattleHex & targetPosition, BattleSide side, bool targetIsTwoHex); | ||||
| 	void trySummonGuardians(const CBattleInfoCallback & battle, const CStack * stack); | ||||
| 	void tryPlaceMoats(const CBattleInfoCallback & battle); | ||||
| 	void castOpeningSpells(const CBattleInfoCallback & battle); | ||||
| 	void activateNextStack(const CBattleInfoCallback & battle); | ||||
| 	void startNextRound(const CBattleInfoCallback & battle, bool isFirstRound); | ||||
|  | ||||
| 	void stackEnchantedTrigger(const CBattleInfoCallback & battle, const CStack * stack); | ||||
| 	void removeObstacle(const CBattleInfoCallback & battle, const CObstacleInstance & obstacle); | ||||
| 	void stackTurnTrigger(const CBattleInfoCallback & battle, const CStack * stack); | ||||
| 	void setActiveStack(const CBattleInfoCallback & battle, const battle::Unit * stack); | ||||
|  | ||||
| 	void makeStackDoNothing(const CBattleInfoCallback & battle, const CStack * next); | ||||
| 	bool makeAutomaticAction(const CBattleInfoCallback & battle, const CStack * stack, BattleAction & ba); //used when action is taken by stack without volition of player (eg. unguided catapult attack) | ||||
|  | ||||
| public: | ||||
| 	explicit BattleFlowProcessor(BattleProcessor * owner, CGameHandler * newGameHandler); | ||||
|  | ||||
| 	void onBattleStarted(const CBattleInfoCallback & battle); | ||||
| 	void onTacticsEnded(const CBattleInfoCallback & battle); | ||||
| 	void onActionMade(const CBattleInfoCallback & battle, const BattleAction & ba); | ||||
| }; | ||||
|   | ||||
		Reference in New Issue
	
	Block a user