1
0
mirror of https://github.com/vcmi/vcmi.git synced 2025-08-10 22:31:40 +02:00

BattleHexArray - new container for BattleHexes

This commit is contained in:
MichalZr6
2024-09-28 20:50:26 +02:00
parent f792eb2da5
commit a99274d72e
85 changed files with 5357 additions and 5036 deletions

View File

@@ -326,9 +326,9 @@ AttackPossibility AttackPossibility::evaluate(
AttackPossibility bestAp(hex, BattleHex::INVALID, attackInfo);
std::vector<BattleHex> defenderHex;
BattleHexArray defenderHex;
if(attackInfo.shooting)
defenderHex.push_back(defender->getPosition());
defenderHex.insert(defender->getPosition());
else
defenderHex = CStack::meleeAttackHexes(attacker, defender, hex);

View File

@@ -355,7 +355,7 @@ BattleAction BattleEvaluator::moveOrAttack(const CStack * stack, BattleHex hex,
}
}
BattleAction BattleEvaluator::goTowardsNearest(const CStack * stack, std::vector<BattleHex> hexes, const PotentialTargets & targets)
BattleAction BattleEvaluator::goTowardsNearest(const CStack * stack, BattleHexArray hexes, const PotentialTargets & targets)
{
auto reachability = cb->getBattle(battleID)->getReachability(stack);
auto avHexes = cb->getBattle(battleID)->battleGetAvailableHexes(reachability, stack, false);
@@ -381,7 +381,7 @@ BattleAction BattleEvaluator::goTowardsNearest(const CStack * stack, std::vector
return BattleAction::makeDefend(stack);
}
std::vector<BattleHex> targetHexes = hexes;
BattleHexArray targetHexes = hexes;
vstd::erase_if(targetHexes, [](const BattleHex & hex) { return !hex.isValid(); });
@@ -419,16 +419,16 @@ BattleAction BattleEvaluator::goTowardsNearest(const CStack * stack, std::vector
if(stack->hasBonusOfType(BonusType::FLYING))
{
std::set<BattleHex> obstacleHexes;
BattleHexArray obstacleHexes;
auto insertAffected = [](const CObstacleInstance & spellObst, std::set<BattleHex> & obstacleHexes) {
auto insertAffected = [](const CObstacleInstance & spellObst, BattleHexArray & obstacleHexes) {
auto affectedHexes = spellObst.getAffectedTiles();
obstacleHexes.insert(affectedHexes.cbegin(), affectedHexes.cend());
obstacleHexes.merge(affectedHexes);
};
const auto & obstacles = hb->battleGetAllObstacles();
for (const auto & obst: obstacles) {
for (const auto & obst : obstacles) {
if(obst->triggersEffects())
{

View File

@@ -51,7 +51,7 @@ public:
bool attemptCastingSpell(const CStack * stack);
bool canCastSpell();
std::optional<PossibleSpellcast> findBestCreatureSpell(const CStack * stack);
BattleAction goTowardsNearest(const CStack * stack, std::vector<BattleHex> hexes, const PotentialTargets & targets);
BattleAction goTowardsNearest(const CStack * stack, BattleHexArray hexes, const PotentialTargets & targets);
std::vector<BattleHex> getBrokenWallMoatHexes() const;
bool hasWorkingTowers() const;
void evaluateCreatureSpellcast(const CStack * stack, PossibleSpellcast & ps); //for offensive damaging spells only

View File

@@ -458,7 +458,7 @@ MoveTarget BattleExchangeEvaluator::findMoveTowardsUnreachable(
}
}
result.positions.push_back(enemyHex);
result.positions.insert(enemyHex);
}
result.cachedAttack = attack;
@@ -487,12 +487,12 @@ std::vector<const battle::Unit *> BattleExchangeEvaluator::getAdjacentUnits(cons
auto hexes = stack->getSurroundingHexes();
for(auto hex : hexes)
{
auto neighbor = cb->battleGetUnitByPos(hex);
auto neighbour = cb->battleGetUnitByPos(hex);
if(neighbor && neighbor->unitSide() == stack->unitSide() && !vstd::contains(checkedStacks, neighbor))
if(neighbour && neighbour->unitSide() == stack->unitSide() && !vstd::contains(checkedStacks, neighbour))
{
queue.push(neighbor);
checkedStacks.push_back(neighbor);
queue.push(neighbour);
checkedStacks.push_back(neighbour);
}
}
}
@@ -511,7 +511,7 @@ ReachabilityData BattleExchangeEvaluator::getExchangeUnits(
auto hexes = ap.attack.defender->getSurroundingHexes();
if(!ap.attack.shooting) hexes.push_back(ap.from);
if(!ap.attack.shooting) hexes.insert(ap.from);
std::vector<const battle::Unit *> allReachableUnits = additionalUnits;
@@ -959,9 +959,11 @@ std::vector<const battle::Unit *> BattleExchangeEvaluator::getOneTurnReachableUn
if(hexStack && cb->battleMatchOwner(unit, hexStack, false))
{
for(BattleHex neighbor : hex.neighbouringTiles())
BattleHexArray neighbours;
neighbours.generateNeighbouringTiles(hex);
for(BattleHex neighbour : neighbours)
{
reachable = unitReachability.distances.at(neighbor) <= radius;
reachable = unitReachability.distances.at(neighbour) <= radius;
if(reachable) break;
}
@@ -1020,10 +1022,9 @@ bool BattleExchangeEvaluator::checkPositionBlocksOurStacks(HypotheticBattle & hb
if(hexStack && cb->battleMatchOwner(unit, hexStack, false))
{
enemyUnit = true;
for(BattleHex neighbor : hex.neighbouringTiles())
for(BattleHex neighbour : BattleHexArray::generateNeighbouringTiles(hex))
{
reachable = unitReachability.distances.at(neighbor) <= unitSpeed;
reachable = unitReachability.distances.at(neighbour) <= unitSpeed;
if(reachable) break;
}

View File

@@ -54,7 +54,7 @@ struct AttackerValue
struct MoveTarget
{
float score;
std::vector<BattleHex> positions;
BattleHexArray positions;
std::optional<AttackPossibility> cachedAttack;
uint8_t turnsToRich;

View File

@@ -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 hasReachableNeighbor(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 hasReachableneighbour(const int3 & pos) const;
};
}

View File

@@ -399,7 +399,7 @@ void AINodeStorage::calculateNeighbours(
#if NKAI_PATHFINDER_TRACE_LEVEL >= 2
logAi->trace(
"Node %s added to neighbors of %s, layer %d",
"Node %s added to neighbours of %s, layer %d",
neighbour.toString(),
source.coord.toString(),
static_cast<int32_t>(layer));

View File

@@ -56,9 +56,9 @@ void ObjectGraphCalculator::calculateConnections()
removeExtraConnections();
}
float ObjectGraphCalculator::getNeighborConnectionsCost(const int3 & pos, std::vector<AIPath> & pathCache)
float ObjectGraphCalculator::getneighbourConnectionsCost(const int3 & pos, std::vector<AIPath> & pathCache)
{
float neighborCost = std::numeric_limits<float>::max();
float neighbourCost = std::numeric_limits<float>::max();
if(NKAI_GRAPH_TRACE_LEVEL >= 2)
{
@@ -68,24 +68,24 @@ float ObjectGraphCalculator::getNeighborConnectionsCost(const int3 & pos, std::v
foreach_neighbour(
ai->cb.get(),
pos,
[this, &neighborCost, &pathCache](const CPlayerSpecificInfoCallback * cb, const int3 & neighbor)
[this, &neighbourCost, &pathCache](const CPlayerSpecificInfoCallback * cb, const int3 & neighbour)
{
ai->pathfinder->calculatePathInfo(pathCache, neighbor);
ai->pathfinder->calculatePathInfo(pathCache, neighbour);
auto costTotal = this->getConnectionsCost(pathCache);
if(costTotal.connectionsCount > 2 && costTotal.avg < neighborCost)
if(costTotal.connectionsCount > 2 && costTotal.avg < neighbourCost)
{
neighborCost = costTotal.avg;
neighbourCost = costTotal.avg;
if(NKAI_GRAPH_TRACE_LEVEL >= 2)
{
logAi->trace("Better node found at %s", neighbor.toString());
logAi->trace("Better node found at %s", neighbour.toString());
}
}
});
return neighborCost;
return neighbourCost;
}
void ObjectGraphCalculator::addMinimalDistanceJunctions()
@@ -105,9 +105,9 @@ void ObjectGraphCalculator::addMinimalDistanceJunctions()
if(currentCost.connectionsCount <= 2)
return;
float neighborCost = getNeighborConnectionsCost(pos, paths);
float neighbourCost = getneighbourConnectionsCost(pos, paths);
if(currentCost.avg < neighborCost)
if(currentCost.avg < neighbourCost)
{
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 & neighbor)
[this, &pos, &pathCache](const CPlayerSpecificInfoCallback * cb, const int3 & neighbour)
{
if(target->hasNodeAt(neighbor))
if(target->hasNodeAt(neighbour))
{
ai->pathfinder->calculatePathInfo(pathCache, neighbor);
ai->pathfinder->calculatePathInfo(pathCache, neighbour);
for(auto & path : pathCache)
{
if(pos == path.targetHero->visitablePos())
{
target->tryAddConnection(pos, neighbor, path.movementCost(), path.getTotalDanger());
target->tryAddConnection(pos, neighbour, path.movementCost(), path.getTotalDanger());
}
}
}

View File

@@ -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 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;
};
}
/*
* 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;
};
}

View File

@@ -69,7 +69,7 @@ public:
const CStack * s;
int adi;
int adr;
std::vector<BattleHex> attackFrom; //for melee fight
BattleHexArray attackFrom; //for melee fight
EnemyInfo(const CStack * _s) : s(_s), adi(0), adr(0)
{}
void calcDmg(std::shared_ptr<CBattleCallback> cb, const BattleID & battleID, const CStack * ourStack)
@@ -107,7 +107,7 @@ static bool willSecondHexBlockMoreEnemyShooters(std::shared_ptr<CBattleCallback>
for(int i = 0; i < 2; i++)
{
for (auto & neighbour : (i ? h2 : h1).neighbouringTiles())
for (auto neighbour : BattleHexArray::generateNeighbouringTiles(i ? h2 : h1))
if(const auto * s = cb->getBattle(battleID)->battleGetUnitByPos(neighbour))
if(s->isShooter())
shooters[i]++;
@@ -157,7 +157,7 @@ void CStupidAI::activeStack(const BattleID & battleID, const CStack * stack)
}
else
{
std::vector<BattleHex> avHexes = cb->getBattle(battleID)->battleGetAvailableHexes(stack, false);
BattleHexArray avHexes = cb->getBattle(battleID)->battleGetAvailableHexes(stack, false);
for (BattleHex hex : avHexes)
{
@@ -170,7 +170,7 @@ void CStupidAI::activeStack(const BattleID & battleID, const CStack * stack)
i = enemiesReachable.begin() + (enemiesReachable.size() - 1);
}
i->attackFrom.push_back(hex);
i->attackFrom.insert(hex);
}
}
@@ -247,7 +247,7 @@ void CStupidAI::battleNewRound(const BattleID & battleID)
print("battleNewRound called");
}
void CStupidAI::battleStackMoved(const BattleID & battleID, const CStack * stack, std::vector<BattleHex> dest, int distance, bool teleport)
void CStupidAI::battleStackMoved(const BattleID & battleID, const CStack * stack, BattleHexArray dest, int distance, bool teleport)
{
print("battleStackMoved called");
}
@@ -278,7 +278,7 @@ void CStupidAI::print(const std::string &text) const
logAi->trace("CStupidAI [%p]: %s", this, text);
}
BattleAction CStupidAI::goTowards(const BattleID & battleID, const CStack * stack, std::vector<BattleHex> hexes) const
BattleAction CStupidAI::goTowards(const BattleID & battleID, const CStack * stack, BattleHexArray hexes) const
{
auto reachability = cb->getBattle(battleID)->getReachability(stack);
auto avHexes = cb->getBattle(battleID)->battleGetAvailableHexes(reachability, stack, false);
@@ -295,7 +295,7 @@ BattleAction CStupidAI::goTowards(const BattleID & battleID, const CStack * stac
for(auto hex : hexes)
{
if(vstd::contains(avHexes, hex))
if(avHexes.contains(hex))
{
if(stack->position == hex)
return BattleAction::makeDefend(stack);
@@ -310,9 +310,9 @@ BattleAction CStupidAI::goTowards(const BattleID & battleID, const CStack * stac
}
}
BattleHex bestNeighbor = hexes.front();
BattleHex bestneighbour = hexes.front();
if(reachability.distances[bestNeighbor] > GameConstants::BFIELD_SIZE)
if(reachability.distances[bestneighbour] > GameConstants::BFIELD_SIZE)
{
return BattleAction::makeDefend(stack);
}
@@ -323,14 +323,14 @@ BattleAction CStupidAI::goTowards(const BattleID & battleID, const CStack * stac
// We just check all available hexes and pick the one closest to the target.
auto nearestAvailableHex = vstd::minElementByFun(avHexes, [&](BattleHex hex) -> int
{
return BattleHex::getDistance(bestNeighbor, hex);
return BattleHex::getDistance(bestneighbour, hex);
});
return BattleAction::makeMove(stack, *nearestAvailableHex);
}
else
{
BattleHex currentDest = bestNeighbor;
BattleHex currentDest = bestneighbour;
while(1)
{
if(!currentDest.isValid())
@@ -339,7 +339,7 @@ BattleAction CStupidAI::goTowards(const BattleID & battleID, const CStack * stac
return BattleAction::makeDefend(stack);
}
if(vstd::contains(avHexes, currentDest))
if(avHexes.contains(currentDest))
{
if(stack->position == currentDest)
return BattleAction::makeDefend(stack);

View File

@@ -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, std::vector<BattleHex> 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, std::vector<BattleHex> 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;
};

View File

@@ -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 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);
}
/*
* 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);
}

View File

@@ -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 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;
};
}
/*
* 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;
};
}

View File

@@ -846,7 +846,7 @@ void CPlayerInterface::battleLogMessage(const BattleID & battleID, const std::ve
battleInt->displayBattleLog(lines);
}
void CPlayerInterface::battleStackMoved(const BattleID & battleID, const CStack * stack, std::vector<BattleHex> dest, int distance, bool teleport)
void CPlayerInterface::battleStackMoved(const BattleID & battleID, const CStack * stack, BattleHexArray dest, int distance, bool teleport)
{
EVENT_HANDLER_CALLED_BY_CLIENT;
BATTLE_EVENT_POSSIBLE_RETURN;

View File

@@ -154,7 +154,7 @@ protected: // Call-ins from server, should not be called directly, but only via
void battleNewRoundFirst(const BattleID & battleID) override; //called at the beginning of each turn before changes are applied; used for HP regen handling
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 battleLogMessage(const BattleID & battleID, const std::vector<MetaString> & lines) override;
void battleStackMoved(const BattleID & battleID, const CStack * stack, std::vector<BattleHex> dest, int distance, bool teleport) override;
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 BattleID & battleID, const BattleTriggerEffect & bte) override; //various one-shot effect

View File

@@ -723,11 +723,11 @@ void BattleActionsController::actionRealize(PossiblePlayerBattleAction action, B
{
if(owner.stacksController->getActiveStack()->doubleWide())
{
std::vector<BattleHex> acc = owner.getBattle()->battleGetAvailableHexes(owner.stacksController->getActiveStack(), false);
BattleHexArray acc = owner.getBattle()->battleGetAvailableHexes(owner.stacksController->getActiveStack(), false);
BattleHex shiftedDest = targetHex.cloneInDirection(owner.stacksController->getActiveStack()->destShiftDir(), false);
if(vstd::contains(acc, targetHex))
if(acc.contains(targetHex))
owner.giveCommand(EActionType::WALK, targetHex);
else if(vstd::contains(acc, shiftedDest))
else if(acc.contains(shiftedDest))
owner.giveCommand(EActionType::WALK, shiftedDest);
}
else
@@ -1008,12 +1008,12 @@ bool BattleActionsController::isCastingPossibleHere(const CSpell * currentSpell,
bool BattleActionsController::canStackMoveHere(const CStack * stackToMove, BattleHex myNumber) const
{
std::vector<BattleHex> acc = owner.getBattle()->battleGetAvailableHexes(stackToMove, false);
BattleHexArray acc = owner.getBattle()->battleGetAvailableHexes(stackToMove, false);
BattleHex shiftedDest = myNumber.cloneInDirection(stackToMove->destShiftDir(), false);
if (vstd::contains(acc, myNumber))
if (acc.contains(myNumber))
return true;
else if (stackToMove->doubleWide() && vstd::contains(acc, shiftedDest))
else if (stackToMove->doubleWide() && acc.contains(shiftedDest))
return true;
else
return false;
@@ -1126,4 +1126,4 @@ void BattleActionsController::pushFrontPossibleAction(PossiblePlayerBattleAction
void BattleActionsController::resetCurrentStackPossibleActions()
{
possibleActions = getPossibleActionsForStack(owner.stacksController->getActiveStack());
}
}

View File

@@ -422,7 +422,7 @@ MovementAnimation::~MovementAnimation()
CCS->soundh->stopSound(moveSoundHandler);
}
MovementAnimation::MovementAnimation(BattleInterface & owner, const CStack *stack, std::vector<BattleHex> _destTiles, int _distance)
MovementAnimation::MovementAnimation(BattleInterface & owner, const CStack *stack, BattleHexArray _destTiles, int _distance)
: StackMoveAnimation(owner, stack, stack->getPosition(), _destTiles.front()),
destTiles(_destTiles),
currentMoveIndex(0),
@@ -892,7 +892,7 @@ EffectAnimation::EffectAnimation(BattleInterface & owner, const AnimationPath &
logAnim->debug("CPointEffectAnimation::init: effect %s", animationName.getName());
}
EffectAnimation::EffectAnimation(BattleInterface & owner, const AnimationPath & animationName, std::vector<BattleHex> hex, int effects, bool reversed):
EffectAnimation::EffectAnimation(BattleInterface & owner, const AnimationPath & animationName, BattleHexArray hex, int effects, bool reversed):
EffectAnimation(owner, animationName, effects, 1.0f, reversed)
{
battlehexes = hex;
@@ -902,7 +902,7 @@ EffectAnimation::EffectAnimation(BattleInterface & owner, const AnimationPath &
EffectAnimation(owner, animationName, effects, transparencyFactor, reversed)
{
assert(hex.isValid());
battlehexes.push_back(hex);
battlehexes.insert(hex);
}
EffectAnimation::EffectAnimation(BattleInterface & owner, const AnimationPath & animationName, std::vector<Point> pos, int effects, bool reversed):
@@ -921,7 +921,7 @@ EffectAnimation::EffectAnimation(BattleInterface & owner, const AnimationPath &
EffectAnimation(owner, animationName, effects, 1.0f, reversed)
{
assert(hex.isValid());
battlehexes.push_back(hex);
battlehexes.insert(hex);
positions.push_back(pos);
}

View File

@@ -9,7 +9,7 @@
*/
#pragma once
#include "../../lib/battle/BattleHex.h"
#include "../../lib/battle/BattleHexArray.h"
#include "../../lib/filesystem/ResourcePath.h"
#include "BattleConstants.h"
@@ -143,7 +143,7 @@ class MovementAnimation : public StackMoveAnimation
private:
int moveSoundHandler; // sound handler used when moving a unit
std::vector<BattleHex> destTiles; //full path, includes already passed hexes
BattleHexArray destTiles; //full path, includes already passed hexes
ui32 currentMoveIndex; // index of nextHex in destTiles
double begX, begY; // starting position
@@ -159,7 +159,7 @@ public:
bool init() override;
void tick(uint32_t msPassed) override;
MovementAnimation(BattleInterface & owner, const CStack *_stack, std::vector<BattleHex> _destTiles, int _distance);
MovementAnimation(BattleInterface & owner, const CStack *_stack, BattleHexArray _destTiles, int _distance);
~MovementAnimation();
};
@@ -316,7 +316,7 @@ class EffectAnimation : public BattleAnimation
std::shared_ptr<CAnimation> animation;
std::vector<Point> positions;
std::vector<BattleHex> battlehexes;
BattleHexArray battlehexes;
bool alignToBottom() const;
bool waitForSound() const;
@@ -344,7 +344,7 @@ public:
/// Create animation positioned at certain hex(es)
EffectAnimation(BattleInterface & owner, const AnimationPath & animationName, BattleHex hex , int effects = 0, float transparencyFactor = 1.0f, bool reversed = false);
EffectAnimation(BattleInterface & owner, const AnimationPath & animationName, std::vector<BattleHex> hex, int effects = 0, bool reversed = false);
EffectAnimation(BattleInterface & owner, const AnimationPath & animationName, BattleHexArray hex, int effects = 0, bool reversed = false);
EffectAnimation(BattleInterface & owner, const AnimationPath & animationName, Point pos, BattleHex hex, int effects = 0, bool reversed = false);
~EffectAnimation();

View File

@@ -267,7 +267,7 @@ void BattleFieldController::showBackgroundImageWithHexes(Canvas & canvas)
void BattleFieldController::redrawBackgroundWithHexes()
{
const CStack *activeStack = owner.stacksController->getActiveStack();
std::vector<BattleHex> attackableHexes;
BattleHexArray attackableHexes;
if(activeStack)
occupiableHexes = owner.getBattle()->battleGetAvailableHexes(activeStack, false, true, &attackableHexes);
@@ -280,8 +280,8 @@ void BattleFieldController::redrawBackgroundWithHexes()
// show shaded hexes for active's stack valid movement and the hexes that it can attack
if(settings["battle"]["stackRange"].Bool())
{
std::vector<BattleHex> hexesToShade = occupiableHexes;
hexesToShade.insert(hexesToShade.end(), attackableHexes.begin(), attackableHexes.end());
BattleHexArray hexesToShade = occupiableHexes;
hexesToShade.merge(attackableHexes);
for(BattleHex hex : hexesToShade)
{
showHighlightedHex(*backgroundWithHexes, cellShade, hex, false);
@@ -312,9 +312,9 @@ void BattleFieldController::showHighlightedHex(Canvas & canvas, std::shared_ptr<
canvas.draw(cellBorder, hexPos);
}
std::set<BattleHex> BattleFieldController::getHighlightedHexesForActiveStack()
BattleHexArray BattleFieldController::getHighlightedHexesForActiveStack()
{
std::set<BattleHex> result;
BattleHexArray result;
if(!owner.stacksController->getActiveStack())
return result;
@@ -324,16 +324,16 @@ std::set<BattleHex> BattleFieldController::getHighlightedHexesForActiveStack()
auto hoveredHex = getHoveredHex();
std::set<BattleHex> set = owner.getBattle()->battleGetAttackedHexes(owner.stacksController->getActiveStack(), hoveredHex);
BattleHexArray set = owner.getBattle()->battleGetAttackedHexes(owner.stacksController->getActiveStack(), hoveredHex);
for(BattleHex hex : set)
result.insert(hex);
return result;
}
std::set<BattleHex> BattleFieldController::getMovementRangeForHoveredStack()
BattleHexArray BattleFieldController::getMovementRangeForHoveredStack()
{
std::set<BattleHex> result;
BattleHexArray result;
if (!owner.stacksController->getActiveStack())
return result;
@@ -344,16 +344,16 @@ std::set<BattleHex> BattleFieldController::getMovementRangeForHoveredStack()
auto hoveredStack = getHoveredStack();
if(hoveredStack)
{
std::vector<BattleHex> v = owner.getBattle()->battleGetAvailableHexes(hoveredStack, true, true, nullptr);
BattleHexArray v = owner.getBattle()->battleGetAvailableHexes(hoveredStack, true, true, nullptr);
for(BattleHex hex : v)
result.insert(hex);
}
return result;
}
std::set<BattleHex> BattleFieldController::getHighlightedHexesForSpellRange()
BattleHexArray BattleFieldController::getHighlightedHexesForSpellRange()
{
std::set<BattleHex> result;
BattleHexArray result;
auto hoveredHex = getHoveredHex();
const spells::Caster *caster = nullptr;
@@ -378,7 +378,7 @@ std::set<BattleHex> BattleFieldController::getHighlightedHexesForSpellRange()
return result;
}
std::set<BattleHex> BattleFieldController::getHighlightedHexesForMovementTarget()
BattleHexArray BattleFieldController::getHighlightedHexesForMovementTarget()
{
const CStack * stack = owner.stacksController->getActiveStack();
auto hoveredHex = getHoveredHex();
@@ -386,7 +386,7 @@ std::set<BattleHex> BattleFieldController::getHighlightedHexesForMovementTarget(
if(!stack)
return {};
std::vector<BattleHex> availableHexes = owner.getBattle()->battleGetAvailableHexes(stack, false, false, nullptr);
BattleHexArray availableHexes = owner.getBattle()->battleGetAvailableHexes(stack, false, false, nullptr);
auto hoveredStack = owner.getBattle()->battleGetStackByPos(hoveredHex, true);
if(owner.getBattle()->battleCanAttack(stack, hoveredStack, hoveredHex))
@@ -402,7 +402,7 @@ std::set<BattleHex> BattleFieldController::getHighlightedHexesForMovementTarget(
}
}
if(vstd::contains(availableHexes, hoveredHex))
if(availableHexes.contains(hoveredHex))
{
if(stack->doubleWide())
return {hoveredHex, stack->occupiedHex(hoveredHex)};
@@ -412,7 +412,7 @@ std::set<BattleHex> BattleFieldController::getHighlightedHexesForMovementTarget(
if(stack->doubleWide())
{
for(auto const & hex : availableHexes)
for(auto hex : availableHexes)
{
if(stack->occupiedHex(hex) == hoveredHex)
return {hoveredHex, hex};
@@ -424,9 +424,9 @@ std::set<BattleHex> BattleFieldController::getHighlightedHexesForMovementTarget(
// Range limit highlight helpers
std::vector<BattleHex> BattleFieldController::getRangeHexes(BattleHex sourceHex, uint8_t distance)
BattleHexArray BattleFieldController::getRangeHexes(BattleHex sourceHex, uint8_t distance)
{
std::vector<BattleHex> rangeHexes;
BattleHexArray rangeHexes;
if (!settings["battle"]["rangeLimitHighlightOnHover"].Bool() && !GH.isKeyboardShiftDown())
return rangeHexes;
@@ -436,27 +436,27 @@ std::vector<BattleHex> BattleFieldController::getRangeHexes(BattleHex sourceHex,
{
BattleHex hex(i);
if(hex.isAvailable() && BattleHex::getDistance(sourceHex, hex) <= distance)
rangeHexes.push_back(hex);
rangeHexes.insert(hex);
}
return rangeHexes;
}
std::vector<BattleHex> BattleFieldController::getRangeLimitHexes(BattleHex hoveredHex, std::vector<BattleHex> rangeHexes, uint8_t distanceToLimit)
BattleHexArray BattleFieldController::getRangeLimitHexes(BattleHex hoveredHex, BattleHexArray rangeHexes, uint8_t distanceToLimit)
{
std::vector<BattleHex> rangeLimitHexes;
BattleHexArray rangeLimitHexes;
// from range hexes get only the ones at the limit
for(auto & hex : rangeHexes)
{
if(BattleHex::getDistance(hoveredHex, hex) == distanceToLimit)
rangeLimitHexes.push_back(hex);
rangeLimitHexes.insert(hex);
}
return rangeLimitHexes;
}
bool BattleFieldController::IsHexInRangeLimit(BattleHex hex, std::vector<BattleHex> & rangeLimitHexes, int * hexIndexInRangeLimit)
bool BattleFieldController::IsHexInRangeLimit(BattleHex hex, BattleHexArray & rangeLimitHexes, int * hexIndexInRangeLimit)
{
bool hexInRangeLimit = false;
@@ -470,18 +470,18 @@ bool BattleFieldController::IsHexInRangeLimit(BattleHex hex, std::vector<BattleH
return hexInRangeLimit;
}
std::vector<std::vector<BattleHex::EDir>> BattleFieldController::getOutsideNeighbourDirectionsForLimitHexes(std::vector<BattleHex> wholeRangeHexes, std::vector<BattleHex> rangeLimitHexes)
std::vector<std::vector<BattleHex::EDir>> BattleFieldController::getOutsideNeighbourDirectionsForLimitHexes(BattleHexArray wholeRangeHexes, BattleHexArray rangeLimitHexes)
{
std::vector<std::vector<BattleHex::EDir>> output;
if(wholeRangeHexes.empty())
return output;
for(auto & hex : rangeLimitHexes)
for(auto hex : rangeLimitHexes)
{
// get all neighbours and their directions
auto neighbouringTiles = hex.allNeighbouringTiles();
auto neighbouringTiles = BattleHexArray::generateAllNeighbouringTiles(hex);
std::vector<BattleHex::EDir> outsideNeighbourDirections;
@@ -525,9 +525,9 @@ std::vector<std::shared_ptr<IImage>> BattleFieldController::calculateRangeLimitH
return output;
}
void BattleFieldController::calculateRangeLimitAndHighlightImages(uint8_t distance, std::shared_ptr<CAnimation> rangeLimitImages, std::vector<BattleHex> & rangeLimitHexes, std::vector<std::shared_ptr<IImage>> & rangeLimitHexesHighlights)
void BattleFieldController::calculateRangeLimitAndHighlightImages(uint8_t distance, std::shared_ptr<CAnimation> rangeLimitImages, BattleHexArray & rangeLimitHexes, std::vector<std::shared_ptr<IImage>> & rangeLimitHexesHighlights)
{
std::vector<BattleHex> rangeHexes = getRangeHexes(hoveredHex, distance);
BattleHexArray rangeHexes = getRangeHexes(hoveredHex, distance);
rangeLimitHexes = getRangeLimitHexes(hoveredHex, rangeHexes, distance);
std::vector<std::vector<BattleHex::EDir>> rangeLimitNeighbourDirections = getOutsideNeighbourDirectionsForLimitHexes(rangeHexes, rangeLimitHexes);
rangeLimitHexesHighlights = calculateRangeLimitHighlightImages(rangeLimitNeighbourDirections, rangeLimitImages);
@@ -535,18 +535,18 @@ void BattleFieldController::calculateRangeLimitAndHighlightImages(uint8_t distan
void BattleFieldController::showHighlightedHexes(Canvas & canvas)
{
std::vector<BattleHex> rangedFullDamageLimitHexes;
std::vector<BattleHex> shootingRangeLimitHexes;
BattleHexArray rangedFullDamageLimitHexes;
BattleHexArray shootingRangeLimitHexes;
std::vector<std::shared_ptr<IImage>> rangedFullDamageLimitHexesHighlights;
std::vector<std::shared_ptr<IImage>> shootingRangeLimitHexesHighlights;
std::set<BattleHex> hoveredStackMovementRangeHexes = getMovementRangeForHoveredStack();
std::set<BattleHex> hoveredSpellHexes = getHighlightedHexesForSpellRange();
std::set<BattleHex> hoveredMoveHexes = getHighlightedHexesForMovementTarget();
BattleHexArray hoveredStackMovementRangeHexes = getMovementRangeForHoveredStack();
BattleHexArray hoveredSpellHexes = getHighlightedHexesForSpellRange();
BattleHexArray hoveredMoveHexes = getHighlightedHexesForMovementTarget();
BattleHex hoveredHex = getHoveredHex();
std::set<BattleHex> hoveredMouseHex = hoveredHex.isValid() ? std::set<BattleHex>({ hoveredHex }) : std::set<BattleHex>();
BattleHexArray hoveredMouseHex = hoveredHex.isValid() ? BattleHexArray({ hoveredHex }) : BattleHexArray();
const CStack * hoveredStack = getHoveredStack();
if(!hoveredStack && hoveredHex == BattleHex::INVALID)
@@ -573,8 +573,8 @@ void BattleFieldController::showHighlightedHexes(Canvas & canvas)
for(int hex = 0; hex < GameConstants::BFIELD_SIZE; ++hex)
{
bool stackMovement = hoveredStackMovementRangeHexes.count(hex);
bool mouse = hoveredMouseHexes.count(hex);
bool stackMovement = hoveredStackMovementRangeHexes.contains(hex);
bool mouse = hoveredMouseHexes.contains(hex);
// calculate if hex is Ranged Full Damage Limit and its position in highlight array
int hexIndexInRangedFullDamageLimit = 0;
@@ -679,7 +679,7 @@ BattleHex BattleFieldController::getHexAtPosition(Point hoverPos)
BattleHex::EDir BattleFieldController::selectAttackDirection(BattleHex myNumber)
{
const bool doubleWide = owner.stacksController->getActiveStack()->doubleWide();
auto neighbours = myNumber.allNeighbouringTiles();
auto neighbours = BattleHexArray::generateAllNeighbouringTiles(myNumber);
// 0 1
// 5 x 2
// 4 3
@@ -696,18 +696,18 @@ BattleHex::EDir BattleFieldController::selectAttackDirection(BattleHex myNumber)
// | - - | - - | - - | - o o | o o - | - - | - - | o o
for (size_t i : { 1, 2, 3})
attackAvailability[i] = vstd::contains(occupiableHexes, neighbours[i]) && vstd::contains(occupiableHexes, neighbours[i].cloneInDirection(BattleHex::RIGHT, false));
attackAvailability[i] = occupiableHexes.contains(neighbours[i]) && occupiableHexes.contains(neighbours[i].cloneInDirection(BattleHex::RIGHT, false));
for (size_t i : { 4, 5, 0})
attackAvailability[i] = vstd::contains(occupiableHexes, neighbours[i]) && vstd::contains(occupiableHexes, neighbours[i].cloneInDirection(BattleHex::LEFT, false));
attackAvailability[i] = occupiableHexes.contains(neighbours[i]) && occupiableHexes.contains(neighbours[i].cloneInDirection(BattleHex::LEFT, false));
attackAvailability[6] = vstd::contains(occupiableHexes, neighbours[0]) && vstd::contains(occupiableHexes, neighbours[1]);
attackAvailability[7] = vstd::contains(occupiableHexes, neighbours[3]) && vstd::contains(occupiableHexes, neighbours[4]);
attackAvailability[6] = occupiableHexes.contains(neighbours[0]) && occupiableHexes.contains(neighbours[1]);
attackAvailability[7] = occupiableHexes.contains(neighbours[3]) && occupiableHexes.contains(neighbours[4]);
}
else
{
for (size_t i = 0; i < 6; ++i)
attackAvailability[i] = vstd::contains(occupiableHexes, neighbours[i]);
attackAvailability[i] = occupiableHexes.contains(neighbours[i]);
attackAvailability[6] = false;
attackAvailability[7] = false;

View File

@@ -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/BattleHex.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
std::vector<BattleHex> 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);
std::set<BattleHex> getHighlightedHexesForActiveStack();
std::set<BattleHex> getMovementRangeForHoveredStack();
std::set<BattleHex> getHighlightedHexesForSpellRange();
std::set<BattleHex> getHighlightedHexesForMovementTarget();
// Range limit highlight helpers
/// get all hexes within a certain distance of given hex
std::vector<BattleHex> getRangeHexes(BattleHex sourceHex, uint8_t distance);
/// get only hexes at the limit of a range
std::vector<BattleHex> getRangeLimitHexes(BattleHex hoveredHex, std::vector<BattleHex> hexRange, uint8_t distanceToLimit);
/// calculate if a hex is in range limit and return its index in range
bool IsHexInRangeLimit(BattleHex hex, std::vector<BattleHex> & 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(std::vector<BattleHex> rangeHexes, std::vector<BattleHex> rangeLimitHexes);
/// calculates what image to use as range limit, depending on the direction of neighbors
/// 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, std::vector<BattleHex> & 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);
};

View File

@@ -216,7 +216,7 @@ void BattleInterface::stackActivated(const CStack *stack)
stacksController->stackActivated(stack);
}
void BattleInterface::stackMoved(const CStack *stack, std::vector<BattleHex> destHex, int distance, bool teleport)
void BattleInterface::stackMoved(const CStack *stack, const BattleHexArray & destHex, int distance, bool teleport)
{
if (teleport)
stacksController->stackTeleported(stack, destHex, distance);

View File

@@ -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, std::vector<BattleHex> 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;
};

View File

@@ -491,7 +491,7 @@ void BattleStacksController::stacksAreAttacked(std::vector<StackAttackedInfo> at
owner.waitForAnimations();
}
void BattleStacksController::stackTeleported(const CStack *stack, std::vector<BattleHex> destHex, int distance)
void BattleStacksController::stackTeleported(const CStack *stack, const BattleHexArray & destHex, int distance)
{
assert(destHex.size() > 0);
//owner.checkForAnimations(); // NOTE: at this point spellcast animations were added, but not executed
@@ -508,7 +508,7 @@ void BattleStacksController::stackTeleported(const CStack *stack, std::vector<Ba
// animations will be executed by spell
}
void BattleStacksController::stackMoved(const CStack *stack, std::vector<BattleHex> destHex, int distance)
void BattleStacksController::stackMoved(const CStack *stack, const BattleHexArray & destHex, int distance)
{
assert(destHex.size() > 0);
owner.checkForAnimations();

View File

@@ -1,148 +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 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, std::vector<BattleHex> destHex, int distance); //stack with id number moved to destHex
void stackTeleported(const CStack *stack, std::vector<BattleHex> 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
};

View File

@@ -34,14 +34,14 @@
#include "../../lib/mapping/CMapDefines.h"
#include "../../lib/pathfinder/CGPathNode.h"
struct NeighborTilesInfo
struct neighbourTilesInfo
{
//567
//3 4
//012
std::bitset<8> d;
NeighborTilesInfo(IMapRendererContext & context, const int3 & pos)
neighbourTilesInfo(IMapRendererContext & context, const int3 & pos)
{
auto checkTile = [&](int dx, int dy)
{
@@ -340,9 +340,9 @@ void MapRendererFow::renderTile(IMapRendererContext & context, Canvas & target,
{
assert(!context.isVisible(coordinates));
const NeighborTilesInfo neighborInfo(context, coordinates);
const neighbourTilesInfo neighbourInfo(context, coordinates);
int retBitmapID = neighborInfo.getBitmapID(); // >=0 -> partial hide, <0 - full hide
int retBitmapID = neighbourInfo.getBitmapID(); // >=0 -> partial hide, <0 - full hide
if(retBitmapID < 0)
{
// generate a number that is predefined for each tile,
@@ -367,8 +367,8 @@ uint8_t MapRendererFow::checksum(IMapRendererContext & context, const int3 & coo
if (context.showSpellRange(coordinates))
return 0xff - 2;
const NeighborTilesInfo neighborInfo(context, coordinates);
int retBitmapID = neighborInfo.getBitmapID();
const neighbourTilesInfo neighbourInfo(context, coordinates);
int retBitmapID = neighbourInfo.getBitmapID();
if(retBitmapID < 0)
return 0xff - 1;
return retBitmapID;
@@ -738,9 +738,9 @@ MapRenderer::TileChecksum MapRenderer::getTileChecksum(IMapRendererContext & con
return result;
}
const NeighborTilesInfo neighborInfo(context, coordinates);
const neighbourTilesInfo neighbourInfo(context, coordinates);
if(!context.isVisible(coordinates) && neighborInfo.areAllHidden())
if(!context.isVisible(coordinates) && neighbourInfo.areAllHidden())
{
result[7] = rendererFow.checksum(context, coordinates);
}
@@ -769,9 +769,9 @@ void MapRenderer::renderTile(IMapRendererContext & context, Canvas & target, con
return;
}
const NeighborTilesInfo neighborInfo(context, coordinates);
const neighbourTilesInfo neighbourInfo(context, coordinates);
if(!context.isVisible(coordinates) && neighborInfo.areAllHidden())
if(!context.isVisible(coordinates) && neighbourInfo.areAllHidden())
{
rendererFow.renderTile(context, target, coordinates);
}

View File

@@ -674,7 +674,7 @@ SDL_Surface * CSDL_Ext::scaleSurfaceIntegerFactor(SDL_Surface * surf, int factor
switch (algorithm)
{
case EScalingAlgorithm::NEAREST:
xbrz::nearestNeighborScale(srcPixels, intermediate->w, intermediate->h, dstPixels, ret->w, ret->h);
xbrz::nearestneighbourScale(srcPixels, intermediate->w, intermediate->h, dstPixels, ret->w, ret->h);
break;
case EScalingAlgorithm::BILINEAR:
xbrz::bilinearScale(srcPixels, intermediate->w, intermediate->h, dstPixels, ret->w, ret->h);

View File

@@ -1273,10 +1273,10 @@ void xbrz::bilinearScale(const uint32_t* src, int srcWidth, int srcHeight,
}
void xbrz::nearestNeighborScale(const uint32_t* src, int srcWidth, int srcHeight,
void xbrz::nearestneighbourScale(const uint32_t* src, int srcWidth, int srcHeight,
/**/ uint32_t* trg, int trgWidth, int trgHeight)
{
nearestNeighborScale(src, srcWidth, srcHeight, srcWidth * sizeof(uint32_t),
nearestneighbourScale(src, srcWidth, srcHeight, srcWidth * sizeof(uint32_t),
trg, trgWidth, trgHeight, trgWidth * sizeof(uint32_t),
0, trgHeight, [](uint32_t pix) { return pix; });
}

View File

@@ -69,7 +69,7 @@ void scale(size_t factor, //valid range: 2 - SCALE_FACTOR_MAX
void bilinearScale(const uint32_t* src, int srcWidth, int srcHeight,
/**/ uint32_t* trg, int trgWidth, int trgHeight);
void nearestNeighborScale(const uint32_t* src, int srcWidth, int srcHeight,
void nearestneighbourScale(const uint32_t* src, int srcWidth, int srcHeight,
/**/ uint32_t* trg, int trgWidth, int trgHeight);

View File

@@ -67,9 +67,9 @@ void fillBlock(Pix* trg, int pitch /*[bytes]*/, Pix col, int blockWidth, int blo
}
//nearest-neighbor (going over target image - slow for upscaling, since source is read multiple times missing out on cache! Fast for similar image sizes!)
//nearest-neighbour (going over target image - slow for upscaling, since source is read multiple times missing out on cache! Fast for similar image sizes!)
template <class PixSrc, class PixTrg, class PixConverter>
void nearestNeighborScale(const PixSrc* src, int srcWidth, int srcHeight, int srcPitch /*[bytes]*/,
void nearestneighbourScale(const PixSrc* src, int srcWidth, int srcHeight, int srcPitch /*[bytes]*/,
/**/ PixTrg* trg, int trgWidth, int trgHeight, int trgPitch /*[bytes]*/,
int yFirst, int yLast, PixConverter pixCvrt /*convert PixSrc to PixTrg*/)
{
@@ -104,9 +104,9 @@ void nearestNeighborScale(const PixSrc* src, int srcWidth, int srcHeight, int sr
}
//nearest-neighbor (going over source image - fast for upscaling, since source is read only once
//nearest-neighbour (going over source image - fast for upscaling, since source is read only once
template <class PixSrc, class PixTrg, class PixConverter>
void nearestNeighborScaleOverSource(const PixSrc* src, int srcWidth, int srcHeight, int srcPitch /*[bytes]*/,
void nearestneighbourScaleOverSource(const PixSrc* src, int srcWidth, int srcHeight, int srcPitch /*[bytes]*/,
/**/ PixTrg* trg, int trgWidth, int trgHeight, int trgPitch /*[bytes]*/,
int yFirst, int yLast, PixConverter pixCvrt /*convert PixSrc to PixTrg*/)
{

View File

@@ -1,103 +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 T>
void randomShuffle(std::vector<T> & container, vstd::RNG & rand)
{
int64_t n = (container.end() - container.begin());
for(int64_t i = n-1; i>0; --i)
{
std::swap(container.begin()[i],container.begin()[rand.nextInt64(0, i)]);
}
}
}
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

View File

@@ -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.emplace_back(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

View File

@@ -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/BattleHex.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;
std::vector<BattleHex> 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

View File

@@ -204,7 +204,7 @@ void CAdventureAI::battleObstaclesChanged(const BattleID & battleID, const std::
battleAI->battleObstaclesChanged(battleID, obstacles);
}
void CAdventureAI::battleStackMoved(const BattleID & battleID, const CStack * stack, std::vector<BattleHex> dest, int distance, bool teleport)
void CAdventureAI::battleStackMoved(const BattleID & battleID, const CStack * stack, BattleHexArray dest, int distance, bool teleport)
{
battleAI->battleStackMoved(battleID, stack, dest, distance, teleport);
}

View File

@@ -151,7 +151,7 @@ public:
void actionFinished(const BattleID & battleID, const BattleAction &action) override;
void battleStacksEffectsSet(const BattleID & battleID, const SetStackEffect & sse) override;
void battleObstaclesChanged(const BattleID & battleID, const std::vector<ObstacleChanges> & obstacles) override;
void battleStackMoved(const BattleID & battleID, const CStack * stack, std::vector<BattleHex> dest, int distance, bool teleport) override;
void battleStackMoved(const BattleID & battleID, const CStack * stack, BattleHexArray dest, int distance, bool teleport) override;
void battleAttack(const BattleID & battleID, const BattleAttack *ba) override;
void battleSpellCast(const BattleID & battleID, const BattleSpellCast *sc) override;
void battleEnd(const BattleID & battleID, const BattleResult *br, QueryID queryID) override;

View File

@@ -47,6 +47,7 @@ set(lib_MAIN_SRCS
battle/BattleAction.cpp
battle/BattleAttackInfo.cpp
battle/BattleHex.cpp
battle/BattleHexArray.cpp
battle/BattleInfo.cpp
battle/BattleLayout.cpp
battle/BattleProxy.cpp
@@ -413,6 +414,7 @@ set(lib_MAIN_HEADERS
battle/BattleAction.h
battle/BattleAttackInfo.h
battle/BattleHex.h
battle/BattleHexArray.h
battle/BattleInfo.h
battle/BattleLayout.h
battle/BattleSide.h

View File

@@ -242,10 +242,10 @@ void CStack::prepareAttacked(BattleStackAttacked & bsa, vstd::RNG & rand, const
bsa.newState.operation = UnitChanges::EOperation::RESET_STATE;
}
std::vector<BattleHex> CStack::meleeAttackHexes(const battle::Unit * attacker, const battle::Unit * defender, BattleHex attackerPos, BattleHex defenderPos)
BattleHexArray CStack::meleeAttackHexes(const battle::Unit * attacker, const battle::Unit * defender, BattleHex attackerPos, BattleHex defenderPos)
{
int mask = 0;
std::vector<BattleHex> res;
BattleHexArray res;
if (!attackerPos.isValid())
attackerPos = attacker->getPosition();
@@ -260,7 +260,7 @@ std::vector<BattleHex> CStack::meleeAttackHexes(const battle::Unit * attacker, c
if((mask & 1) == 0)
{
mask |= 1;
res.push_back(defenderPos);
res.insert(defenderPos);
}
}
if (attacker->doubleWide() //back <=> front
@@ -269,7 +269,7 @@ std::vector<BattleHex> CStack::meleeAttackHexes(const battle::Unit * attacker, c
if((mask & 1) == 0)
{
mask |= 1;
res.push_back(defenderPos);
res.insert(defenderPos);
}
}
if (defender->doubleWide()//front <=> back
@@ -278,7 +278,7 @@ std::vector<BattleHex> CStack::meleeAttackHexes(const battle::Unit * attacker, c
if((mask & 2) == 0)
{
mask |= 2;
res.push_back(otherDefenderPos);
res.insert(otherDefenderPos);
}
}
if (defender->doubleWide() && attacker->doubleWide()//back <=> back
@@ -287,7 +287,7 @@ std::vector<BattleHex> CStack::meleeAttackHexes(const battle::Unit * attacker, c
if((mask & 2) == 0)
{
mask |= 2;
res.push_back(otherDefenderPos);
res.insert(otherDefenderPos);
}
}

View File

@@ -60,7 +60,7 @@ public:
std::vector<SpellID> activeSpells() const; //returns vector of active spell IDs sorted by time of cast
const CGHeroInstance * getMyHero() const; //if stack belongs to hero (directly or was by him summoned) returns hero, nullptr otherwise
static std::vector<BattleHex> meleeAttackHexes(const battle::Unit * attacker, const battle::Unit * defender, BattleHex attackerPos = BattleHex::INVALID, BattleHex defenderPos = BattleHex::INVALID);
static BattleHexArray meleeAttackHexes(const battle::Unit * attacker, const battle::Unit * defender, BattleHex attackerPos = BattleHex::INVALID, BattleHex defenderPos = BattleHex::INVALID);
static bool isMeleeAttackPossible(const battle::Unit * attacker, const battle::Unit * defender, BattleHex attackerPos = BattleHex::INVALID, BattleHex defenderPos = BattleHex::INVALID);
BattleHex::EDir destShiftDir() const;
@@ -146,4 +146,4 @@ private:
const BattleInfo * battle; //do not serialize
};
VCMI_LIB_NAMESPACE_END
VCMI_LIB_NAMESPACE_END

View File

@@ -10,7 +10,7 @@
#pragma once
#include "networkPacks/EInfoWindowMode.h"
#include "battle/BattleHex.h"
#include "battle/BattleHexArray.h"
#include "GameConstants.h"
#include "int3.h"
@@ -63,7 +63,7 @@ public:
virtual void battleNewRoundFirst(const BattleID & battleID){}; //called at the beginning of each turn before changes are applied;
virtual void battleNewRound(const BattleID & battleID){}; //called at the beginning of each turn, round=-1 is the tactic phase, round=0 is the first "normal" turn
virtual void battleLogMessage(const BattleID & battleID, const std::vector<MetaString> & lines){};
virtual void battleStackMoved(const BattleID & battleID, const CStack * stack, std::vector<BattleHex> dest, int distance, bool teleport){};
virtual void battleStackMoved(const BattleID & battleID, const CStack * stack, BattleHexArray dest, int distance, bool teleport){};
virtual void battleSpellCast(const BattleID & battleID, const BattleSpellCast *sc){};
virtual void battleStacksEffectsSet(const BattleID & battleID, const SetStackEffect & sse){};//called when a specific effect is set to stacks
virtual void battleTriggerEffect(const BattleID & battleID, const BattleTriggerEffect & bte){}; //called for various one-shot effects

View File

@@ -1,130 +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;
}
std::vector<BattleHex> ObstacleInfo::getBlocked(BattleHex hex) const
{
std::vector<BattleHex> ret;
if(isAbsoluteObstacle)
{
assert(!hex.isValid());
range::copy(blockedTiles, std::back_inserter(ret));
return 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.push_back(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

View File

@@ -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/BattleHex.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;
std::vector<BattleHex> 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

View File

@@ -1,245 +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;
}
bool BattleHex::isValid() const
{
return hex >= 0 && hex < GameConstants::BFIELD_SIZE;
}
bool BattleHex::isAvailable() const
{
return isValid() && getX() > 0 && getX() < GameConstants::BFIELD_WIDTH-1;
}
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);
}
std::vector<BattleHex> BattleHex::neighbouringTiles() const
{
std::vector<BattleHex> ret;
ret.reserve(6);
for(auto dir : hexagonalDirections())
checkAndPush(cloneInDirection(dir, false), ret);
return ret;
}
std::vector<BattleHex> BattleHex::allNeighbouringTiles() const
{
std::vector<BattleHex> ret;
ret.resize(6);
for(auto dir : hexagonalDirections())
ret[dir] = cloneInDirection(dir, false);
return ret;
}
BattleHex::EDir BattleHex::mutualPosition(BattleHex hex1, BattleHex hex2)
{
for(auto dir : hexagonalDirections())
if(hex2 == hex1.cloneInDirection(dir, false))
return dir;
return NONE;
}
uint8_t BattleHex::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);
}
void BattleHex::checkAndPush(BattleHex tile, std::vector<BattleHex> & ret)
{
if(tile.isAvailable())
ret.push_back(tile);
}
BattleHex BattleHex::getClosestTile(BattleSide side, BattleHex initialPos, std::set<BattleHex> & possibilities)
{
std::vector<BattleHex> sortedTiles (possibilities.begin(), possibilities.end()); //set can't be sorted properly :(
BattleHex initialHex = BattleHex(initialPos);
auto compareDistance = [initialHex](const BattleHex left, const BattleHex right) -> bool
{
return initialHex.getDistance (initialHex, left) < initialHex.getDistance (initialHex, right);
};
boost::sort (sortedTiles, compareDistance); //closest tiles at front
int closestDistance = initialHex.getDistance(initialPos, sortedTiles.front()); //sometimes closest tiles can be many hexes away
auto notClosest = [closestDistance, initialPos](const BattleHex here) -> bool
{
return closestDistance < here.getDistance (initialPos, here);
};
vstd::erase_if(sortedTiles, notClosest); //only closest tiles are interesting
auto compareHorizontal = [side, initialPos](const BattleHex left, const BattleHex right) -> bool
{
if(left.getX() != right.getX())
{
if(side == BattleSide::ATTACKER)
return left.getX() > right.getX(); //find furthest right
else
return left.getX() < right.getX(); //find furthest left
}
else
{
//Prefer tiles in the same row.
return std::abs(left.getY() - initialPos.getY()) < std::abs(right.getY() - initialPos.getY());
}
};
boost::sort (sortedTiles, compareHorizontal);
return sortedTiles.front();
}
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);
}
static BattleHex::NeighbouringTilesCache calculateNeighbouringTiles()
{
BattleHex::NeighbouringTilesCache ret;
ret.resize(GameConstants::BFIELD_SIZE);
for(si16 hex = 0; hex < GameConstants::BFIELD_SIZE; hex++)
{
auto hexes = BattleHex(hex).neighbouringTiles();
size_t index = 0;
for(auto neighbour : hexes)
ret[hex].at(index++) = neighbour;
}
return ret;
}
const BattleHex::NeighbouringTilesCache BattleHex::neighbouringTilesCache = calculateNeighbouringTiles();
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

View File

@@ -1,117 +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;
bool isValid() const;
bool isAvailable() const; //valid position not in first or last column
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;
/// returns all valid neighbouring tiles
std::vector<BattleHex> neighbouringTiles() const;
/// returns all tiles, unavailable tiles will be set as invalid
/// order of returned tiles matches EDir enim
std::vector<BattleHex> allNeighbouringTiles() const;
static EDir mutualPosition(BattleHex hex1, BattleHex hex2);
static uint8_t getDistance(BattleHex hex1, BattleHex hex2);
static void checkAndPush(BattleHex tile, std::vector<BattleHex> & ret);
static BattleHex getClosestTile(BattleSide side, BattleHex initialPos, std::set<BattleHex> & possibilities); //TODO: vector or set? copying one to another is bad
template <typename Handler>
void serialize(Handler &h)
{
h & hex;
}
using NeighbouringTiles = std::array<BattleHex, 6>;
using NeighbouringTilesCache = std::vector<NeighbouringTiles>;
static const NeighbouringTilesCache neighbouringTilesCache;
private:
//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

View File

@@ -0,0 +1,103 @@
/*
* BattleHexArray.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 "BattleHexArray.h"
VCMI_LIB_NAMESPACE_BEGIN
BattleHexArray::BattleHexArray(std::initializer_list<BattleHex> initList) noexcept
: BattleHexArray()
{
for(auto hex : initList)
{
insert(hex);
}
}
BattleHex BattleHexArray::getClosestTile(BattleSide side, BattleHex initialPos)
{
BattleHex initialHex = BattleHex(initialPos);
auto compareDistance = [initialHex](const BattleHex left, const BattleHex right) -> bool
{
return initialHex.getDistance(initialHex, left) < initialHex.getDistance(initialHex, right);
};
BattleHexArray sortedTiles(*this);
boost::sort(sortedTiles, compareDistance); //closest tiles at front
int closestDistance = initialHex.getDistance(initialPos, sortedTiles.front()); //sometimes closest tiles can be many hexes away
auto notClosest = [closestDistance, initialPos](const BattleHex here) -> bool
{
return closestDistance < here.getDistance(initialPos, here);
};
vstd::erase_if(sortedTiles, notClosest); //only closest tiles are interesting
auto compareHorizontal = [side, initialPos](const BattleHex left, const BattleHex right) -> bool
{
if(left.getX() != right.getX())
{
if(side == BattleSide::ATTACKER)
return left.getX() > right.getX(); //find furthest right
else
return left.getX() < right.getX(); //find furthest left
}
else
{
//Prefer tiles in the same row.
return std::abs(left.getY() - initialPos.getY()) < std::abs(right.getY() - initialPos.getY());
}
};
boost::sort(sortedTiles, compareHorizontal);
return sortedTiles.front();
}
void BattleHexArray::merge(const BattleHexArray & other) noexcept
{
for(auto hex : other)
{
insert(hex);
}
}
void BattleHexArray::erase(iterator first, iterator last) noexcept
{
for(auto it = first; it != last && it != internalStorage.end(); ++it)
{
presenceFlags[*it] = 0;
}
internalStorage.erase(first, last);
}
void BattleHexArray::clear() noexcept
{
for(auto hex : internalStorage)
presenceFlags[hex] = 0;
internalStorage.clear();
}
static BattleHexArray::NeighbouringTilesCache calculateNeighbouringTiles()
{
BattleHexArray::NeighbouringTilesCache ret;
for(si16 hex = 0; hex < GameConstants::BFIELD_SIZE; hex++)
{
auto hexes = BattleHexArray::generateNeighbouringTiles(hex);
size_t index = 0;
for(auto neighbour : hexes)
ret[hex].at(index++) = neighbour;
}
return ret;
}
const BattleHexArray::NeighbouringTilesCache BattleHexArray::neighbouringTilesCache = calculateNeighbouringTiles();
VCMI_LIB_NAMESPACE_END

300
lib/battle/BattleHexArray.h Normal file
View File

@@ -0,0 +1,300 @@
/*
* BattleHexArray.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 "BattleHex.h"
VCMI_LIB_NAMESPACE_BEGIN
/// Class representing an array of unique BattleHex objects
class DLL_LINKAGE BattleHexArray
{
public:
static constexpr uint8_t totalSize = GameConstants::BFIELD_SIZE;
using StorageType = std::vector<BattleHex>;
using value_type = BattleHex;
using size_type = StorageType::size_type;
using reference = value_type &;
using const_reference = const value_type &;
using pointer = value_type *;
using const_pointer = const value_type *;
using difference_type = typename StorageType::difference_type;
using iterator = typename StorageType::iterator;
using const_iterator = typename StorageType::const_iterator;
using reverse_iterator = typename StorageType::reverse_iterator;
using const_reverse_iterator = typename StorageType::const_reverse_iterator;
using NeighbouringTiles = std::array<BattleHex, 6>;
using NeighbouringTilesCache = std::array<NeighbouringTiles, GameConstants::BFIELD_SIZE>;
static const NeighbouringTilesCache neighbouringTilesCache;
BattleHexArray() noexcept
{
internalStorage.reserve(totalSize);
}
template <typename Container, typename = std::enable_if_t<
std::is_convertible_v<typename Container::value_type, BattleHex>>>
BattleHexArray(const Container & container) noexcept
: BattleHexArray()
{
for(auto value : container)
{
insert(value);
}
}
void resize(size_type size)
{
clear();
internalStorage.resize(size);
}
BattleHexArray(std::initializer_list<BattleHex> initList) noexcept;
/// returns all valid neighbouring tiles
static BattleHexArray generateNeighbouringTiles(BattleHex hex)
{
BattleHexArray ret;
for(auto dir : BattleHex::hexagonalDirections())
ret.checkAndPush(hex.cloneInDirection(dir, false));
return ret;
}
/// returns all tiles, unavailable tiles will be set as invalid
/// order of returned tiles matches EDir enum
static BattleHexArray generateAllNeighbouringTiles(BattleHex hex)
{
BattleHexArray ret;
ret.resize(6);
for(auto dir : BattleHex::hexagonalDirections())
ret.set(dir, hex.cloneInDirection(dir, false));
return ret;
}
BattleHex getClosestTile(BattleSide side, BattleHex initialPos);
void checkAndPush(BattleHex tile)
{
if(tile.isAvailable() && !contains(tile))
{
presenceFlags[tile] = 1;
internalStorage.emplace_back(tile);
}
}
void insert(BattleHex hex) noexcept
{
/*if(isNotValidForInsertion(hex))
return;*/
if(contains(hex))
return;
presenceFlags[hex] = 1;
internalStorage.emplace_back(hex);
}
void set(size_type index, BattleHex hex)
{
/*if(isNotValidForInsertion(hex))
return;*/
if(contains(hex))
return;
presenceFlags[hex] = 1;
internalStorage[index] = hex;
}
iterator BattleHexArray::insert(iterator pos, BattleHex hex) noexcept
{
/*if(isNotValidForInsertion(hex))
return pos;*/
if(contains(hex))
return pos;
presenceFlags[hex] = 1;
return internalStorage.insert(pos, hex);
}
void merge(const BattleHexArray & other) noexcept;
void clear() noexcept;
inline void erase(size_type index) noexcept
{
assert(index < totalSize);
internalStorage[index] = BattleHex::INVALID;
presenceFlags[index] = 0;
}
void erase(iterator first, iterator last) noexcept;
inline void pop_back() noexcept
{
presenceFlags[internalStorage.back()] = 0;
internalStorage.pop_back();
}
inline std::vector<BattleHex> toVector() const noexcept
{
return internalStorage;
}
template <typename Predicate>
iterator findIf(Predicate predicate) noexcept
{
return std::find_if(begin(), end(), predicate);
}
template <typename Predicate>
const_iterator findIf(Predicate predicate) const noexcept
{
return std::find_if(begin(), end(), predicate);
}
template <typename Predicate>
BattleHexArray filterBy(Predicate predicate) const noexcept
{
BattleHexArray filtered;
for(auto hex : internalStorage)
{
if(predicate(hex))
{
filtered.insert(hex);
}
}
return filtered;
}
[[nodiscard]] inline bool BattleHexArray::contains(BattleHex hex) const noexcept
{
if(hex.isValid())
return presenceFlags[hex];
/*
if(!isTower(hex))
logGlobal->warn("BattleHexArray::contains( %d ) - invalid BattleHex!", hex);
*/
// return true for invalid hexes
return true;
}
template <typename Serializer>
void serialize(Serializer & s)
{
s & internalStorage;
if(!internalStorage.empty() && presenceFlags[internalStorage.front()] == 0)
{
for(auto hex : internalStorage)
presenceFlags[hex] = 1;
}
}
[[nodiscard]] inline const BattleHex & BattleHexArray::back() const noexcept
{
return internalStorage.back();
}
[[nodiscard]] inline const BattleHex & BattleHexArray::front() const noexcept
{
return internalStorage.front();
}
[[nodiscard]] inline const BattleHex & BattleHexArray::operator[](size_type index) const noexcept
{
return internalStorage[index];
}
[[nodiscard]] inline const BattleHex & BattleHexArray::at(size_type index) const
{
return internalStorage.at(index);
}
[[nodiscard]] inline size_type size() const noexcept
{
return internalStorage.size();
}
[[nodiscard]] inline BattleHexArray::iterator BattleHexArray::begin() noexcept
{
return internalStorage.begin();
}
[[nodiscard]] inline BattleHexArray::const_iterator BattleHexArray::begin() const noexcept
{
return internalStorage.begin();
}
[[nodiscard]] inline bool BattleHexArray::empty() const noexcept
{
return internalStorage.empty();
}
[[nodiscard]] inline BattleHexArray::iterator BattleHexArray::end() noexcept
{
return internalStorage.end();
}
[[nodiscard]] inline BattleHexArray::const_iterator BattleHexArray::end() const noexcept
{
return internalStorage.end();
}
[[nodiscard]] inline BattleHexArray::reverse_iterator BattleHexArray::rbegin() noexcept
{
return reverse_iterator(end());
}
[[nodiscard]] inline BattleHexArray::const_reverse_iterator BattleHexArray::rbegin() const noexcept
{
return const_reverse_iterator(end());
}
[[nodiscard]] inline BattleHexArray::reverse_iterator BattleHexArray::rend() noexcept
{
return reverse_iterator(begin());
}
[[nodiscard]] inline BattleHexArray::const_reverse_iterator BattleHexArray::rend() const noexcept
{
return const_reverse_iterator(begin());
}
private:
StorageType internalStorage;
std::array<uint8_t, totalSize> presenceFlags = {};
[[nodiscard]] inline bool BattleHexArray::isNotValidForInsertion(BattleHex hex) const
{
if(isTower(hex))
return true;
if(!hex.isValid())
{
//logGlobal->warn("BattleHexArray::insert( %d ) - invalid BattleHex!", hex);
return true;
}
return contains(hex) || internalStorage.size() >= totalSize;
}
[[nodiscard]] inline bool isTower(BattleHex hex) const
{
return hex == BattleHex::CASTLE_CENTRAL_TOWER || hex == BattleHex::CASTLE_UPPER_TOWER || hex == BattleHex::CASTLE_BOTTOM_TOWER;
}
};
VCMI_LIB_NAMESPACE_END

View File

@@ -207,7 +207,7 @@ BattleInfo * BattleInfo::setupBattle(const int3 & tile, TerrainId terrain, const
r.rand(1,8); //battle sound ID to play... can't do anything with it here
int tilesToBlock = r.rand(5,12);
std::vector<BattleHex> blockedTiles;
BattleHexArray blockedTiles;
auto appropriateAbsoluteObstacle = [&](int id)
{
@@ -232,7 +232,7 @@ BattleInfo * BattleInfo::setupBattle(const int3 & tile, TerrainId terrain, const
currentBattle->obstacles.push_back(obstPtr);
for(BattleHex blocked : obstPtr->getBlockedTiles())
blockedTiles.push_back(blocked);
blockedTiles.insert(blocked);
tilesToBlock -= Obstacle(obstPtr->ID).getInfo()->blockedTiles.size() / 2;
}
catch(RangeGenerator::ExhaustedPossibilities &)
@@ -259,14 +259,14 @@ BattleInfo * BattleInfo::setupBattle(const int3 & tile, TerrainId terrain, const
return false;
if(pos.getX() + obi.width > 15)
return false;
if(vstd::contains(blockedTiles, pos))
if(blockedTiles.contains(pos))
return false;
for(BattleHex blocked : obi.getBlocked(pos))
{
if(tileAccessibility[blocked] == EAccessibility::UNAVAILABLE) //for ship-to-ship battlefield - exclude hardcoded unavailable tiles
return false;
if(vstd::contains(blockedTiles, blocked))
if(blockedTiles.contains(blocked))
return false;
int x = blocked.getX();
if(x <= 2 || x >= 14)
@@ -285,7 +285,7 @@ BattleInfo * BattleInfo::setupBattle(const int3 & tile, TerrainId terrain, const
currentBattle->obstacles.push_back(obstPtr);
for(BattleHex blocked : obstPtr->getBlockedTiles())
blockedTiles.push_back(blocked);
blockedTiles.insert(blocked);
tilesToBlock -= static_cast<int>(obi.blockedTiles.size());
}
}

View File

@@ -147,21 +147,21 @@ ESpellCastProblem CBattleInfoCallback::battleCanCastSpell(const spells::Caster *
return ESpellCastProblem::OK;
}
std::pair< std::vector<BattleHex>, int > CBattleInfoCallback::getPath(BattleHex start, BattleHex dest, const battle::Unit * stack) const
std::pair< BattleHexArray, int > CBattleInfoCallback::getPath(BattleHex start, BattleHex dest, const battle::Unit * stack) const
{
auto reachability = getReachability(stack);
if(reachability.predecessors[dest] == -1) //cannot reach destination
{
return std::make_pair(std::vector<BattleHex>(), 0);
return std::make_pair(BattleHexArray(), 0);
}
//making the Path
std::vector<BattleHex> path;
BattleHexArray path;
BattleHex curElem = dest;
while(curElem != start)
{
path.push_back(curElem);
path.insert(curElem);
curElem = reachability.predecessors[curElem];
}
@@ -191,23 +191,21 @@ bool CBattleInfoCallback::battleHasPenaltyOnLine(BattleHex from, BattleHex dest,
return isWallPartAttackable(wallPart);
};
// Count wall penalty requirement by shortest path, not by arbitrary line, to avoid various OH3 bugs
auto getShortestPath = [](BattleHex from, BattleHex dest) -> std::vector<BattleHex>
auto getShortestPath = [](BattleHex from, BattleHex dest) -> BattleHexArray
{
//Out early
if(from == dest)
return {};
std::vector<BattleHex> ret;
BattleHexArray ret;
auto next = from;
//Not a real direction, only to indicate to which side we should search closest tile
auto direction = from.getX() > dest.getX() ? BattleSide::DEFENDER : BattleSide::ATTACKER;
while (next != dest)
{
auto tiles = next.neighbouringTiles();
std::set<BattleHex> possibilities = {tiles.begin(), tiles.end()};
next = BattleHex::getClosestTile(direction, dest, possibilities);
ret.push_back(next);
next = BattleHexArray::generateNeighbouringTiles(next).getClosestTile(direction, dest);
ret.insert(next);
}
assert(!ret.empty());
ret.pop_back(); //Remove destination hex
@@ -318,9 +316,9 @@ PossiblePlayerBattleAction CBattleInfoCallback::getCasterAction(const CSpell * s
return PossiblePlayerBattleAction(spellSelMode, spell->id);
}
std::set<BattleHex> CBattleInfoCallback::battleGetAttackedHexes(const battle::Unit * attacker, BattleHex destinationTile, BattleHex attackerPos) const
BattleHexArray CBattleInfoCallback::battleGetAttackedHexes(const battle::Unit * attacker, BattleHex destinationTile, BattleHex attackerPos) const
{
std::set<BattleHex> attackedHexes;
BattleHexArray attackedHexes;
RETURN_IF_NOT_BATTLE(attackedHexes);
AttackableTiles at = getPotentiallyAttackableHexes(attacker, destinationTile, attackerPos);
@@ -347,7 +345,7 @@ const CStack* CBattleInfoCallback::battleGetStackByPos(BattleHex pos, bool onlyA
{
RETURN_IF_NOT_BATTLE(nullptr);
for(const auto * s : battleGetAllStacks(true))
if(vstd::contains(s->getHexes(), pos) && (!onlyAlive || s->alive()))
if(s->getHexes().contains(pos) && (!onlyAlive || s->alive()))
return s;
return nullptr;
@@ -569,21 +567,21 @@ void CBattleInfoCallback::battleGetTurnOrder(std::vector<battle::Units> & turns,
battleGetTurnOrder(turns, maxUnits, maxTurns, actualTurn + 1, sideThatLastMoved);
}
std::vector<BattleHex> CBattleInfoCallback::battleGetAvailableHexes(const battle::Unit * unit, bool obtainMovementRange) const
BattleHexArray CBattleInfoCallback::battleGetAvailableHexes(const battle::Unit * unit, bool obtainMovementRange) const
{
RETURN_IF_NOT_BATTLE(std::vector<BattleHex>());
RETURN_IF_NOT_BATTLE(BattleHexArray());
if(!unit->getPosition().isValid()) //turrets
return std::vector<BattleHex>();
return BattleHexArray();
auto reachability = getReachability(unit);
return battleGetAvailableHexes(reachability, unit, obtainMovementRange);
}
std::vector<BattleHex> CBattleInfoCallback::battleGetAvailableHexes(const ReachabilityInfo & cache, const battle::Unit * unit, bool obtainMovementRange) const
BattleHexArray CBattleInfoCallback::battleGetAvailableHexes(const ReachabilityInfo & cache, const battle::Unit * unit, bool obtainMovementRange) const
{
std::vector<BattleHex> ret;
BattleHexArray ret;
RETURN_IF_NOT_BATTLE(ret);
if(!unit->getPosition().isValid()) //turrets
@@ -612,28 +610,27 @@ std::vector<BattleHex> CBattleInfoCallback::battleGetAvailableHexes(const Reacha
continue;
}
ret.emplace_back(i);
ret.insert(i);
}
return ret;
}
std::vector<BattleHex> CBattleInfoCallback::battleGetAvailableHexes(const battle::Unit * unit, bool obtainMovementRange, bool addOccupiable, std::vector<BattleHex> * attackable) const
BattleHexArray CBattleInfoCallback::battleGetAvailableHexes(const battle::Unit * unit, bool obtainMovementRange, bool addOccupiable, BattleHexArray * attackable) const
{
std::vector<BattleHex> ret = battleGetAvailableHexes(unit, obtainMovementRange);
BattleHexArray ret = battleGetAvailableHexes(unit, obtainMovementRange);
if(ret.empty())
return ret;
if(addOccupiable && unit->doubleWide())
{
std::vector<BattleHex> occupiable;
BattleHexArray occupiable;
occupiable.reserve(ret.size());
for(auto hex : ret)
occupiable.push_back(unit->occupiedHex(hex));
occupiable.insert(unit->occupiedHex(hex));
vstd::concatenate(ret, occupiable);
ret.merge(occupiable);
}
@@ -643,36 +640,33 @@ std::vector<BattleHex> CBattleInfoCallback::battleGetAvailableHexes(const battle
{
// Return true if given hex has at least one available neighbour.
// Available hexes are already present in ret vector.
auto availableNeighbor = boost::find_if(ret, [=] (BattleHex availableHex)
auto availableneighbour = boost::find_if(ret, [=] (BattleHex availableHex)
{
return BattleHex::mutualPosition(hex, availableHex) >= 0;
});
return availableNeighbor != ret.end();
return availableneighbour != ret.end();
};
for(const auto * otherSt : battleAliveUnits(otherSide(unit->unitSide())))
{
if(!otherSt->isValidTarget(false))
continue;
std::vector<BattleHex> occupied = otherSt->getHexes();
BattleHexArray occupied = otherSt->getHexes();
if(battleCanShoot(unit, otherSt->getPosition()))
{
attackable->insert(attackable->end(), occupied.begin(), occupied.end());
attackable->merge(occupied);
continue;
}
for(BattleHex he : occupied)
{
if(meleeAttackable(he))
attackable->push_back(he);
attackable->insert(he);
}
}
}
//adding occupiable likely adds duplicates to ret -> clean it up
boost::sort(ret);
ret.erase(boost::unique(ret).end(), ret.end());
return ret;
}
@@ -857,8 +851,8 @@ std::vector<std::shared_ptr<const CObstacleInstance>> CBattleInfoCallback::battl
RETURN_IF_NOT_BATTLE(obstacles);
for(auto & obs : battleGetAllObstacles())
{
if(vstd::contains(obs->getBlockedTiles(), tile)
|| (!onlyBlocking && vstd::contains(obs->getAffectedTiles(), tile)))
if(obs->getBlockedTiles().contains(tile)
|| (!onlyBlocking && obs->getAffectedTiles().contains(tile)))
{
obstacles.push_back(obs);
}
@@ -866,18 +860,18 @@ std::vector<std::shared_ptr<const CObstacleInstance>> CBattleInfoCallback::battl
return obstacles;
}
std::vector<std::shared_ptr<const CObstacleInstance>> CBattleInfoCallback::getAllAffectedObstaclesByStack(const battle::Unit * unit, const std::set<BattleHex> & passed) const
std::vector<std::shared_ptr<const CObstacleInstance>> CBattleInfoCallback::getAllAffectedObstaclesByStack(const battle::Unit * unit, const BattleHexArray & passed) const
{
auto affectedObstacles = std::vector<std::shared_ptr<const CObstacleInstance>>();
RETURN_IF_NOT_BATTLE(affectedObstacles);
if(unit->alive())
{
if(!passed.count(unit->getPosition()))
if(!passed.contains(unit->getPosition()))
affectedObstacles = battleGetAllObstaclesOnPos(unit->getPosition(), false);
if(unit->doubleWide())
{
BattleHex otherHex = unit->occupiedHex();
if(otherHex.isValid() && !passed.count(otherHex))
if(otherHex.isValid() && !passed.contains(otherHex))
for(auto & i : battleGetAllObstaclesOnPos(otherHex, false))
if(!vstd::contains(affectedObstacles, i))
affectedObstacles.push_back(i);
@@ -891,7 +885,7 @@ std::vector<std::shared_ptr<const CObstacleInstance>> CBattleInfoCallback::getAl
return affectedObstacles;
}
bool CBattleInfoCallback::handleObstacleTriggersForUnit(SpellCastEnvironment & spellEnv, const battle::Unit & unit, const std::set<BattleHex> & passed) const
bool CBattleInfoCallback::handleObstacleTriggersForUnit(SpellCastEnvironment & spellEnv, const battle::Unit & unit, const BattleHexArray & passed) const
{
if(!unit.alive())
return false;
@@ -970,7 +964,7 @@ AccessibilityInfo CBattleInfoCallback::getAccessibility() const
if(bFieldType != BattleField::NONE)
{
std::vector<BattleHex> impassableHexes = bFieldType.getInfo()->impassableHexes;
BattleHexArray impassableHexes = bFieldType.getInfo()->impassableHexes;
for(auto hex : impassableHexes)
ret[hex] = EAccessibility::UNAVAILABLE;
@@ -1040,7 +1034,7 @@ AccessibilityInfo CBattleInfoCallback::getAccessibility(const battle::Unit * sta
return getAccessibility(battle::Unit::getHexes(stack->getPosition(), stack->doubleWide(), stack->unitSide()));
}
AccessibilityInfo CBattleInfoCallback::getAccessibility(const std::vector<BattleHex> & accessibleHexes) const
AccessibilityInfo CBattleInfoCallback::getAccessibility(const BattleHexArray & accessibleHexes) const
{
auto ret = getAccessibility();
for(auto hex : accessibleHexes)
@@ -1062,7 +1056,7 @@ ReachabilityInfo CBattleInfoCallback::makeBFS(const AccessibilityInfo &accessibi
if(!params.startPosition.isValid()) //if got call for arrow turrets
return ret;
const std::set<BattleHex> obstacles = getStoppers(params.perspective);
const BattleHexArray obstacles = getStoppers(params.perspective);
auto checkParams = params;
checkParams.ignoreKnownAccessible = true; //Ignore starting hexes obstacles
@@ -1087,7 +1081,7 @@ ReachabilityInfo CBattleInfoCallback::makeBFS(const AccessibilityInfo &accessibi
const int costToNeighbour = ret.distances.at(curHex.hex) + 1;
for(BattleHex neighbour : BattleHex::neighbouringTilesCache[curHex.hex])
for(BattleHex neighbour : BattleHexArray::neighbouringTilesCache[curHex.hex])
{
if(neighbour.isValid())
{
@@ -1120,17 +1114,17 @@ ReachabilityInfo CBattleInfoCallback::makeBFS(const AccessibilityInfo &accessibi
bool CBattleInfoCallback::isInObstacle(
BattleHex hex,
const std::set<BattleHex> & obstacles,
const BattleHexArray & obstacleHexes,
const ReachabilityInfo::Parameters & params) const
{
auto occupiedHexes = battle::Unit::getHexes(hex, params.doubleWide, params.side);
for(auto occupiedHex : occupiedHexes)
{
if(params.ignoreKnownAccessible && vstd::contains(params.knownAccessible, occupiedHex))
if(params.ignoreKnownAccessible && params.knownAccessible.contains(occupiedHex))
continue;
if(vstd::contains(obstacles, occupiedHex))
if(obstacleHexes.contains(occupiedHex))
{
if(occupiedHex == BattleHex::GATE_BRIDGE)
{
@@ -1145,9 +1139,9 @@ bool CBattleInfoCallback::isInObstacle(
return false;
}
std::set<BattleHex> CBattleInfoCallback::getStoppers(BattleSide whichSidePerspective) const
BattleHexArray CBattleInfoCallback::getStoppers(BattleSide whichSidePerspective) const
{
std::set<BattleHex> ret;
BattleHexArray ret;
RETURN_IF_NOT_BATTLE(ret);
for(auto &oi : battleGetAllObstacles(whichSidePerspective))
@@ -1225,7 +1219,7 @@ BattleHex CBattleInfoCallback::getAvailableHex(const CreatureID & creID, BattleS
auto accessibility = getAccessibility();
std::set<BattleHex> occupyable;
BattleHexArray occupyable;
for(int i = 0; i < accessibility.size(); i++)
if(accessibility.accessible(i, twoHex, side))
occupyable.insert(i);
@@ -1235,7 +1229,7 @@ BattleHex CBattleInfoCallback::getAvailableHex(const CreatureID & creID, BattleS
return BattleHex::INVALID; //all tiles are covered
}
return BattleHex::getClosestTile(side, pos, occupyable);
return occupyable.getClosestTile(side, pos);
}
si8 CBattleInfoCallback::battleGetTacticDist() const
@@ -1353,7 +1347,7 @@ AttackableTiles CBattleInfoCallback::getPotentiallyAttackableHexes(
}
if(attacker->hasBonusOfType(BonusType::THREE_HEADED_ATTACK))
{
std::vector<BattleHex> hexes = attacker->getSurroundingHexes(attackerPos);
BattleHexArray hexes = attacker->getSurroundingHexes(attackerPos);
for(BattleHex tile : hexes)
{
if((BattleHex::mutualPosition(tile, destinationTile) > -1 && BattleHex::mutualPosition(tile, attackOriginHex) > -1)) //adjacent both to attacker's head and attacked tile
@@ -1366,12 +1360,12 @@ AttackableTiles CBattleInfoCallback::getPotentiallyAttackableHexes(
}
if(attacker->hasBonusOfType(BonusType::WIDE_BREATH))
{
std::vector<BattleHex> hexes = destinationTile.neighbouringTiles();
for(int i = 0; i<hexes.size(); i++)
BattleHexArray hexes = BattleHexArray::generateNeighbouringTiles(destinationTile);
for(int i = 0; i < hexes.size(); i++)
{
if(hexes.at(i) == attackOriginHex)
{
hexes.erase(hexes.begin() + i);
hexes.erase(i);
i = 0;
}
}
@@ -1439,10 +1433,10 @@ AttackableTiles CBattleInfoCallback::getPotentiallyShootableHexes(const battle::
AttackableTiles at;
RETURN_IF_NOT_BATTLE(at);
if(attacker->hasBonusOfType(BonusType::SHOOTS_ALL_ADJACENT) && !vstd::contains(attackerPos.neighbouringTiles(), destinationTile))
if(attacker->hasBonusOfType(BonusType::SHOOTS_ALL_ADJACENT) && !BattleHexArray::generateNeighbouringTiles(attackerPos).contains(destinationTile))
{
std::vector<BattleHex> targetHexes = destinationTile.neighbouringTiles();
targetHexes.push_back(destinationTile);
auto targetHexes = BattleHexArray::generateNeighbouringTiles(destinationTile);
targetHexes.insert(destinationTile);
boost::copy(targetHexes, vstd::set_inserter(at.hostileCreaturePositions));
}
@@ -1480,9 +1474,9 @@ std::vector<const battle::Unit*> CBattleInfoCallback::getAttackedBattleUnits(
for (BattleHex hex : battle::Unit::getHexes(unit->getPosition(), unit->doubleWide(), unit->unitSide()))
{
if (vstd::contains(at.hostileCreaturePositions, hex))
if (at.hostileCreaturePositions.contains(hex))
return true;
if (vstd::contains(at.friendlyCreaturePositions, hex))
if (at.friendlyCreaturePositions.contains(hex))
return true;
}
return false;
@@ -1671,15 +1665,15 @@ bool CBattleInfoCallback::isWallPartAttackable(EWallPart wallPart) const
return false;
}
std::vector<BattleHex> CBattleInfoCallback::getAttackableBattleHexes() const
BattleHexArray CBattleInfoCallback::getAttackableBattleHexes() const
{
std::vector<BattleHex> attackableBattleHexes;
BattleHexArray attackableBattleHexes;
RETURN_IF_NOT_BATTLE(attackableBattleHexes);
for(const auto & wallPartPair : wallParts)
{
if(isWallPartAttackable(wallPartPair.second))
attackableBattleHexes.emplace_back(wallPartPair.first);
attackableBattleHexes.insert(wallPartPair.first);
}
return attackableBattleHexes;

View File

@@ -38,8 +38,8 @@ namespace spells
struct DLL_LINKAGE AttackableTiles
{
std::set<BattleHex> hostileCreaturePositions;
std::set<BattleHex> friendlyCreaturePositions; //for Dragon Breath
BattleHexArray hostileCreaturePositions;
BattleHexArray friendlyCreaturePositions; //for Dragon Breath
template <typename Handler> void serialize(Handler &h)
{
h & hostileCreaturePositions;
@@ -59,9 +59,9 @@ public:
std::optional<BattleSide> battleIsFinished() const override; //return none if battle is ongoing; otherwise the victorious side (0/1) or 2 if it is a draw
std::vector<std::shared_ptr<const CObstacleInstance>> battleGetAllObstaclesOnPos(BattleHex tile, bool onlyBlocking = true) const override;
std::vector<std::shared_ptr<const CObstacleInstance>> getAllAffectedObstaclesByStack(const battle::Unit * unit, const std::set<BattleHex> & passed) const override;
std::vector<std::shared_ptr<const CObstacleInstance>> getAllAffectedObstaclesByStack(const battle::Unit * unit, const BattleHexArray & passed) const override;
//Handle obstacle damage here, requires SpellCastEnvironment
bool handleObstacleTriggersForUnit(SpellCastEnvironment & spellEnv, const battle::Unit & unit, const std::set<BattleHex> & passed = {}) const;
bool handleObstacleTriggersForUnit(SpellCastEnvironment & spellEnv, const battle::Unit & unit, const BattleHexArray & passed = {}) const;
const CStack * battleGetStackByPos(BattleHex pos, bool onlyAlive = true) const;
@@ -75,20 +75,20 @@ public:
void battleGetTurnOrder(std::vector<battle::Units> & out, const size_t maxUnits, const int maxTurns, const int turn = 0, BattleSide lastMoved = BattleSide::NONE) const;
///returns reachable hexes (valid movement destinations), DOES contain stack current position
std::vector<BattleHex> battleGetAvailableHexes(const battle::Unit * unit, bool obtainMovementRange, bool addOccupiable, std::vector<BattleHex> * attackable) const;
BattleHexArray battleGetAvailableHexes(const battle::Unit * unit, bool obtainMovementRange, bool addOccupiable, BattleHexArray * attackable) const;
///returns reachable hexes (valid movement destinations), DOES contain stack current position (lite version)
std::vector<BattleHex> battleGetAvailableHexes(const battle::Unit * unit, bool obtainMovementRange) const;
BattleHexArray battleGetAvailableHexes(const battle::Unit * unit, bool obtainMovementRange) const;
std::vector<BattleHex> battleGetAvailableHexes(const ReachabilityInfo & cache, const battle::Unit * unit, bool obtainMovementRange) const;
BattleHexArray battleGetAvailableHexes(const ReachabilityInfo & cache, const battle::Unit * unit, bool obtainMovementRange) const;
int battleGetSurrenderCost(const PlayerColor & Player) const; //returns cost of surrendering battle, -1 if surrendering is not possible
ReachabilityInfo::TDistances battleGetDistances(const battle::Unit * unit, BattleHex assumedPosition) const;
std::set<BattleHex> battleGetAttackedHexes(const battle::Unit * attacker, BattleHex destinationTile, BattleHex attackerPos = BattleHex::INVALID) const;
BattleHexArray battleGetAttackedHexes(const battle::Unit * attacker, BattleHex destinationTile, BattleHex attackerPos = BattleHex::INVALID) const;
bool isEnemyUnitWithinSpecifiedRange(BattleHex attackerPosition, const battle::Unit * defenderUnit, unsigned int range) const;
bool isHexWithinSpecifiedRange(BattleHex attackerPosition, BattleHex targetPosition, unsigned int range) const;
std::pair< std::vector<BattleHex>, int > getPath(BattleHex start, BattleHex dest, const battle::Unit * stack) const;
std::pair< BattleHexArray, int > getPath(BattleHex start, BattleHex dest, const battle::Unit * stack) const;
bool battleCanTargetEmptyHex(const battle::Unit * attacker) const; //determines of stack with given ID can target empty hex to attack - currently used only for SPELL_LIKE_ATTACK shooting
bool battleCanAttack(const battle::Unit * stack, const battle::Unit * target, BattleHex dest) const; //determines if stack with given ID can attack target at the selected destination
@@ -116,7 +116,7 @@ public:
EWallPart battleHexToWallPart(BattleHex hex) const; //returns part of destructible wall / gate / keep under given hex or -1 if not found
bool isWallPartPotentiallyAttackable(EWallPart wallPart) const; // returns true if the wall part is potentially attackable (independent of wall state), false if not
bool isWallPartAttackable(EWallPart wallPart) const; // returns true if the wall part is actually attackable, false if not
std::vector<BattleHex> getAttackableBattleHexes() const;
BattleHexArray getAttackableBattleHexes() const;
si8 battleMinSpellLevel(BattleSide side) const; //calculates maximum spell level possible to be cast on battlefield - takes into account artifacts of both heroes; if no effects are set, 0 is returned
si8 battleMaxSpellLevel(BattleSide side) const; //calculates minimum spell level possible to be cast on battlefield - takes into account artifacts of both heroes; if no effects are set, 0 is returned
@@ -162,15 +162,15 @@ public:
ReachabilityInfo getReachability(const ReachabilityInfo::Parameters & params) const;
AccessibilityInfo getAccessibility() const;
AccessibilityInfo getAccessibility(const battle::Unit * stack) const; //Hexes occupied by stack will be marked as accessible.
AccessibilityInfo getAccessibility(const std::vector<BattleHex> & accessibleHexes) const; //given hexes will be marked as accessible
AccessibilityInfo getAccessibility(const BattleHexArray & accessibleHexes) const; //given hexes will be marked as accessible
std::pair<const battle::Unit *, BattleHex> getNearestStack(const battle::Unit * closest) const;
BattleHex getAvailableHex(const CreatureID & creID, BattleSide side, int initialPos = -1) const; //find place for adding new stack
protected:
ReachabilityInfo getFlyingReachability(const ReachabilityInfo::Parameters & params) const;
ReachabilityInfo makeBFS(const AccessibilityInfo & accessibility, const ReachabilityInfo::Parameters & params) const;
bool isInObstacle(BattleHex hex, const std::set<BattleHex> & obstacles, const ReachabilityInfo::Parameters & params) const;
std::set<BattleHex> getStoppers(BattleSide whichSidePerspective) const; //get hexes with stopping obstacles (quicksands)
bool isInObstacle(BattleHex hex, const BattleHexArray & obstacles, const ReachabilityInfo::Parameters & params) const;
BattleHexArray getStoppers(BattleSide whichSidePerspective) const; //get hexes with stopping obstacles (quicksands)
};
VCMI_LIB_NAMESPACE_END
VCMI_LIB_NAMESPACE_END

View File

@@ -24,21 +24,21 @@ const ObstacleInfo & CObstacleInstance::getInfo() const
return *Obstacle(ID).getInfo();
}
std::vector<BattleHex> CObstacleInstance::getBlockedTiles() const
BattleHexArray CObstacleInstance::getBlockedTiles() const
{
if(blocksTiles())
return getAffectedTiles();
return std::vector<BattleHex>();
return BattleHexArray();
}
std::vector<BattleHex> CObstacleInstance::getStoppingTile() const
BattleHexArray CObstacleInstance::getStoppingTile() const
{
if(stopsMovement())
return getAffectedTiles();
return std::vector<BattleHex>();
return BattleHexArray();
}
std::vector<BattleHex> CObstacleInstance::getAffectedTiles() const
BattleHexArray CObstacleInstance::getAffectedTiles() const
{
switch(obstacleType)
{
@@ -47,7 +47,7 @@ std::vector<BattleHex> CObstacleInstance::getAffectedTiles() const
return getInfo().getBlocked(pos);
default:
assert(0);
return std::vector<BattleHex>();
return BattleHexArray();
}
}
@@ -215,12 +215,16 @@ void SpellCreatedObstacle::serializeJson(JsonSerializeFormat & handler)
JsonArraySerializer customSizeJson = handler.enterArray("customSize");
customSizeJson.syncSize(customSize, JsonNode::JsonType::DATA_INTEGER);
BattleHex hex;
for(size_t index = 0; index < customSizeJson.size(); index++)
customSizeJson.serializeInt(index, customSize.at(index));
{
customSizeJson.serializeInt(index, hex);
customSize.set(index, hex);
}
}
}
std::vector<BattleHex> SpellCreatedObstacle::getAffectedTiles() const
BattleHexArray SpellCreatedObstacle::getAffectedTiles() const
{
return customSize;
}
@@ -258,4 +262,4 @@ int SpellCreatedObstacle::getAnimationYOffset(int imageHeight) const
return offset;
}
VCMI_LIB_NAMESPACE_END
VCMI_LIB_NAMESPACE_END

View File

@@ -8,7 +8,7 @@
*
*/
#pragma once
#include "BattleHex.h"
#include "BattleHexArray.h"
#include "../constants/EntityIdentifiers.h"
#include "../filesystem/ResourcePath.h"
@@ -39,8 +39,8 @@ struct DLL_LINKAGE CObstacleInstance : public Serializeable
const ObstacleInfo &getInfo() const; //allowed only when not generated by spell (usual or absolute)
std::vector<BattleHex> getBlockedTiles() const;
std::vector<BattleHex> getStoppingTile() const; //hexes that will stop stack move
BattleHexArray getBlockedTiles() const;
BattleHexArray getStoppingTile() const; //hexes that will stop stack move
//The two functions below describe how the obstacle affects affected tiles
//additional effects (like hurting stack or disappearing) are hardcoded for appropriate obstacleTypes
@@ -49,7 +49,7 @@ struct DLL_LINKAGE CObstacleInstance : public Serializeable
virtual bool triggersEffects() const;
virtual SpellID getTrigger() const;
virtual std::vector<BattleHex> getAffectedTiles() const;
virtual BattleHexArray getAffectedTiles() const;
virtual bool visibleForSide(BattleSide side, bool hasNativeStack) const; //0 attacker
virtual void battleTurnPassed(){};
@@ -97,11 +97,11 @@ struct DLL_LINKAGE SpellCreatedObstacle : CObstacleInstance
int animationYOffset;
std::vector<BattleHex> customSize;
BattleHexArray customSize;
SpellCreatedObstacle();
std::vector<BattleHex> getAffectedTiles() const override;
BattleHexArray getAffectedTiles() const override;
bool visibleForSide(BattleSide side, bool hasNativeStack) const override;
bool blocksTiles() const override;

View File

@@ -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 "BattleHex.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

View File

@@ -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 "BattleHex.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 std::set<BattleHex> & 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

View File

@@ -1,91 +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 std::vector<BattleHex> & targetHexes,
BattleHex * chosenHex) const
{
uint32_t ret = 1000000;
for(auto targetHex : targetHexes)
{
for(auto & n : targetHex.neighbouringTiles())
{
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
vstd::concatenate(attackableHexes, battle::Unit::getHexes(defender->occupiedHex(), true, defender->unitSide()));
}
else
{
vstd::concatenate(attackableHexes, battle::Unit::getHexes(defender->getPosition(), true, defender->unitSide()));
}
}
vstd::removeDuplicates(attackableHexes);
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::generateNeighbouringTiles(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

View File

@@ -8,12 +8,14 @@
*
*/
#pragma once
#include "BattleHex.h"
#include "BattleHexArray.h"
#include "CBattleInfoEssentials.h"
#include "AccessibilityInfo.h"
VCMI_LIB_NAMESPACE_BEGIN
class BattleHexArray;
// Reachability info is result of BFS calculation. It's dependent on stack (it's owner, whether it's flying),
// startPosition and perspective.
struct DLL_LINKAGE ReachabilityInfo
@@ -30,7 +32,7 @@ struct DLL_LINKAGE ReachabilityInfo
bool flying = false;
bool ignoreKnownAccessible = false; //Ignore obstacles if it is in accessible hexes
bool bypassEnemyStacks = false; // in case of true will count amount of turns needed to kill enemy and thus move forward
std::vector<BattleHex> knownAccessible; //hexes that will be treated as accessible, even if they're occupied by stack (by default - tiles occupied by stack we do reachability for, so it doesn't block itself)
BattleHexArray knownAccessible; //hexes that will be treated as accessible, even if they're occupied by stack (by default - tiles occupied by stack we do reachability for, so it doesn't block itself)
std::map<BattleHex, ui8> destructibleEnemyTurns; // hom many turns it is needed to kill enemy on specific hex
BattleHex startPosition; //assumed position of stack
@@ -50,7 +52,7 @@ struct DLL_LINKAGE ReachabilityInfo
bool isReachable(BattleHex hex) const;
uint32_t distToNearestNeighbour(
const std::vector<BattleHex> & targetHexes,
const BattleHexArray & targetHexes,
BattleHex * chosenHex = nullptr) const;
uint32_t distToNearestNeighbour(

View File

@@ -1,242 +1,240 @@
/*
* Unit.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 "Unit.h"
#include "../VCMI_Lib.h"
#include "../texts/CGeneralTextHandler.h"
#include "../serializer/JsonDeserializer.h"
#include "../serializer/JsonSerializer.h"
#include <vcmi/Faction.h>
#include <vcmi/FactionService.h>
VCMI_LIB_NAMESPACE_BEGIN
namespace battle
{
///Unit
Unit::~Unit() = default;
bool Unit::isDead() const
{
return !alive() && !isGhost();
}
bool Unit::isTurret() const
{
return creatureIndex() == CreatureID::ARROW_TOWERS;
}
std::string Unit::getDescription() const
{
boost::format fmt("Unit %d of side %d");
fmt % unitId() % static_cast<int>(unitSide());
return fmt.str();
}
//TODO: deduplicate these functions
const IBonusBearer* Unit::getBonusBearer() const
{
return this;
}
std::vector<BattleHex> Unit::getSurroundingHexes(BattleHex assumedPosition) const
{
BattleHex hex = (assumedPosition != BattleHex::INVALID) ? assumedPosition : getPosition(); //use hypothetical position
return getSurroundingHexes(hex, doubleWide(), unitSide());
}
std::vector<BattleHex> Unit::getSurroundingHexes(BattleHex position, bool twoHex, BattleSide side)
{
std::vector<BattleHex> hexes;
if(twoHex)
{
const BattleHex otherHex = occupiedHex(position, twoHex, side);
if(side == BattleSide::ATTACKER)
{
for(auto dir = static_cast<BattleHex::EDir>(0); dir <= static_cast<BattleHex::EDir>(4); dir = static_cast<BattleHex::EDir>(dir + 1))
BattleHex::checkAndPush(position.cloneInDirection(dir, false), hexes);
BattleHex::checkAndPush(otherHex.cloneInDirection(BattleHex::EDir::BOTTOM_LEFT, false), hexes);
BattleHex::checkAndPush(otherHex.cloneInDirection(BattleHex::EDir::LEFT, false), hexes);
BattleHex::checkAndPush(otherHex.cloneInDirection(BattleHex::EDir::TOP_LEFT, false), hexes);
}
else
{
BattleHex::checkAndPush(position.cloneInDirection(BattleHex::EDir::TOP_LEFT, false), hexes);
for(auto dir = static_cast<BattleHex::EDir>(0); dir <= static_cast<BattleHex::EDir>(4); dir = static_cast<BattleHex::EDir>(dir + 1))
BattleHex::checkAndPush(otherHex.cloneInDirection(dir, false), hexes);
BattleHex::checkAndPush(position.cloneInDirection(BattleHex::EDir::BOTTOM_LEFT, false), hexes);
BattleHex::checkAndPush(position.cloneInDirection(BattleHex::EDir::LEFT, false), hexes);
}
return hexes;
}
else
{
return position.neighbouringTiles();
}
}
std::vector<BattleHex> Unit::getAttackableHexes(const Unit * attacker) const
{
auto defenderHexes = battle::Unit::getHexes(
getPosition(),
doubleWide(),
unitSide());
std::vector<BattleHex> targetableHexes;
for(auto defenderHex : defenderHexes)
{
auto hexes = battle::Unit::getHexes(
defenderHex,
attacker->doubleWide(),
unitSide());
if(hexes.size() == 2 && BattleHex::getDistance(hexes.front(), hexes.back()) != 1)
hexes.pop_back();
for(auto hex : hexes)
vstd::concatenate(targetableHexes, hex.neighbouringTiles());
}
vstd::removeDuplicates(targetableHexes);
return targetableHexes;
}
bool Unit::coversPos(BattleHex pos) const
{
return getPosition() == pos || (doubleWide() && (occupiedHex() == pos));
}
std::vector<BattleHex> Unit::getHexes() const
{
return getHexes(getPosition(), doubleWide(), unitSide());
}
std::vector<BattleHex> Unit::getHexes(BattleHex assumedPos) const
{
return getHexes(assumedPos, doubleWide(), unitSide());
}
std::vector<BattleHex> Unit::getHexes(BattleHex assumedPos, bool twoHex, BattleSide side)
{
std::vector<BattleHex> hexes;
hexes.push_back(assumedPos);
if(twoHex)
hexes.push_back(occupiedHex(assumedPos, twoHex, side));
return hexes;
}
BattleHex Unit::occupiedHex() const
{
return occupiedHex(getPosition(), doubleWide(), unitSide());
}
BattleHex Unit::occupiedHex(BattleHex assumedPos) const
{
return occupiedHex(assumedPos, doubleWide(), unitSide());
}
BattleHex Unit::occupiedHex(BattleHex assumedPos, bool twoHex, BattleSide side)
{
if(twoHex)
{
if(side == BattleSide::ATTACKER)
return assumedPos - 1;
else
return assumedPos + 1;
}
else
{
return BattleHex::INVALID;
}
}
void Unit::addText(MetaString & text, EMetaText type, int32_t serial, const boost::logic::tribool & plural) const
{
if(boost::logic::indeterminate(plural))
serial = VLC->generaltexth->pluralText(serial, getCount());
else if(plural)
serial = VLC->generaltexth->pluralText(serial, 2);
else
serial = VLC->generaltexth->pluralText(serial, 1);
text.appendLocalString(type, serial);
}
void Unit::addNameReplacement(MetaString & text, const boost::logic::tribool & plural) const
{
if(boost::logic::indeterminate(plural))
text.replaceName(creatureId(), getCount());
else if(plural)
text.replaceNamePlural(creatureIndex());
else
text.replaceNameSingular(creatureIndex());
}
std::string Unit::formatGeneralMessage(const int32_t baseTextId) const
{
const int32_t textId = VLC->generaltexth->pluralText(baseTextId, getCount());
MetaString text;
text.appendLocalString(EMetaText::GENERAL_TXT, textId);
text.replaceName(creatureId(), getCount());
return text.toString();
}
int Unit::getRawSurrenderCost() const
{
//we pay for our stack that comes from our army slots - condition eliminates summoned cres and war machines
if(unitSlot().validSlot())
return creatureCost() * getCount();
else
return 0;
}
///UnitInfo
void UnitInfo::serializeJson(JsonSerializeFormat & handler)
{
handler.serializeInt("count", count);
handler.serializeId("type", type, CreatureID(CreatureID::NONE));
handler.serializeInt("side", side);
handler.serializeInt("position", position);
handler.serializeBool("summoned", summoned);
}
void UnitInfo::save(JsonNode & data)
{
data.clear();
JsonSerializer ser(nullptr, data);
ser.serializeStruct("newUnitInfo", *this);
}
void UnitInfo::load(uint32_t id_, const JsonNode & data)
{
id = id_;
JsonDeserializer deser(nullptr, data);
deser.serializeStruct("newUnitInfo", *this);
}
}
VCMI_LIB_NAMESPACE_END
/*
* Unit.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 "Unit.h"
#include "../VCMI_Lib.h"
#include "../texts/CGeneralTextHandler.h"
#include "../serializer/JsonDeserializer.h"
#include "../serializer/JsonSerializer.h"
#include <vcmi/Faction.h>
#include <vcmi/FactionService.h>
VCMI_LIB_NAMESPACE_BEGIN
namespace battle
{
///Unit
Unit::~Unit() = default;
bool Unit::isDead() const
{
return !alive() && !isGhost();
}
bool Unit::isTurret() const
{
return creatureIndex() == CreatureID::ARROW_TOWERS;
}
std::string Unit::getDescription() const
{
boost::format fmt("Unit %d of side %d");
fmt % unitId() % static_cast<int>(unitSide());
return fmt.str();
}
//TODO: deduplicate these functions
const IBonusBearer* Unit::getBonusBearer() const
{
return this;
}
BattleHexArray Unit::getSurroundingHexes(BattleHex assumedPosition) const
{
BattleHex hex = (assumedPosition != BattleHex::INVALID) ? assumedPosition : getPosition(); //use hypothetical position
return getSurroundingHexes(hex, doubleWide(), unitSide());
}
BattleHexArray Unit::getSurroundingHexes(BattleHex position, bool twoHex, BattleSide side)
{
BattleHexArray hexes;
if(twoHex)
{
const BattleHex otherHex = occupiedHex(position, twoHex, side);
if(side == BattleSide::ATTACKER)
{
for(auto dir = static_cast<BattleHex::EDir>(0); dir <= static_cast<BattleHex::EDir>(4); dir = static_cast<BattleHex::EDir>(dir + 1))
hexes.checkAndPush(position.cloneInDirection(dir, false));
hexes.checkAndPush(otherHex.cloneInDirection(BattleHex::EDir::BOTTOM_LEFT, false));
hexes.checkAndPush(otherHex.cloneInDirection(BattleHex::EDir::LEFT, false));
hexes.checkAndPush(otherHex.cloneInDirection(BattleHex::EDir::TOP_LEFT, false));
}
else
{
hexes.checkAndPush(position.cloneInDirection(BattleHex::EDir::TOP_LEFT, false));
for(auto dir = static_cast<BattleHex::EDir>(0); dir <= static_cast<BattleHex::EDir>(4); dir = static_cast<BattleHex::EDir>(dir + 1))
hexes.checkAndPush(otherHex.cloneInDirection(dir, false));
hexes.checkAndPush(position.cloneInDirection(BattleHex::EDir::BOTTOM_LEFT, false));
hexes.checkAndPush(position.cloneInDirection(BattleHex::EDir::LEFT, false));
}
return hexes;
}
else
{
return BattleHexArray::generateNeighbouringTiles(position);
}
}
BattleHexArray Unit::getAttackableHexes(const Unit * attacker) const
{
auto defenderHexes = battle::Unit::getHexes(
getPosition(),
doubleWide(),
unitSide());
BattleHexArray targetableHexes;
for(auto defenderHex : defenderHexes)
{
auto hexes = battle::Unit::getHexes(
defenderHex,
attacker->doubleWide(),
unitSide());
if(hexes.size() == 2 && BattleHex::getDistance(hexes.front(), hexes.back()) != 1)
hexes.pop_back();
for(auto hex : hexes)
targetableHexes.merge(BattleHexArray::generateNeighbouringTiles(hex));
}
return targetableHexes;
}
bool Unit::coversPos(BattleHex pos) const
{
return getPosition() == pos || (doubleWide() && (occupiedHex() == pos));
}
BattleHexArray Unit::getHexes() const
{
return getHexes(getPosition(), doubleWide(), unitSide());
}
BattleHexArray Unit::getHexes(BattleHex assumedPos) const
{
return getHexes(assumedPos, doubleWide(), unitSide());
}
BattleHexArray Unit::getHexes(BattleHex assumedPos, bool twoHex, BattleSide side)
{
BattleHexArray hexes;
hexes.insert(assumedPos);
if(twoHex)
hexes.insert(occupiedHex(assumedPos, twoHex, side));
return hexes;
}
BattleHex Unit::occupiedHex() const
{
return occupiedHex(getPosition(), doubleWide(), unitSide());
}
BattleHex Unit::occupiedHex(BattleHex assumedPos) const
{
return occupiedHex(assumedPos, doubleWide(), unitSide());
}
BattleHex Unit::occupiedHex(BattleHex assumedPos, bool twoHex, BattleSide side)
{
if(twoHex)
{
if(side == BattleSide::ATTACKER)
return assumedPos - 1;
else
return assumedPos + 1;
}
else
{
return BattleHex::INVALID;
}
}
void Unit::addText(MetaString & text, EMetaText type, int32_t serial, const boost::logic::tribool & plural) const
{
if(boost::logic::indeterminate(plural))
serial = VLC->generaltexth->pluralText(serial, getCount());
else if(plural)
serial = VLC->generaltexth->pluralText(serial, 2);
else
serial = VLC->generaltexth->pluralText(serial, 1);
text.appendLocalString(type, serial);
}
void Unit::addNameReplacement(MetaString & text, const boost::logic::tribool & plural) const
{
if(boost::logic::indeterminate(plural))
text.replaceName(creatureId(), getCount());
else if(plural)
text.replaceNamePlural(creatureIndex());
else
text.replaceNameSingular(creatureIndex());
}
std::string Unit::formatGeneralMessage(const int32_t baseTextId) const
{
const int32_t textId = VLC->generaltexth->pluralText(baseTextId, getCount());
MetaString text;
text.appendLocalString(EMetaText::GENERAL_TXT, textId);
text.replaceName(creatureId(), getCount());
return text.toString();
}
int Unit::getRawSurrenderCost() const
{
//we pay for our stack that comes from our army slots - condition eliminates summoned cres and war machines
if(unitSlot().validSlot())
return creatureCost() * getCount();
else
return 0;
}
///UnitInfo
void UnitInfo::serializeJson(JsonSerializeFormat & handler)
{
handler.serializeInt("count", count);
handler.serializeId("type", type, CreatureID(CreatureID::NONE));
handler.serializeInt("side", side);
handler.serializeInt("position", position);
handler.serializeBool("summoned", summoned);
}
void UnitInfo::save(JsonNode & data)
{
data.clear();
JsonSerializer ser(nullptr, data);
ser.serializeStruct("newUnitInfo", *this);
}
void UnitInfo::load(uint32_t id_, const JsonNode & data)
{
id = id_;
JsonDeserializer deser(nullptr, data);
deser.serializeStruct("newUnitInfo", *this);
}
}
VCMI_LIB_NAMESPACE_END

View File

@@ -17,7 +17,7 @@
#include "../bonuses/IBonusBearer.h"
#include "IUnitInfo.h"
#include "BattleHex.h"
#include "BattleHexArray.h"
VCMI_LIB_NAMESPACE_BEGIN
@@ -84,14 +84,11 @@ public:
bool isTurret() const;
virtual bool isValidTarget(bool allowDead = false) const = 0; //non-turret non-ghost stacks (can be attacked or be object of magic effect)
virtual bool isHypnotized() const = 0;
virtual bool isClone() const = 0;
virtual bool hasClone() const = 0;
virtual bool canCast() const = 0;
virtual bool isCaster() const = 0;
virtual bool canShootBlocked() const = 0;
virtual bool canShoot() const = 0;
virtual bool isShooter() const = 0;
@@ -115,6 +112,8 @@ public:
virtual BattleHex getPosition() const = 0;
virtual void setPosition(BattleHex hex) = 0;
virtual int32_t getInitiative(int turn = 0) const = 0;
virtual bool canMove(int turn = 0) const = 0; //if stack can move
virtual bool defended(int turn = 0) const = 0;
virtual bool moved(int turn = 0) const = 0; //if stack was already moved this turn
@@ -128,15 +127,15 @@ public:
virtual std::string getDescription() const;
std::vector<BattleHex> getSurroundingHexes(BattleHex assumedPosition = BattleHex::INVALID) const; // get six or 8 surrounding hexes depending on creature size
std::vector<BattleHex> getAttackableHexes(const Unit * attacker) const;
static std::vector<BattleHex> getSurroundingHexes(BattleHex position, bool twoHex, BattleSide side);
BattleHexArray getSurroundingHexes(BattleHex assumedPosition = BattleHex::INVALID) const; // get six or 8 surrounding hexes depending on creature size
BattleHexArray getAttackableHexes(const Unit * attacker) const;
static BattleHexArray getSurroundingHexes(BattleHex position, bool twoHex, BattleSide side);
bool coversPos(BattleHex position) const; //checks also if unit is double-wide
std::vector<BattleHex> getHexes() const; //up to two occupied hexes, starting from front
std::vector<BattleHex> getHexes(BattleHex assumedPos) const; //up to two occupied hexes, starting from front
static std::vector<BattleHex> getHexes(BattleHex assumedPos, bool twoHex, BattleSide side);
BattleHexArray getHexes() const; //up to two occupied hexes, starting from front
BattleHexArray getHexes(BattleHex assumedPos) const; //up to two occupied hexes, starting from front
static BattleHexArray getHexes(BattleHex assumedPos, bool twoHex, BattleSide side);
BattleHex occupiedHex() const; //returns number of occupied hex (not the position) if stack is double wide; otherwise -1
BattleHex occupiedHex(BattleHex assumedPos) const; //returns number of occupied hex (not the position) if stack is double wide and would stand on assumedPos; otherwise -1

View File

@@ -142,7 +142,7 @@ class JsonNode;
BONUS_NAME(WIDE_BREATH) /* initial desigh: dragon breath affecting multiple nearby hexes */\
BONUS_NAME(FIRST_STRIKE) /* first counterattack, then attack if possible */\
BONUS_NAME(SYNERGY_TARGET) /* dummy skill for alternative upgrades mod */\
BONUS_NAME(SHOOTS_ALL_ADJACENT) /* H4 Cyclops-like shoot (attacks all hexes neighboring with target) without spell-like mechanics */\
BONUS_NAME(SHOOTS_ALL_ADJACENT) /* H4 Cyclops-like shoot (attacks all hexes neighbouring with target) without spell-like mechanics */\
BONUS_NAME(BLOCK_MAGIC_BELOW) /*blocks casting spells of the level < value */ \
BONUS_NAME(DESTRUCTION) /*kills extra units after hit, subtype = 0 - kill percentage of units, 1 - kill amount, val = chance in percent to trigger, additional info - amount/percentage to kill*/ \
BONUS_NAME(SPECIAL_CRYSTAL_GENERATION) /*crystal dragon crystal generation*/ \

View File

@@ -226,7 +226,7 @@ ILimiter::EDecision UnitOnHexLimiter::limit(const BonusLimitationContext &contex
return accept ? ILimiter::EDecision::ACCEPT : ILimiter::EDecision::DISCARD;
}
UnitOnHexLimiter::UnitOnHexLimiter(const std::set<BattleHex> & applicableHexes):
UnitOnHexLimiter::UnitOnHexLimiter(const BattleHexArray & applicableHexes):
applicableHexes(applicableHexes)
{
}

View File

@@ -263,9 +263,9 @@ public:
class DLL_LINKAGE UnitOnHexLimiter : public ILimiter //works only on selected hexes
{
public:
std::set<BattleHex> applicableHexes;
BattleHexArray applicableHexes;
UnitOnHexLimiter(const std::set<BattleHex> & applicableHexes = {});
UnitOnHexLimiter(const BattleHexArray & applicableHexes = {});
EDecision limit(const BonusLimitationContext &context) const override;
JsonNode toJsonNode() const override;

View File

@@ -278,7 +278,7 @@ CGHeroInstance * CMap::getHero(HeroTypeID heroID)
bool CMap::isCoastalTile(const int3 & pos) const
{
//todo: refactoring: extract neighbor tile iterator and use it in GameState
//todo: refactoring: extract neighbour tile iterator and use it in GameState
static const int3 dirs[] = { int3(0,1,0),int3(0,-1,0),int3(-1,0,0),int3(+1,0,0),
int3(1,1,0),int3(-1,1,0),int3(1,-1,0),int3(-1,-1,0) };

View File

@@ -22,6 +22,7 @@ class CGHeroInstance;
class CArmedInstance;
class IBattleState;
class BattleInfo;
class BattleHexArray;
struct DLL_LINKAGE BattleStart : public CPackForClient
{
@@ -170,10 +171,10 @@ struct DLL_LINKAGE BattleStackMoved : public CPackForClient
{
BattleID battleID = BattleID::NONE;
ui32 stack = 0;
std::vector<BattleHex> tilesToMove;
BattleHexArray tilesToMove;
int distance = 0;
bool teleporting = false;
void applyGs(CGameState * gs) override;
void applyBattle(IBattleState * battleState);

View File

@@ -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 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
/*
* 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

File diff suppressed because it is too large Load Diff

View File

@@ -172,4 +172,4 @@ bool ObstaclePlacer::isProhibited(const rmg::Area & objArea) const
return false;
}
VCMI_LIB_NAMESPACE_END
VCMI_LIB_NAMESPACE_END

View File

@@ -13,6 +13,7 @@
#include "SerializerReflection.h"
#include "ESerializationVersion.h"
#include "../mapObjects/CGHeroInstance.h"
#include "../battle/BattleHexArray.h"
VCMI_LIB_NAMESPACE_BEGIN
@@ -442,6 +443,19 @@ 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))

View File

@@ -15,6 +15,7 @@
#include "ESerializationVersion.h"
#include "Serializeable.h"
#include "../mapObjects/CArmedInstance.h"
#include "../battle/BattleHexArray.h"
VCMI_LIB_NAMESPACE_BEGIN

View File

@@ -508,11 +508,11 @@ bool BattleSpellMechanics::counteringSelector(const Bonus * bonus) const
return false;
}
std::set<BattleHex> BattleSpellMechanics::spellRangeInHexes(BattleHex centralHex) const
BattleHexArray BattleSpellMechanics::spellRangeInHexes(BattleHex centralHex) const
{
using namespace SRSLPraserHelpers;
std::set<BattleHex> ret;
BattleHexArray ret;
std::vector<int> rng = owner->getLevelInfo(getRangeLevel()).range;
for(auto & elem : rng)
@@ -604,13 +604,13 @@ std::vector<Destination> BattleSpellMechanics::getPossibleDestinations(size_t in
if(fast)
{
auto stacks = battle()->battleGetAllStacks();
std::set<BattleHex> hexesToCheck;
BattleHexArray hexesToCheck;
for(auto stack : stacks)
{
hexesToCheck.insert(stack->getPosition());
for(auto adjacent : stack->getPosition().neighbouringTiles())
for(auto adjacent : BattleHexArray::generateNeighbouringTiles(stack->getPosition()))
hexesToCheck.insert(adjacent);
}
@@ -661,17 +661,17 @@ bool BattleSpellMechanics::isReceptive(const battle::Unit * target) const
return targetCondition->isReceptive(this, target);
}
std::vector<BattleHex> BattleSpellMechanics::rangeInHexes(BattleHex centralHex) const
BattleHexArray BattleSpellMechanics::rangeInHexes(BattleHex centralHex) const
{
if(isMassive() || !centralHex.isValid())
return std::vector<BattleHex>(1, BattleHex::INVALID);
return BattleHexArray();
Target aimPoint;
aimPoint.push_back(Destination(centralHex));
Target spellTarget = transformSpellTarget(aimPoint);
std::set<BattleHex> effectRange;
BattleHexArray effectRange;
effects->forEachEffect(getEffectLevel(), [&](const effects::Effect * effect, bool & stop)
{
@@ -681,12 +681,7 @@ std::vector<BattleHex> BattleSpellMechanics::rangeInHexes(BattleHex centralHex)
}
});
std::vector<BattleHex> ret;
ret.reserve(effectRange.size());
std::copy(effectRange.begin(), effectRange.end(), std::back_inserter(ret));
return ret;
return effectRange;
}
const Spell * BattleSpellMechanics::getSpell() const

View File

@@ -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
std::vector<BattleHex> 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);
std::set<BattleHex> 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

View File

@@ -357,7 +357,7 @@ int32_t CSpell::getLevelPower(const int32_t skillLevel) const
si32 CSpell::getProbability(const FactionID & factionId) const
{
if(!vstd::contains(probabilities,factionId))
if(!vstd::contains(probabilities, factionId))
{
return defaultProbability;
}
@@ -701,7 +701,7 @@ const std::vector<std::string> & CSpellHandler::getTypeNames() const
std::vector<int> CSpellHandler::spellRangeInHexes(std::string input) const
{
std::set<BattleHex> ret;
BattleHexArray ret;
std::string rng = input + ','; //copy + artificial comma for easier handling
if(rng.size() >= 2 && std::tolower(rng[0]) != 'x') //there is at least one hex in range (+artificial comma)

View File

@@ -17,7 +17,7 @@
#include "../ConstTransitivePtr.h"
#include "../int3.h"
#include "../GameConstants.h"
#include "../battle/BattleHex.h"
#include "../battle/BattleHexArray.h"
#include "../bonuses/Bonus.h"
#include "../filesystem/ResourcePath.h"
#include "../json/JsonNode.h"

View File

@@ -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 std::vector<BattleHex> 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

View File

@@ -1,82 +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 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(std::set<BattleHex> & 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

View File

@@ -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(std::set<BattleHex> & 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

View File

@@ -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(std::set<BattleHex> & 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

View File

@@ -32,7 +32,7 @@ namespace spells
namespace effects
{
static void serializeMoatHexes(JsonSerializeFormat & handler, const std::string & fieldName, std::vector<std::vector<BattleHex>> & moatHexes)
static void serializeMoatHexes(JsonSerializeFormat & handler, const std::string & fieldName, std::vector<BattleHexArray> & moatHexes)
{
{
JsonArraySerializer outer = handler.enterArray(fieldName);
@@ -43,8 +43,12 @@ static void serializeMoatHexes(JsonSerializeFormat & handler, const std::string
JsonArraySerializer inner = outer.enterArray(outerIndex);
inner.syncSize(moatHexes.at(outerIndex), JsonNode::JsonType::DATA_INTEGER);
BattleHex hex;
for(size_t innerIndex = 0; innerIndex < inner.size(); innerIndex++)
inner.serializeInt(innerIndex, moatHexes.at(outerIndex).at(innerIndex));
{
inner.serializeInt(innerIndex, hex);
moatHexes.at(outerIndex).set(innerIndex, hex);
}
}
}
}
@@ -96,10 +100,10 @@ void Moat::convertBonus(const Mechanics * m, std::vector<Bonus> & converted) con
nb.sid = BonusSourceID(m->getSpellId()); //for all
nb.source = BonusSource::SPELL_EFFECT;//for all
}
std::set<BattleHex> flatMoatHexes;
BattleHexArray flatMoatHexes;
for(const auto & moatPatch : moatHexes)
flatMoatHexes.insert(moatPatch.begin(), moatPatch.end());
flatMoatHexes.merge(moatPatch);
nb.limiter = std::make_shared<UnitOnHexLimiter>(std::move(flatMoatHexes));
converted.push_back(nb);
@@ -164,7 +168,7 @@ void Moat::placeObstacles(ServerCallback * server, const Mechanics * m, const Ef
obstacle.appearSound = sideOptions.appearSound; //For dispellable moats
obstacle.appearAnimation = sideOptions.appearAnimation; //For dispellable moats
obstacle.animation = sideOptions.animation;
obstacle.customSize.insert(obstacle.customSize.end(),destination.cbegin(), destination.cend());
obstacle.customSize.merge(destination);
obstacle.animationYOffset = sideOptions.offsetY;
pack.changes.emplace_back();
obstacle.toInfo(pack.changes.back());

View File

@@ -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<std::vector<BattleHex>> 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

View File

@@ -95,7 +95,7 @@ void ObstacleSideOptions::serializeJson(JsonSerializeFormat & handler)
handler.serializeInt("offsetY", offsetY);
}
void Obstacle::adjustAffectedHexes(std::set<BattleHex> & hexes, const Mechanics * m, const Target & spellTarget) const
void Obstacle::adjustAffectedHexes(BattleHexArray & hexes, const Mechanics * m, const Target & spellTarget) const
{
EffectTarget effectTarget = transformTarget(m, spellTarget, spellTarget);
@@ -180,11 +180,11 @@ void Obstacle::apply(ServerCallback * server, const Mechanics * m, const EffectT
{
if(patchCount > 0)
{
std::vector<BattleHex> availableTiles;
auto insertAvailable = [&m](const BattleHex & hex, std::vector<BattleHex> & availableTiles)
BattleHexArray availableTiles;
auto insertAvailable = [&m](const BattleHex & hex, BattleHexArray & availableTiles)
{
if(isHexAvailable(m->battle(), hex, true))
availableTiles.push_back(hex);
availableTiles.insert(hex);
};
if(m->isMassive())
@@ -309,7 +309,6 @@ void Obstacle::placeObstacles(ServerCallback * server, const Mechanics * m, cons
obstacle.animationYOffset = options.offsetY;
obstacle.customSize.clear();
obstacle.customSize.reserve(options.shape.size());
for(const auto & shape : options.shape)
{
@@ -318,7 +317,7 @@ void Obstacle::placeObstacles(ServerCallback * server, const Mechanics * m, cons
for(auto direction : shape)
hex.moveInDirection(direction, false);
obstacle.customSize.emplace_back(hex);
obstacle.customSize.insert(hex);
}
pack.changes.emplace_back();

View File

@@ -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/BattleHex.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(std::set<BattleHex> & 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

View File

@@ -28,7 +28,7 @@ namespace spells
namespace effects
{
void Summon::adjustAffectedHexes(std::set<BattleHex> & hexes, const Mechanics * m, const Target & spellTarget) const
void Summon::adjustAffectedHexes(BattleHexArray & hexes, const Mechanics * m, const Target & spellTarget) const
{
//no hexes affected
}
@@ -207,4 +207,4 @@ EffectTarget Summon::transformTarget(const Mechanics * m, const Target & aimPoin
}
}
VCMI_LIB_NAMESPACE_END
VCMI_LIB_NAMESPACE_END

View File

@@ -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(std::set<BattleHex> & 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

View File

@@ -81,8 +81,8 @@ void Teleport::apply(ServerCallback * server, const Mechanics * m, const EffectT
pack.battleID = m->battle()->getBattle()->getBattleID();
pack.distance = 0;
pack.stack = targetUnit->unitId();
std::vector<BattleHex> tiles;
tiles.push_back(destination);
BattleHexArray tiles;
tiles.insert(destination);
pack.tilesToMove = tiles;
pack.teleporting = true;
server->apply(pack);

View File

@@ -30,7 +30,7 @@ void UnitEffect::adjustTargetTypes(std::vector<TargetType> & types) const
}
void UnitEffect::adjustAffectedHexes(std::set<BattleHex> & hexes, const Mechanics * m, const Target & spellTarget) const
void UnitEffect::adjustAffectedHexes(BattleHexArray & hexes, const Mechanics * m, const Target & spellTarget) const
{
for(const auto & destnation : spellTarget)
hexes.insert(destnation.hexValue);
@@ -193,7 +193,7 @@ EffectTarget UnitEffect::transformTargetByChain(const Mechanics * m, const Targe
return EffectTarget();
}
std::set<BattleHex> possibleHexes;
BattleHexArray possibleHexes;
auto possibleTargets = m->battle()->battleGetUnitsIf([&](const battle::Unit * unit) -> bool
{
@@ -228,7 +228,7 @@ EffectTarget UnitEffect::transformTargetByChain(const Mechanics * m, const Targe
if(possibleHexes.empty())
break;
destHex = BattleHex::getClosestTile(unit->unitSide(), destHex, possibleHexes);
destHex = possibleHexes.getClosestTile(unit->unitSide(), destHex);
}
return effectTarget;
@@ -278,4 +278,4 @@ void UnitEffect::serializeJsonEffect(JsonSerializeFormat & handler)
}
}
VCMI_LIB_NAMESPACE_END
VCMI_LIB_NAMESPACE_END

View File

@@ -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(std::set<BattleHex> & 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

View File

@@ -635,7 +635,7 @@ int BattleActionProcessor::moveStack(const CBattleInfoCallback & battle, int sta
//initing necessary tables
auto accessibility = battle.getAccessibility(curStack);
std::set<BattleHex> passed;
BattleHexArray passed;
//Ignore obstacles on starting position
passed.insert(curStack->getPosition());
if(curStack->doubleWide())
@@ -665,7 +665,7 @@ int BattleActionProcessor::moveStack(const CBattleInfoCallback & battle, int sta
canUseGate = true;
}
std::pair< std::vector<BattleHex>, int > path = battle.getPath(start, dest, curStack);
std::pair< BattleHexArray, int > path = battle.getPath(start, dest, curStack);
ret = path.second;
@@ -723,8 +723,8 @@ int BattleActionProcessor::moveStack(const CBattleInfoCallback & battle, int sta
BattleStackMoved sm;
sm.battleID = battle.getBattle()->getBattleID();
sm.stack = curStack->unitId();
std::vector<BattleHex> tiles;
tiles.push_back(path.first[0]);
BattleHexArray tiles;
tiles.insert(path.first[0]);
sm.tilesToMove = tiles;
sm.distance = path.second;
sm.teleporting = false;
@@ -733,10 +733,10 @@ int BattleActionProcessor::moveStack(const CBattleInfoCallback & battle, int sta
}
else //for non-flying creatures
{
std::vector<BattleHex> tiles;
BattleHexArray tiles;
const int tilesToMove = std::max((int)(path.first.size() - creSpeed), 0);
int v = (int)path.first.size()-1;
path.first.push_back(start);
path.first.insert(start);
// check if gate need to be open or closed at some point
BattleHex openGateAtHex, gateMayCloseAtHex;
@@ -822,7 +822,7 @@ int BattleActionProcessor::moveStack(const CBattleInfoCallback & battle, int sta
for (bool obstacleHit = false; (!obstacleHit) && (!gateStateChanging) && (v >= tilesToMove); --v)
{
BattleHex hex = path.first[v];
tiles.push_back(hex);
tiles.insert(hex);
if ((openGateAtHex.isValid() && openGateAtHex == hex) ||
(gateMayCloseAtHex.isValid() && gateMayCloseAtHex == hex))

View File

@@ -36,7 +36,7 @@ BattleFlowProcessor::BattleFlowProcessor(BattleProcessor * owner, CGameHandler *
{
}
void BattleFlowProcessor::summonGuardiansHelper(const CBattleInfoCallback & battle, std::vector<BattleHex> & output, const BattleHex & targetPosition, BattleSide side, bool targetIsTwoHex) //return hexes for summoning two hex monsters in output, target = unit to guard
void BattleFlowProcessor::summonGuardiansHelper(const CBattleInfoCallback & battle, BattleHexArray & output, const BattleHex & targetPosition, BattleSide side, bool targetIsTwoHex) //return hexes for summoning two hex monsters in output, target = unit to guard
{
int x = targetPosition.getX();
int y = targetPosition.getY();
@@ -44,31 +44,31 @@ void BattleFlowProcessor::summonGuardiansHelper(const CBattleInfoCallback & batt
const bool targetIsAttacker = side == BattleSide::ATTACKER;
if (targetIsAttacker) //handle front guardians, TODO: should we handle situation when units start battle near opposite side of the battlefield? Cannot happen in normal H3...
BattleHex::checkAndPush(targetPosition.cloneInDirection(BattleHex::EDir::RIGHT, false).cloneInDirection(BattleHex::EDir::RIGHT, false), output);
output.checkAndPush(targetPosition.cloneInDirection(BattleHex::EDir::RIGHT, false).cloneInDirection(BattleHex::EDir::RIGHT, false));
else
BattleHex::checkAndPush(targetPosition.cloneInDirection(BattleHex::EDir::LEFT, false).cloneInDirection(BattleHex::EDir::LEFT, false), output);
output.checkAndPush(targetPosition.cloneInDirection(BattleHex::EDir::LEFT, false).cloneInDirection(BattleHex::EDir::LEFT, false));
//guardian spawn locations for four default position cases for attacker and defender, non-default starting location for att and def is handled in first two if's
if (targetIsAttacker && ((y % 2 == 0) || (x > 1)))
{
if (targetIsTwoHex && (y % 2 == 1) && (x == 2)) //handle exceptional case
{
BattleHex::checkAndPush(targetPosition.cloneInDirection(BattleHex::EDir::TOP_RIGHT, false), output);
BattleHex::checkAndPush(targetPosition.cloneInDirection(BattleHex::EDir::BOTTOM_RIGHT, false), output);
output.checkAndPush(targetPosition.cloneInDirection(BattleHex::EDir::TOP_RIGHT, false));
output.checkAndPush(targetPosition.cloneInDirection(BattleHex::EDir::BOTTOM_RIGHT, false));
}
else
{ //add back-side guardians for two-hex target, side guardians for one-hex
BattleHex::checkAndPush(targetPosition.cloneInDirection(targetIsTwoHex ? BattleHex::EDir::TOP_LEFT : BattleHex::EDir::TOP_RIGHT, false), output);
BattleHex::checkAndPush(targetPosition.cloneInDirection(targetIsTwoHex ? BattleHex::EDir::BOTTOM_LEFT : BattleHex::EDir::BOTTOM_RIGHT, false), output);
output.checkAndPush(targetPosition.cloneInDirection(targetIsTwoHex ? BattleHex::EDir::TOP_LEFT : BattleHex::EDir::TOP_RIGHT, false));
output.checkAndPush(targetPosition.cloneInDirection(targetIsTwoHex ? BattleHex::EDir::BOTTOM_LEFT : BattleHex::EDir::BOTTOM_RIGHT, false));
if (!targetIsTwoHex && x > 2) //back guard for one-hex
BattleHex::checkAndPush(targetPosition.cloneInDirection(BattleHex::EDir::LEFT, false), output);
output.checkAndPush(targetPosition.cloneInDirection(BattleHex::EDir::LEFT, false));
else if (targetIsTwoHex)//front-side guardians for two-hex target
{
BattleHex::checkAndPush(targetPosition.cloneInDirection(BattleHex::EDir::RIGHT, false).cloneInDirection(BattleHex::EDir::TOP_RIGHT, false), output);
BattleHex::checkAndPush(targetPosition.cloneInDirection(BattleHex::EDir::RIGHT, false).cloneInDirection(BattleHex::EDir::BOTTOM_RIGHT, false), output);
output.checkAndPush(targetPosition.cloneInDirection(BattleHex::EDir::RIGHT, false).cloneInDirection(BattleHex::EDir::TOP_RIGHT, false));
output.checkAndPush(targetPosition.cloneInDirection(BattleHex::EDir::RIGHT, false).cloneInDirection(BattleHex::EDir::BOTTOM_RIGHT, false));
if (x > 3) //back guard for two-hex
BattleHex::checkAndPush(targetPosition.cloneInDirection(BattleHex::EDir::LEFT, false).cloneInDirection(BattleHex::EDir::LEFT, false), output);
output.checkAndPush(targetPosition.cloneInDirection(BattleHex::EDir::LEFT, false).cloneInDirection(BattleHex::EDir::LEFT, false));
}
}
@@ -78,36 +78,36 @@ void BattleFlowProcessor::summonGuardiansHelper(const CBattleInfoCallback & batt
{
if (targetIsTwoHex && (y % 2 == 0) && (x == GameConstants::BFIELD_WIDTH - 3)) //handle exceptional case... equivalent for above for defender side
{
BattleHex::checkAndPush(targetPosition.cloneInDirection(BattleHex::EDir::TOP_LEFT, false), output);
BattleHex::checkAndPush(targetPosition.cloneInDirection(BattleHex::EDir::BOTTOM_LEFT, false), output);
output.checkAndPush(targetPosition.cloneInDirection(BattleHex::EDir::TOP_LEFT, false));
output.checkAndPush(targetPosition.cloneInDirection(BattleHex::EDir::BOTTOM_LEFT, false));
}
else
{
BattleHex::checkAndPush(targetPosition.cloneInDirection(targetIsTwoHex ? BattleHex::EDir::TOP_RIGHT : BattleHex::EDir::TOP_LEFT, false), output);
BattleHex::checkAndPush(targetPosition.cloneInDirection(targetIsTwoHex ? BattleHex::EDir::BOTTOM_RIGHT : BattleHex::EDir::BOTTOM_LEFT, false), output);
output.checkAndPush(targetPosition.cloneInDirection(targetIsTwoHex ? BattleHex::EDir::TOP_RIGHT : BattleHex::EDir::TOP_LEFT, false));
output.checkAndPush(targetPosition.cloneInDirection(targetIsTwoHex ? BattleHex::EDir::BOTTOM_RIGHT : BattleHex::EDir::BOTTOM_LEFT, false));
if (!targetIsTwoHex && x < GameConstants::BFIELD_WIDTH - 3)
BattleHex::checkAndPush(targetPosition.cloneInDirection(BattleHex::EDir::RIGHT, false), output);
output.checkAndPush(targetPosition.cloneInDirection(BattleHex::EDir::RIGHT, false));
else if (targetIsTwoHex)
{
BattleHex::checkAndPush(targetPosition.cloneInDirection(BattleHex::EDir::LEFT, false).cloneInDirection(BattleHex::EDir::TOP_LEFT, false), output);
BattleHex::checkAndPush(targetPosition.cloneInDirection(BattleHex::EDir::LEFT, false).cloneInDirection(BattleHex::EDir::BOTTOM_LEFT, false), output);
output.checkAndPush(targetPosition.cloneInDirection(BattleHex::EDir::LEFT, false).cloneInDirection(BattleHex::EDir::TOP_LEFT, false));
output.checkAndPush(targetPosition.cloneInDirection(BattleHex::EDir::LEFT, false).cloneInDirection(BattleHex::EDir::BOTTOM_LEFT, false));
if (x < GameConstants::BFIELD_WIDTH - 4)
BattleHex::checkAndPush(targetPosition.cloneInDirection(BattleHex::EDir::RIGHT, false).cloneInDirection(BattleHex::EDir::RIGHT, false), output);
output.checkAndPush(targetPosition.cloneInDirection(BattleHex::EDir::RIGHT, false).cloneInDirection(BattleHex::EDir::RIGHT, false));
}
}
}
else if (!targetIsAttacker && y % 2 == 0)
{
BattleHex::checkAndPush(targetPosition.cloneInDirection(BattleHex::EDir::LEFT, false).cloneInDirection(BattleHex::EDir::TOP_LEFT, false), output);
BattleHex::checkAndPush(targetPosition.cloneInDirection(BattleHex::EDir::LEFT, false).cloneInDirection(BattleHex::EDir::BOTTOM_LEFT, false), output);
output.checkAndPush(targetPosition.cloneInDirection(BattleHex::EDir::LEFT, false).cloneInDirection(BattleHex::EDir::TOP_LEFT, false));
output.checkAndPush(targetPosition.cloneInDirection(BattleHex::EDir::LEFT, false).cloneInDirection(BattleHex::EDir::BOTTOM_LEFT, false));
}
else if (targetIsAttacker && y % 2 == 1)
{
BattleHex::checkAndPush(targetPosition.cloneInDirection(BattleHex::EDir::RIGHT, false).cloneInDirection(BattleHex::EDir::TOP_RIGHT, false), output);
BattleHex::checkAndPush(targetPosition.cloneInDirection(BattleHex::EDir::RIGHT, false).cloneInDirection(BattleHex::EDir::BOTTOM_RIGHT, false), output);
output.checkAndPush(targetPosition.cloneInDirection(BattleHex::EDir::RIGHT, false).cloneInDirection(BattleHex::EDir::TOP_RIGHT, false));
output.checkAndPush(targetPosition.cloneInDirection(BattleHex::EDir::RIGHT, false).cloneInDirection(BattleHex::EDir::BOTTOM_RIGHT, false));
}
}
@@ -150,7 +150,7 @@ void BattleFlowProcessor::trySummonGuardians(const CBattleInfoCallback & battle,
std::shared_ptr<const Bonus> summonInfo = stack->getBonus(Selector::type()(BonusType::SUMMON_GUARDIANS));
auto accessibility = battle.getAccessibility();
CreatureID creatureData = summonInfo->subtype.as<CreatureID>();
std::vector<BattleHex> targetHexes;
BattleHexArray targetHexes;
const bool targetIsBig = stack->unitType()->isDoubleWide(); //target = creature to guard
const bool guardianIsBig = creatureData.toCreature()->isDoubleWide();

View File

@@ -1,61 +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 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, std::vector<BattleHex> & 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);
};