1
0
mirror of https://github.com/vcmi/vcmi.git synced 2024-12-24 22:14:36 +02:00

Separated game and battle callback (server & client only)

This commit is contained in:
Ivan Savenko 2023-08-28 17:43:57 +03:00
parent fc4dfda00f
commit 3a88180494
34 changed files with 437 additions and 346 deletions

View File

@ -390,7 +390,7 @@ bool BattleEvaluator::attemptCastingSpell(const CStack * activeStack)
for(auto & round : queue)
{
if(!firstRound)
state->nextRound(0);//todo: set actual value?
state->nextRound();
for(auto unit : round)
{
if(!vstd::contains(values, unit->unitId()))

View File

@ -325,7 +325,7 @@ int32_t HypotheticBattle::getActiveStackID() const
return activeUnitId;
}
void HypotheticBattle::nextRound(int32_t roundNr)
void HypotheticBattle::nextRound()
{
//TODO:HypotheticBattle::nextRound
for(auto unit : battleAliveUnits())
@ -462,6 +462,24 @@ int64_t HypotheticBattle::getActualDamage(const DamageRange & damage, int32_t at
return (damage.min + damage.max) / 2;
}
std::vector<SpellID> HypotheticBattle::getUsedSpells(ui8 side) const
{
// TODO
return {};
}
int3 HypotheticBattle::getLocation() const
{
// TODO
return int3(-1, -1, -1);
}
bool HypotheticBattle::isCreatureBank() const
{
// TODO
return false;
}
int64_t HypotheticBattle::getTreeVersion() const
{
return getBonusBearer()->getTreeVersion() + bonusTreeVersion;
@ -552,8 +570,9 @@ const Services * HypotheticBattle::HypotheticEnvironment::services() const
return env->services();
}
const Environment::BattleCb * HypotheticBattle::HypotheticEnvironment::battle() const
const Environment::BattleCb * HypotheticBattle::HypotheticEnvironment::battle(const BattleID & battleID) const
{
assert(battleID == owner->getBattleID());
return owner;
}

View File

@ -110,11 +110,13 @@ public:
std::shared_ptr<StackWithBonuses> getForUpdate(uint32_t id);
BattleID getBattleID() const override;
int32_t getActiveStackID() const override;
battle::Units getUnitsIf(battle::UnitFilter predicate) const override;
void nextRound(int32_t roundNr) override;
void nextRound() override;
void nextTurn(uint32_t unitId) override;
void addUnit(uint32_t id, const JsonNode & data) override;
@ -136,6 +138,9 @@ public:
uint32_t nextUnitId() const override;
int64_t getActualDamage(const DamageRange & damage, int32_t attackerCount, vstd::RNG & rng) const override;
std::vector<SpellID> getUsedSpells(ui8 side) const override;
int3 getLocation() const override;
bool isCreatureBank() const override;
int64_t getTreeVersion() const;
@ -177,7 +182,7 @@ private:
HypotheticEnvironment(HypotheticBattle * owner_, const Environment * upperEnvironment);
const Services * services() const override;
const BattleCb * battle() const override;
const BattleCb * battle(const BattleID & battleID) const override;
const GameCb * game() const override;
vstd::CLoggerBase * logger() const override;
events::EventBus * eventBus() const override;

View File

@ -16,6 +16,7 @@ class Services;
class IGameInfoCallback;
class IBattleInfoCallback;
class BattleID;
namespace events
{
@ -31,7 +32,7 @@ public:
virtual ~Environment() = default;
virtual const Services * services() const = 0;
virtual const BattleCb * battle() const = 0;
virtual const BattleCb * battle(const BattleID & battleID) const = 0;
virtual const GameCb * game() const = 0;
virtual vstd::CLoggerBase * logger() const = 0;
virtual events::EventBus * eventBus() const = 0;

View File

@ -39,6 +39,7 @@ struct StackLocation;
struct ArtSlotInfo;
struct QuestInfo;
class IBattleState;
class BattleInfo;
// This one teleport-specific, but has to be available everywhere in callbacks and netpacks
// For now it's will be there till teleports code refactored and moved into own file
@ -1478,7 +1479,6 @@ struct DLL_LINKAGE MapObjectSelectDialog : public Query
}
};
class BattleInfo;
struct DLL_LINKAGE BattleStart : public CPackForClient
{
void applyGs(CGameState * gs) const;
@ -1500,14 +1500,12 @@ struct DLL_LINKAGE BattleNextRound : public CPackForClient
void applyGs(CGameState * gs) const;
BattleID battleID = BattleID::NONE;
si32 round = 0;
virtual void visitTyped(ICPackVisitor & visitor) override;
template <typename Handler> void serialize(Handler & h, const int version)
{
h & battleID;
h & round;
}
};

View File

@ -2124,7 +2124,7 @@ void BattleStart::applyGs(CGameState * gs) const
void BattleNextRound::applyGs(CGameState * gs) const
{
gs->getBattle(battleID)->nextRound(round);
gs->getBattle(battleID)->nextRound();
}
void BattleSetActiveStack::applyGs(CGameState * gs) const

View File

@ -26,37 +26,6 @@
VCMI_LIB_NAMESPACE_BEGIN
///BattleInfo
std::pair< std::vector<BattleHex>, int > BattleInfo::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);
}
//making the Path
std::vector<BattleHex> path;
BattleHex curElem = dest;
while(curElem != start)
{
path.push_back(curElem);
curElem = reachability.predecessors[curElem];
}
return std::make_pair(path, reachability.distances[dest]);
}
void BattleInfo::calculateCasualties(std::map<ui32,si32> * casualties) const
{
for(const auto & st : stacks) //setting casualties
{
si32 killed = st->getKilled();
if(killed > 0)
casualties[st->unitSide()][st->creatureId()] += killed;
}
}
CStack * BattleInfo::generateNewStack(uint32_t id, const CStackInstance & base, ui8 side, const SlotID & slot, BattleHex position)
{
PlayerColor owner = sides[side].color;
@ -560,10 +529,24 @@ BattleInfo::BattleInfo():
tacticsSide(0),
tacticDistance(0)
{
setBattle(this);
setNodeType(BATTLE);
}
BattleID BattleInfo::getBattleID() const
{
return battleID;
}
const IBattleInfo * BattleInfo::getBattle() const
{
return this;
}
std::optional<PlayerColor> BattleInfo::getPlayerID() const
{
return std::nullopt;
}
BattleInfo::~BattleInfo()
{
for (auto & elem : stacks)
@ -689,14 +672,30 @@ int64_t BattleInfo::getActualDamage(const DamageRange & damage, int32_t attacker
}
}
void BattleInfo::nextRound(int32_t roundNr)
int3 BattleInfo::getLocation() const
{
return tile;
}
bool BattleInfo::isCreatureBank() const
{
return creatureBank;
}
std::vector<SpellID> BattleInfo::getUsedSpells(ui8 side) const
{
return sides.at(side).usedSpellsHistory;
}
void BattleInfo::nextRound()
{
for(int i = 0; i < 2; ++i)
{
sides.at(i).castSpellsCount = 0;
vstd::amax(--sides.at(i).enchanterCounter, 0);
}
round = roundNr;
round += 1;
for(CStack * s : stacks)
{

View File

@ -75,9 +75,14 @@ public:
BattleInfo();
virtual ~BattleInfo();
const IBattleInfo * getBattle() const override;
std::optional<PlayerColor> getPlayerID() const override;
//////////////////////////////////////////////////////////////////////////
// IBattleInfo
BattleID getBattleID() const override;
int32_t getActiveStackID() const override;
TStacks getStacksIf(TStackFilter predicate) const override;
@ -109,10 +114,15 @@ public:
int64_t getActualDamage(const DamageRange & damage, int32_t attackerCount, vstd::RNG & rng) const override;
int3 getLocation() const override;
bool isCreatureBank() const override;
std::vector<SpellID> getUsedSpells(ui8 side) const override;
//////////////////////////////////////////////////////////////////////////
// IBattleState
void nextRound(int32_t roundNr) override;
void nextRound() override;
void nextTurn(uint32_t unitId) override;
void addUnit(uint32_t id, const JsonNode & data) override;
@ -140,10 +150,6 @@ public:
using CBattleInfoEssentials::battleGetFightingHero;
CGHeroInstance * battleGetFightingHero(ui8 side) const;
std::pair< std::vector<BattleHex>, int > getPath(BattleHex start, BattleHex dest, const battle::Unit * stack) const; //returned value: pair<path, length>; length may be different than number of elements in path since flying creatures jump between distant hexes
void calculateCasualties(std::map<ui32,si32> * casualties) const; //casualties are array of maps size 2 (attacker, defeneder), maps are (crid => amount)
CStack * generateNewStack(uint32_t id, const CStackInstance & base, ui8 side, const SlotID & slot, BattleHex position);
CStack * generateNewStack(uint32_t id, const CStackBasicDescriptor & base, ui8 side, const SlotID & slot, BattleHex position);

View File

@ -17,13 +17,20 @@ VCMI_LIB_NAMESPACE_BEGIN
BattleProxy::BattleProxy(Subject subject_):
subject(std::move(subject_))
{
setBattle(this);
player = subject->getPlayerID();
}
{}
BattleProxy::~BattleProxy() = default;
const IBattleInfo * BattleProxy::getBattle() const
{
return this;
}
std::optional<PlayerColor> BattleProxy::getPlayerID() const
{
return subject->getPlayerID();
}
int32_t BattleProxy::getActiveStackID() const
{
const auto * ret = subject->battleActiveUnit();

View File

@ -24,6 +24,8 @@ public:
//////////////////////////////////////////////////////////////////////////
// IBattleInfo
const IBattleInfo * getBattle() const override;
std::optional<PlayerColor> getPlayerID() const override;
int32_t getActiveStackID() const override;

View File

@ -136,6 +136,27 @@ 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
{
auto reachability = getReachability(stack);
if(reachability.predecessors[dest] == -1) //cannot reach destination
{
return std::make_pair(std::vector<BattleHex>(), 0);
}
//making the Path
std::vector<BattleHex> path;
BattleHex curElem = dest;
while(curElem != start)
{
path.push_back(curElem);
curElem = reachability.predecessors[curElem];
}
return std::make_pair(path, reachability.distances[dest]);
}
bool CBattleInfoCallback::battleHasPenaltyOnLine(BattleHex from, BattleHex dest, bool checkWall, bool checkMoat) const
{
auto isTileBlocked = [&](BattleHex tile)

View File

@ -89,6 +89,8 @@ public:
std::set<BattleHex> battleGetAttackedHexes(const battle::Unit * attacker, BattleHex destinationTile, BattleHex attackerPos = BattleHex::INVALID) const;
bool isEnemyUnitWithinSpecifiedRange(BattleHex attackerPosition, const battle::Unit * defenderUnit, unsigned int range) const;
std::pair< std::vector<BattleHex>, int > getPath(BattleHex start, BattleHex dest, const battle::Unit * stack) const;
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
bool battleCanShoot(const battle::Unit * attacker, BattleHex dest) const; //determines if stack with given ID shoot at the selected destination
bool battleCanShoot(const battle::Unit * attacker) const; //determines if stack with given ID shoot in principle

View File

@ -17,6 +17,11 @@
VCMI_LIB_NAMESPACE_BEGIN
bool CBattleInfoEssentials::duringBattle() const
{
return getBattle() != nullptr;
}
TerrainId CBattleInfoEssentials::battleTerrainType() const
{
RETURN_IF_NOT_BATTLE(TerrainId());
@ -47,7 +52,7 @@ std::vector<std::shared_ptr<const CObstacleInstance>> CBattleInfoEssentials::bat
}
else
{
if(!!player && *perspective != battleGetMySide())
if(!!getPlayerID() && *perspective != battleGetMySide())
logGlobal->warn("Unauthorized obstacles access attempt, assuming massive spell");
}
@ -157,14 +162,14 @@ const CGTownInstance * CBattleInfoEssentials::battleGetDefendedTown() const
BattlePerspective::BattlePerspective CBattleInfoEssentials::battleGetMySide() const
{
RETURN_IF_NOT_BATTLE(BattlePerspective::INVALID);
if(!player || player->isSpectator())
if(!getPlayerID() || getPlayerID()->isSpectator())
return BattlePerspective::ALL_KNOWING;
if(*player == getBattle()->getSidePlayer(BattleSide::ATTACKER))
if(*getPlayerID() == getBattle()->getSidePlayer(BattleSide::ATTACKER))
return BattlePerspective::LEFT_SIDE;
if(*player == getBattle()->getSidePlayer(BattleSide::DEFENDER))
if(*getPlayerID() == getBattle()->getSidePlayer(BattleSide::DEFENDER))
return BattlePerspective::RIGHT_SIDE;
logGlobal->error("Cannot find player %s in battle!", player->toString());
logGlobal->error("Cannot find player %s in battle!", getPlayerID()->toString());
return BattlePerspective::INVALID;
}

View File

@ -34,7 +34,7 @@ namespace BattlePerspective
};
}
class DLL_LINKAGE CBattleInfoEssentials : public virtual CCallbackBase, public IBattleInfoCallback
class DLL_LINKAGE CBattleInfoEssentials : public IBattleInfoCallback
{
protected:
bool battleDoWeKnowAbout(ui8 side) const;
@ -45,6 +45,7 @@ public:
ONLY_MINE, ONLY_ENEMY, MINE_AND_ENEMY
};
bool duringBattle() const;
BattlePerspective::BattlePerspective battleGetMySide() const;
const IBonusBearer * getBonusBearer() const override;

View File

@ -13,26 +13,11 @@
VCMI_LIB_NAMESPACE_BEGIN
bool CCallbackBase::duringBattle() const
{
return getBattle() != nullptr;
}
const IBattleInfo * CCallbackBase::getBattle() const
{
return battle;
}
CCallbackBase::CCallbackBase(std::optional<PlayerColor> Player):
player(std::move(Player))
{
}
void CCallbackBase::setBattle(const IBattleInfo * B)
{
battle = B;
}
std::optional<PlayerColor> CCallbackBase::getPlayerID() const
{
return player;

View File

@ -11,7 +11,7 @@
#include "../GameConstants.h"
#define RETURN_IF_NOT_BATTLE(...) if(!duringBattle()) {logGlobal->error("%s called when no battle!", __FUNCTION__); return __VA_ARGS__; }
#define ASSERT_IF_CALLED_WITH_PLAYER if(!player) {logGlobal->error(BOOST_CURRENT_FUNCTION); assert(0);}
#define ASSERT_IF_CALLED_WITH_PLAYER if(!getPlayerID()) {logGlobal->error(BOOST_CURRENT_FUNCTION); assert(0);}
VCMI_LIB_NAMESPACE_BEGIN
@ -19,26 +19,17 @@ class IBattleInfo;
class BattleInfo;
class CBattleInfoEssentials;
//Basic class for various callbacks (interfaces called by players to get info about game and so forth)
class DLL_LINKAGE CCallbackBase
{
const IBattleInfo * battle = nullptr; //battle to which the player is engaged, nullptr if none or not applicable
protected:
std::optional<PlayerColor> player; // not set gives access to all information, otherwise callback provides only information "visible" for player
CCallbackBase(std::optional<PlayerColor> Player);
CCallbackBase() = default;
const IBattleInfo * getBattle() const;
void setBattle(const IBattleInfo * B);
bool duringBattle() const;
public:
std::optional<PlayerColor> getPlayerID() const;
friend class CBattleInfoEssentials;
};

View File

@ -18,7 +18,7 @@ bool CPlayerBattleCallback::battleCanFlee() const
{
RETURN_IF_NOT_BATTLE(false);
ASSERT_IF_CALLED_WITH_PLAYER
return CBattleInfoEssentials::battleCanFlee(*player);
return CBattleInfoEssentials::battleCanFlee(*getPlayerID());
}
TStacks CPlayerBattleCallback::battleGetStacks(EStackOwnership whose, bool onlyAlive) const
@ -30,8 +30,8 @@ TStacks CPlayerBattleCallback::battleGetStacks(EStackOwnership whose, bool onlyA
return battleGetStacksIf([=](const CStack * s){
const bool ownerMatches = (whose == MINE_AND_ENEMY)
|| (whose == ONLY_MINE && s->unitOwner() == player)
|| (whose == ONLY_ENEMY && s->unitOwner() != player);
|| (whose == ONLY_MINE && s->unitOwner() == getPlayerID())
|| (whose == ONLY_ENEMY && s->unitOwner() != getPlayerID());
return ownerMatches && s->isValidTarget(!onlyAlive);
});
@ -41,7 +41,7 @@ int CPlayerBattleCallback::battleGetSurrenderCost() const
{
RETURN_IF_NOT_BATTLE(-3)
ASSERT_IF_CALLED_WITH_PLAYER
return CBattleInfoCallback::battleGetSurrenderCost(*player);
return CBattleInfoCallback::battleGetSurrenderCost(*getPlayerID());
}
const CGHeroInstance * CPlayerBattleCallback::battleGetMyHero() const

View File

@ -19,6 +19,7 @@ VCMI_LIB_NAMESPACE_BEGIN
struct CObstacleInstance;
class BattleField;
class IBattleInfo;
namespace battle
{
@ -54,6 +55,9 @@ public:
virtual scripting::Pool * getContextPool() const = 0;
#endif
virtual const IBattleInfo * getBattle() const = 0;
virtual std::optional<PlayerColor> getPlayerID() const = 0;
virtual TerrainId battleTerrainType() const = 0;
virtual BattleField battleGetBattlefieldType() const = 0;

View File

@ -19,6 +19,7 @@ struct Bonus;
class JsonNode;
class JsonSerializeFormat;
class BattleField;
class int3;
namespace vstd
{
@ -37,6 +38,8 @@ public:
virtual ~IBattleInfo() = default;
virtual BattleID getBattleID() const = 0;
virtual int32_t getActiveStackID() const = 0;
virtual TStacks getStacksIf(TStackFilter predicate) const = 0;
@ -55,6 +58,8 @@ public:
virtual PlayerColor getSidePlayer(ui8 side) const = 0;
virtual const CArmedInstance * getSideArmy(ui8 side) const = 0;
virtual const CGHeroInstance * getSideHero(ui8 side) const = 0;
/// Returns list of all spells used by specified side (and that can be learned by opposite hero)
virtual std::vector<SpellID> getUsedSpells(ui8 side) const = 0;
virtual uint32_t getCastSpells(ui8 side) const = 0;
virtual int32_t getEnchanterCounter(ui8 side) const = 0;
@ -65,14 +70,15 @@ public:
virtual uint32_t nextUnitId() const = 0;
virtual int64_t getActualDamage(const DamageRange & damage, int32_t attackerCount, vstd::RNG & rng) const = 0;
virtual int3 getLocation() const = 0;
virtual bool isCreatureBank() const = 0;
};
class DLL_LINKAGE IBattleState : public IBattleInfo
{
public:
//TODO: add non-const API
virtual void nextRound(int32_t roundNr) = 0;
virtual void nextRound() = 0;
virtual void nextTurn(uint32_t unitId) = 0;
virtual void addUnit(uint32_t id, const JsonNode & data) = 0;

View File

@ -136,9 +136,9 @@ const Services * CGameHandler::services() const
return VLC;
}
const CGameHandler::BattleCb * CGameHandler::battle() const
const CGameHandler::BattleCb * CGameHandler::battle(const BattleID & battleID) const
{
return this;
return gs->getBattle(battleID);
}
const CGameHandler::GameCb * CGameHandler::game() const

View File

@ -12,7 +12,6 @@
#include <vcmi/Environment.h>
#include "../lib/IGameCallback.h"
#include "../lib/battle/CBattleInfoCallback.h"
#include "../lib/LoadProgress.h"
#include "../lib/ScriptHandler.h"
#include "TurnTimerHandler.h"
@ -53,14 +52,12 @@ class TurnOrderProcessor;
class QueriesProcessor;
class CObjectVisitQuery;
class CGameHandler : public IGameCallback, public CBattleInfoCallback, public Environment
class CGameHandler : public IGameCallback, public Environment
{
CVCMIServer * lobby;
std::shared_ptr<CApplier<CBaseForGHApply>> applier;
public:
using CCallbackBase::setBattle;
std::unique_ptr<HeroPoolProcessor> heroPool;
std::unique_ptr<BattleProcessor> battles;
std::unique_ptr<QueriesProcessor> queries;
@ -84,7 +81,7 @@ public:
TurnTimerHandler turnTimerHandler;
const Services * services() const override;
const BattleCb * battle() const override;
const BattleCb * battle(const BattleID & battleID) const override;
const GameCb * game() const override;
vstd::CLoggerBase * logger() const override;
events::EventBus * eventBus() const override;

View File

@ -20,7 +20,7 @@
#include "../lib/IGameCallback.h"
#include "../lib/mapObjects/CGTownInstance.h"
#include "../lib/gameState/CGameState.h"
#include "../lib/battle/BattleInfo.h"
#include "../lib/battle/IBattleState.h"
#include "../lib/battle/BattleAction.h"
#include "../lib/battle/Unit.h"
#include "../lib/serializer/Connection.h"

View File

@ -17,7 +17,8 @@
#include "../../lib/CGeneralTextHandler.h"
#include "../../lib/CStack.h"
#include "../../lib/GameSettings.h"
#include "../../lib/battle/BattleInfo.h"
#include "../../lib/battle/CBattleInfoCallback.h"
#include "../../lib/battle/IBattleState.h"
#include "../../lib/battle/BattleAction.h"
#include "../../lib/gameState/CGameState.h"
#include "../../lib/NetPacks.h"
@ -37,19 +38,19 @@ void BattleActionProcessor::setGameHandler(CGameHandler * newGameHandler)
gameHandler = newGameHandler;
}
bool BattleActionProcessor::doEmptyAction(const BattleInfo & battle, const BattleAction & ba)
bool BattleActionProcessor::doEmptyAction(const CBattleInfoCallback & battle, const BattleAction & ba)
{
return true;
}
bool BattleActionProcessor::doEndTacticsAction(const BattleInfo & battle, const BattleAction & ba)
bool BattleActionProcessor::doEndTacticsAction(const CBattleInfoCallback & battle, const BattleAction & ba)
{
return true;
}
bool BattleActionProcessor::doWaitAction(const BattleInfo & battle, const BattleAction & ba)
bool BattleActionProcessor::doWaitAction(const CBattleInfoCallback & battle, const BattleAction & ba)
{
const CStack * stack = gameHandler->battleGetStackByID(ba.stackNumber);
const CStack * stack = battle.battleGetStackByID(ba.stackNumber);
if (!canStackAct(battle, stack))
return false;
@ -57,9 +58,9 @@ bool BattleActionProcessor::doWaitAction(const BattleInfo & battle, const Battle
return true;
}
bool BattleActionProcessor::doRetreatAction(const BattleInfo & battle, const BattleAction & ba)
bool BattleActionProcessor::doRetreatAction(const CBattleInfoCallback & battle, const BattleAction & ba)
{
if (!battle.battleCanFlee(battle.sides.at(ba.side).color))
if (!battle.battleCanFlee(battle.sideToPlayer(ba.side)))
{
gameHandler->complain("Cannot retreat!");
return false;
@ -69,9 +70,9 @@ bool BattleActionProcessor::doRetreatAction(const BattleInfo & battle, const Bat
return true;
}
bool BattleActionProcessor::doSurrenderAction(const BattleInfo & battle, const BattleAction & ba)
bool BattleActionProcessor::doSurrenderAction(const CBattleInfoCallback & battle, const BattleAction & ba)
{
PlayerColor player = battle.sides.at(ba.side).color;
PlayerColor player = battle.sideToPlayer(ba.side);
int cost = battle.battleGetSurrenderCost(player);
if (cost < 0)
{
@ -90,7 +91,7 @@ bool BattleActionProcessor::doSurrenderAction(const BattleInfo & battle, const B
return true;
}
bool BattleActionProcessor::doHeroSpellAction(const BattleInfo & battle, const BattleAction & ba)
bool BattleActionProcessor::doHeroSpellAction(const CBattleInfoCallback & battle, const BattleAction & ba)
{
const CGHeroInstance *h = battle.battleGetFightingHero(ba.side);
if (!h)
@ -127,9 +128,9 @@ bool BattleActionProcessor::doHeroSpellAction(const BattleInfo & battle, const B
return true;
}
bool BattleActionProcessor::doWalkAction(const BattleInfo & battle, const BattleAction & ba)
bool BattleActionProcessor::doWalkAction(const CBattleInfoCallback & battle, const BattleAction & ba)
{
const CStack * stack = gameHandler->battleGetStackByID(ba.stackNumber);
const CStack * stack = battle.battleGetStackByID(ba.stackNumber);
battle::Target target = ba.getTarget(&battle);
if (!canStackAct(battle, stack))
@ -150,9 +151,9 @@ bool BattleActionProcessor::doWalkAction(const BattleInfo & battle, const Battle
return true;
}
bool BattleActionProcessor::doDefendAction(const BattleInfo & battle, const BattleAction & ba)
bool BattleActionProcessor::doDefendAction(const CBattleInfoCallback & battle, const BattleAction & ba)
{
const CStack * stack = gameHandler->battleGetStackByID(ba.stackNumber);
const CStack * stack = battle.battleGetStackByID(ba.stackNumber);
if (!canStackAct(battle, stack))
return false;
@ -199,9 +200,9 @@ bool BattleActionProcessor::doDefendAction(const BattleInfo & battle, const Batt
return true;
}
bool BattleActionProcessor::doAttackAction(const BattleInfo & battle, const BattleAction & ba)
bool BattleActionProcessor::doAttackAction(const CBattleInfoCallback & battle, const BattleAction & ba)
{
const CStack * stack = gameHandler->battleGetStackByID(ba.stackNumber);
const CStack * stack = battle.battleGetStackByID(ba.stackNumber);
battle::Target target = ba.getTarget(&battle);
if (!canStackAct(battle, stack))
@ -302,9 +303,9 @@ bool BattleActionProcessor::doAttackAction(const BattleInfo & battle, const Batt
return true;
}
bool BattleActionProcessor::doShootAction(const BattleInfo & battle, const BattleAction & ba)
bool BattleActionProcessor::doShootAction(const CBattleInfoCallback & battle, const BattleAction & ba)
{
const CStack * stack = gameHandler->battleGetStackByID(ba.stackNumber);
const CStack * stack = battle.battleGetStackByID(ba.stackNumber);
battle::Target target = ba.getTarget(&battle);
if (!canStackAct(battle, stack))
@ -369,7 +370,7 @@ bool BattleActionProcessor::doShootAction(const BattleInfo & battle, const Battl
return true;
}
bool BattleActionProcessor::doCatapultAction(const BattleInfo & battle, const BattleAction & ba)
bool BattleActionProcessor::doCatapultAction(const CBattleInfoCallback & battle, const BattleAction & ba)
{
const CStack * stack = battle.battleGetStackByID(ba.stackNumber);
battle::Target target = ba.getTarget(&battle);
@ -393,7 +394,7 @@ bool BattleActionProcessor::doCatapultAction(const BattleInfo & battle, const Ba
return true;
}
bool BattleActionProcessor::doUnitSpellAction(const BattleInfo & battle, const BattleAction & ba)
bool BattleActionProcessor::doUnitSpellAction(const CBattleInfoCallback & battle, const BattleAction & ba)
{
const CStack * stack = battle.battleGetStackByID(ba.stackNumber);
battle::Target target = ba.getTarget(&battle);
@ -406,8 +407,8 @@ bool BattleActionProcessor::doUnitSpellAction(const BattleInfo & battle, const B
std::shared_ptr<const Bonus> spellcaster = stack->getBonus(Selector::typeSubtype(BonusType::SPELLCASTER, spellID));
//TODO special bonus for genies ability
if (randSpellcaster && gameHandler->battleGetRandomStackSpell(gameHandler->getRandomGenerator(), stack, CBattleInfoCallback::RANDOM_AIMED) == SpellID::NONE)
spellID = gameHandler->battleGetRandomStackSpell(gameHandler->getRandomGenerator(), stack, CBattleInfoCallback::RANDOM_GENIE);
if (randSpellcaster && battle.battleGetRandomStackSpell(gameHandler->getRandomGenerator(), stack, CBattleInfoCallback::RANDOM_AIMED) == SpellID::NONE)
spellID = battle.battleGetRandomStackSpell(gameHandler->getRandomGenerator(), stack, CBattleInfoCallback::RANDOM_GENIE);
if (spellID == SpellID::NONE)
gameHandler->complain("That stack can't cast spells!");
@ -426,7 +427,7 @@ bool BattleActionProcessor::doUnitSpellAction(const BattleInfo & battle, const B
return true;
}
bool BattleActionProcessor::doHealAction(const BattleInfo & battle, const BattleAction & ba)
bool BattleActionProcessor::doHealAction(const CBattleInfoCallback & battle, const BattleAction & ba)
{
const CStack * stack = battle.battleGetStackByID(ba.stackNumber);
battle::Target target = ba.getTarget(&battle);
@ -463,7 +464,7 @@ bool BattleActionProcessor::doHealAction(const BattleInfo & battle, const Battle
return true;
}
bool BattleActionProcessor::canStackAct(const BattleInfo & battle, const CStack * stack)
bool BattleActionProcessor::canStackAct(const CBattleInfoCallback & battle, const CStack * stack)
{
if (!stack)
{
@ -476,9 +477,9 @@ bool BattleActionProcessor::canStackAct(const BattleInfo & battle, const CStack
return false;
}
if (gameHandler->battleTacticDist())
if (battle.battleTacticDist())
{
if (stack && stack->unitSide() != gameHandler->battleGetTacticsSide())
if (stack && stack->unitSide() != battle.battleGetTacticsSide())
{
gameHandler->complain("This is not a stack of side that has tactics!");
return false;
@ -486,7 +487,7 @@ bool BattleActionProcessor::canStackAct(const BattleInfo & battle, const CStack
}
else
{
if (stack->unitId() != battle.getActiveStackID())
if (stack != battle.battleActiveUnit())
{
gameHandler->complain("Action has to be about active stack!");
return false;
@ -495,7 +496,7 @@ bool BattleActionProcessor::canStackAct(const BattleInfo & battle, const CStack
return true;
}
bool BattleActionProcessor::dispatchBattleAction(const BattleInfo & battle, const BattleAction & ba)
bool BattleActionProcessor::dispatchBattleAction(const CBattleInfoCallback & battle, const BattleAction & ba)
{
switch(ba.actionType)
{
@ -531,7 +532,7 @@ bool BattleActionProcessor::dispatchBattleAction(const BattleInfo & battle, cons
return false;
}
bool BattleActionProcessor::makeBattleActionImpl(const BattleInfo & battle, const BattleAction &ba)
bool BattleActionProcessor::makeBattleActionImpl(const CBattleInfoCallback & battle, const BattleAction &ba)
{
logGlobal->trace("Making action: %s", ba.toString());
const CStack * stack = battle.battleGetStackByID(ba.stackNumber);
@ -552,22 +553,22 @@ bool BattleActionProcessor::makeBattleActionImpl(const BattleInfo & battle, cons
}
if(ba.actionType == EActionType::WAIT || ba.actionType == EActionType::DEFEND || ba.actionType == EActionType::SHOOT || ba.actionType == EActionType::MONSTER_SPELL)
gameHandler->handleObstacleTriggersForUnit(*gameHandler->spellEnv, *stack);
battle.handleObstacleTriggersForUnit(*gameHandler->spellEnv, *stack);
return result;
}
int BattleActionProcessor::moveStack(const BattleInfo & battle, int stack, BattleHex dest)
int BattleActionProcessor::moveStack(const CBattleInfoCallback & battle, int stack, BattleHex dest)
{
int ret = 0;
const CStack *curStack = gameHandler->battleGetStackByID(stack);
const CStack *curStack = battle.battleGetStackByID(stack);
const CStack *stackAtEnd = battle.battleGetStackByPos(dest);
assert(curStack);
assert(dest < GameConstants::BFIELD_SIZE);
if (battle.tacticDistance)
if (battle.battleGetTacticDist())
{
assert(battle.isInTacticRange(dest));
}
@ -577,7 +578,7 @@ int BattleActionProcessor::moveStack(const BattleInfo & battle, int stack, Battl
return 0;
//initing necessary tables
auto accessibility = gameHandler->getAccesibility(curStack);
auto accessibility = battle.getAccesibility(curStack);
std::set<BattleHex> passed;
//Ignore obstacles on starting position
passed.insert(curStack->getPosition());
@ -600,8 +601,8 @@ int BattleActionProcessor::moveStack(const BattleInfo & battle, int stack, Battl
}
bool canUseGate = false;
auto dbState = battle.si.gateState;
if(gameHandler->battleGetSiegeLevel() > 0 && curStack->unitSide() == BattleSide::DEFENDER &&
auto dbState = battle.battleGetGateState();
if(battle.battleGetSiegeLevel() > 0 && curStack->unitSide() == BattleSide::DEFENDER &&
dbState != EGateState::DESTROYED &&
dbState != EGateState::BLOCKED)
{
@ -614,10 +615,10 @@ int BattleActionProcessor::moveStack(const BattleInfo & battle, int stack, Battl
int creSpeed = curStack->speed(0, true);
if (battle.tacticDistance > 0 && creSpeed > 0)
if (battle.battleGetTacticDist() > 0 && creSpeed > 0)
creSpeed = GameConstants::BFIELD_SIZE;
bool hasWideMoat = vstd::contains_if(gameHandler->battleGetAllObstaclesOnPos(BattleHex(BattleHex::GATE_BRIDGE), false), [](const std::shared_ptr<const CObstacleInstance> & obst)
bool hasWideMoat = vstd::contains_if(battle.battleGetAllObstaclesOnPos(BattleHex(BattleHex::GATE_BRIDGE), false), [](const std::shared_ptr<const CObstacleInstance> & obst)
{
return obst->obstacleType == CObstacleInstance::MOAT;
});
@ -772,14 +773,14 @@ int BattleActionProcessor::moveStack(const BattleInfo & battle, int stack, Battl
}
//if we walked onto something, finalize this portion of stack movement check into obstacle
if(!gameHandler->battleGetAllObstaclesOnPos(hex, false).empty())
if(!battle.battleGetAllObstaclesOnPos(hex, false).empty())
obstacleHit = true;
if (curStack->doubleWide())
{
BattleHex otherHex = curStack->occupiedHex(hex);
//two hex creature hit obstacle by backside
auto obstacle2 = gameHandler->battleGetAllObstaclesOnPos(otherHex, false);
auto obstacle2 = battle.battleGetAllObstaclesOnPos(otherHex, false);
if(otherHex.isValid() && !obstacle2.empty())
obstacleHit = true;
}
@ -805,7 +806,7 @@ int BattleActionProcessor::moveStack(const BattleInfo & battle, int stack, Battl
{
if(stackIsMoving && start != curStack->getPosition())
{
stackIsMoving = gameHandler->handleObstacleTriggersForUnit(*gameHandler->spellEnv, *curStack, passed);
stackIsMoving = battle.handleObstacleTriggersForUnit(*gameHandler->spellEnv, *curStack, passed);
passed.insert(curStack->getPosition());
if(curStack->doubleWide())
passed.insert(curStack->occupiedHex());
@ -843,12 +844,12 @@ int BattleActionProcessor::moveStack(const BattleInfo & battle, int stack, Battl
passed.clear(); //Just empty passed, obstacles will handled automatically
}
//handling obstacle on the final field (separate, because it affects both flying and walking stacks)
gameHandler->handleObstacleTriggersForUnit(*gameHandler->spellEnv, *curStack, passed);
battle.handleObstacleTriggersForUnit(*gameHandler->spellEnv, *curStack, passed);
return ret;
}
void BattleActionProcessor::makeAttack(const BattleInfo & battle, const CStack * attacker, const CStack * defender, int distance, BattleHex targetHex, bool first, bool ranged, bool counter)
void BattleActionProcessor::makeAttack(const CBattleInfoCallback & battle, const CStack * attacker, const CStack * defender, int distance, BattleHex targetHex, bool first, bool ranged, bool counter)
{
if(first && !counter)
handleAttackBeforeCasting(battle, ranged, attacker, defender);
@ -891,7 +892,7 @@ void BattleActionProcessor::makeAttack(const BattleInfo & battle, const CStack *
bat.flags |= BattleAttack::DEATH_BLOW;
}
const auto * owner = battle.getHero(attacker->unitOwner());
const auto * owner = battle.battleGetFightingHero(attacker->unitOwner());
if(owner)
{
int chance = owner->valOfBonuses(BonusType::BONUS_DAMAGE_CHANCE, attacker->creatureIndex());
@ -1012,7 +1013,7 @@ void BattleActionProcessor::makeAttack(const BattleInfo & battle, const CStack *
const CStack * actor = item.first;
int64_t rawDamage = item.second;
const CGHeroInstance * actorOwner = battle.getHero(actor->unitOwner());
const CGHeroInstance * actorOwner = battle.battleGetFightingHero(actor->unitOwner());
if(actorOwner)
{
@ -1058,7 +1059,7 @@ void BattleActionProcessor::makeAttack(const BattleInfo & battle, const CStack *
handleAfterAttackCasting(battle, ranged, attacker, defender);
}
void BattleActionProcessor::attackCasting(const BattleInfo & battle, bool ranged, BonusType attackMode, const battle::Unit * attacker, const battle::Unit * defender)
void BattleActionProcessor::attackCasting(const CBattleInfoCallback & battle, bool ranged, BonusType attackMode, const battle::Unit * attacker, const battle::Unit * defender)
{
if(attacker->hasBonusOfType(attackMode))
{
@ -1126,12 +1127,12 @@ void BattleActionProcessor::attackCasting(const BattleInfo & battle, bool ranged
}
}
void BattleActionProcessor::handleAttackBeforeCasting(const BattleInfo & battle, bool ranged, const CStack * attacker, const CStack * defender)
void BattleActionProcessor::handleAttackBeforeCasting(const CBattleInfoCallback & battle, bool ranged, const CStack * attacker, const CStack * defender)
{
attackCasting(battle, ranged, BonusType::SPELL_BEFORE_ATTACK, attacker, defender); //no death stare / acid breath needed?
}
void BattleActionProcessor::handleAfterAttackCasting(const BattleInfo & battle, bool ranged, const CStack * attacker, const CStack * defender)
void BattleActionProcessor::handleAfterAttackCasting(const CBattleInfoCallback & battle, bool ranged, const CStack * attacker, const CStack * defender)
{
if(!attacker->alive() || !defender->alive()) // can be already dead
return;
@ -1286,7 +1287,7 @@ void BattleActionProcessor::handleAfterAttackCasting(const BattleInfo & battle,
}
}
int64_t BattleActionProcessor::applyBattleEffects(const BattleInfo & battle, BattleAttack & bat, std::shared_ptr<battle::CUnitState> attackerState, FireShieldInfo & fireShield, const CStack * def, int distance, bool secondary)
int64_t BattleActionProcessor::applyBattleEffects(const CBattleInfoCallback & battle, BattleAttack & bat, std::shared_ptr<battle::CUnitState> attackerState, FireShieldInfo & fireShield, const CStack * def, int distance, bool secondary)
{
BattleStackAttacked bsa;
if(secondary)
@ -1303,7 +1304,7 @@ int64_t BattleActionProcessor::applyBattleEffects(const BattleInfo & battle, Bat
bai.unluckyStrike = bat.unlucky();
auto range = battle.calculateDmgRange(bai);
bsa.damageAmount = battle.getActualDamage(range.damage, attackerState->getCount(), gameHandler->getRandomGenerator());
bsa.damageAmount = battle.getBattle()->getActualDamage(range.damage, attackerState->getCount(), gameHandler->getRandomGenerator());
CStack::prepareAttacked(bsa, gameHandler->getRandomGenerator(), bai.defender->acquireState()); //calculate casualties
}
@ -1390,17 +1391,17 @@ void BattleActionProcessor::addGenericKilledLog(BattleLogMessage & blm, const CS
}
}
bool BattleActionProcessor::makeAutomaticBattleAction(const BattleInfo & battle, const BattleAction & ba)
bool BattleActionProcessor::makeAutomaticBattleAction(const CBattleInfoCallback & battle, const BattleAction & ba)
{
return makeBattleActionImpl(battle, ba);
}
bool BattleActionProcessor::makePlayerBattleAction(const BattleInfo & battle, PlayerColor player, const BattleAction &ba)
bool BattleActionProcessor::makePlayerBattleAction(const CBattleInfoCallback & battle, PlayerColor player, const BattleAction &ba)
{
if (ba.side != 0 && ba.side != 1 && gameHandler->complain("Can not make action - invalid battle side!"))
return false;
if(battle.tacticDistance != 0)
if(battle.battleGetTacticDist() != 0)
{
if(!ba.isTacticsAction())
{
@ -1408,7 +1409,7 @@ bool BattleActionProcessor::makePlayerBattleAction(const BattleInfo & battle, Pl
return false;
}
if(player != battle.sides[ba.side].color)
if(player != battle.sideToPlayer(ba.side))
{
gameHandler->complain("Can not make actions in battles you are not part of!");
return false;
@ -1416,16 +1417,16 @@ bool BattleActionProcessor::makePlayerBattleAction(const BattleInfo & battle, Pl
}
else
{
if (ba.isUnitAction() && ba.stackNumber != battle.getActiveStackID())
auto active = battle.battleActiveUnit();
if(!active && gameHandler->complain("No active unit in battle!"))
return false;
if (ba.isUnitAction() && ba.stackNumber != active->unitId())
{
gameHandler->complain("Can not make actions - stack is not active!");
return false;
}
auto active = battle.battleActiveUnit();
if(!active && gameHandler->complain("No active unit in battle!"))
return false;
auto unitOwner = battle.battleGetOwner(active);
if(player != unitOwner && gameHandler->complain("Can not make actions in battles you are not part of!"))

View File

@ -14,7 +14,7 @@ VCMI_LIB_NAMESPACE_BEGIN
struct BattleLogMessage;
struct BattleAttack;
class BattleAction;
class BattleInfo;
class CBattleInfoCallback;
struct BattleHex;
class CStack;
class PlayerColor;
@ -39,42 +39,42 @@ class BattleActionProcessor : boost::noncopyable
BattleProcessor * owner;
CGameHandler * gameHandler;
int moveStack(const BattleInfo & battle, int stack, BattleHex dest); //returned value - travelled distance
void makeAttack(const BattleInfo & battle, const CStack * attacker, const CStack * defender, int distance, BattleHex targetHex, bool first, bool ranged, bool counter);
int moveStack(const CBattleInfoCallback & battle, int stack, BattleHex dest); //returned value - travelled distance
void makeAttack(const CBattleInfoCallback & battle, const CStack * attacker, const CStack * defender, int distance, BattleHex targetHex, bool first, bool ranged, bool counter);
void handleAttackBeforeCasting(const BattleInfo & battle, bool ranged, const CStack * attacker, const CStack * defender);
void handleAfterAttackCasting(const BattleInfo & battle, bool ranged, const CStack * attacker, const CStack * defender);
void attackCasting(const BattleInfo & battle, bool ranged, BonusType attackMode, const battle::Unit * attacker, const battle::Unit * defender);
void handleAttackBeforeCasting(const CBattleInfoCallback & battle, bool ranged, const CStack * attacker, const CStack * defender);
void handleAfterAttackCasting(const CBattleInfoCallback & battle, bool ranged, const CStack * attacker, const CStack * defender);
void attackCasting(const CBattleInfoCallback & battle, bool ranged, BonusType attackMode, const battle::Unit * attacker, const battle::Unit * defender);
// damage, drain life & fire shield; returns amount of drained life
int64_t applyBattleEffects(const BattleInfo & battle, BattleAttack & bat, std::shared_ptr<battle::CUnitState> attackerState, FireShieldInfo & fireShield, const CStack * def, int distance, bool secondary);
int64_t applyBattleEffects(const CBattleInfoCallback & battle, BattleAttack & bat, std::shared_ptr<battle::CUnitState> attackerState, FireShieldInfo & fireShield, const CStack * def, int distance, bool secondary);
void sendGenericKilledLog(const CStack * defender, int32_t killed, bool multiple);
void addGenericKilledLog(BattleLogMessage & blm, const CStack * defender, int32_t killed, bool multiple);
bool canStackAct(const BattleInfo & battle, const CStack * stack);
bool canStackAct(const CBattleInfoCallback & battle, const CStack * stack);
bool doEmptyAction(const BattleInfo & battle, const BattleAction & ba);
bool doEndTacticsAction(const BattleInfo & battle, const BattleAction & ba);
bool doRetreatAction(const BattleInfo & battle, const BattleAction & ba);
bool doSurrenderAction(const BattleInfo & battle, const BattleAction & ba);
bool doHeroSpellAction(const BattleInfo & battle, const BattleAction & ba);
bool doWalkAction(const BattleInfo & battle, const BattleAction & ba);
bool doWaitAction(const BattleInfo & battle, const BattleAction & ba);
bool doDefendAction(const BattleInfo & battle, const BattleAction & ba);
bool doAttackAction(const BattleInfo & battle, const BattleAction & ba);
bool doShootAction(const BattleInfo & battle, const BattleAction & ba);
bool doCatapultAction(const BattleInfo & battle, const BattleAction & ba);
bool doUnitSpellAction(const BattleInfo & battle, const BattleAction & ba);
bool doHealAction(const BattleInfo & battle, const BattleAction & ba);
bool doEmptyAction(const CBattleInfoCallback & battle, const BattleAction & ba);
bool doEndTacticsAction(const CBattleInfoCallback & battle, const BattleAction & ba);
bool doRetreatAction(const CBattleInfoCallback & battle, const BattleAction & ba);
bool doSurrenderAction(const CBattleInfoCallback & battle, const BattleAction & ba);
bool doHeroSpellAction(const CBattleInfoCallback & battle, const BattleAction & ba);
bool doWalkAction(const CBattleInfoCallback & battle, const BattleAction & ba);
bool doWaitAction(const CBattleInfoCallback & battle, const BattleAction & ba);
bool doDefendAction(const CBattleInfoCallback & battle, const BattleAction & ba);
bool doAttackAction(const CBattleInfoCallback & battle, const BattleAction & ba);
bool doShootAction(const CBattleInfoCallback & battle, const BattleAction & ba);
bool doCatapultAction(const CBattleInfoCallback & battle, const BattleAction & ba);
bool doUnitSpellAction(const CBattleInfoCallback & battle, const BattleAction & ba);
bool doHealAction(const CBattleInfoCallback & battle, const BattleAction & ba);
bool dispatchBattleAction(const BattleInfo & battle, const BattleAction & ba);
bool makeBattleActionImpl(const BattleInfo & battle, const BattleAction & ba);
bool dispatchBattleAction(const CBattleInfoCallback & battle, const BattleAction & ba);
bool makeBattleActionImpl(const CBattleInfoCallback & battle, const BattleAction & ba);
public:
explicit BattleActionProcessor(BattleProcessor * owner);
void setGameHandler(CGameHandler * newGameHandler);
bool makeAutomaticBattleAction(const BattleInfo & battle, const BattleAction & ba);
bool makePlayerBattleAction(const BattleInfo & battle, PlayerColor player, const BattleAction & ba);
bool makeAutomaticBattleAction(const CBattleInfoCallback & battle, const BattleAction & ba);
bool makePlayerBattleAction(const CBattleInfoCallback & battle, PlayerColor player, const BattleAction & ba);
};

View File

@ -16,7 +16,8 @@
#include "../../lib/CStack.h"
#include "../../lib/GameSettings.h"
#include "../../lib/battle/BattleInfo.h"
#include "../../lib/battle/CBattleInfoCallback.h"
#include "../../lib/battle/IBattleState.h"
#include "../../lib/gameState/CGameState.h"
#include "../../lib/mapObjects/CGTownInstance.h"
#include "../../lib/NetPacks.h"
@ -35,7 +36,7 @@ void BattleFlowProcessor::setGameHandler(CGameHandler * newGameHandler)
gameHandler = newGameHandler;
}
void BattleFlowProcessor::summonGuardiansHelper(const BattleInfo & battle, std::vector<BattleHex> & output, const BattleHex & targetPosition, ui8 side, bool targetIsTwoHex) //return hexes for summoning two hex monsters in output, target = unit to guard
void BattleFlowProcessor::summonGuardiansHelper(const CBattleInfoCallback & battle, std::vector<BattleHex> & output, const BattleHex & targetPosition, ui8 side, bool targetIsTwoHex) //return hexes for summoning two hex monsters in output, target = unit to guard
{
int x = targetPosition.getX();
int y = targetPosition.getY();
@ -110,39 +111,39 @@ void BattleFlowProcessor::summonGuardiansHelper(const BattleInfo & battle, std::
}
}
void BattleFlowProcessor::tryPlaceMoats(const BattleInfo & battle)
void BattleFlowProcessor::tryPlaceMoats(const CBattleInfoCallback & battle)
{
const auto * town = battle.battleGetDefendedTown();
//Moat should be initialized here, because only here we can use spellcasting
if (battle.town && battle.town->fortLevel() >= CGTownInstance::CITADEL)
if (town && town->fortLevel() >= CGTownInstance::CITADEL)
{
const auto * h = battle.battleGetFightingHero(BattleSide::DEFENDER);
const auto * actualCaster = h ? static_cast<const spells::Caster*>(h) : nullptr;
auto moatCaster = spells::SilentCaster(battle.getSidePlayer(BattleSide::DEFENDER), actualCaster);
auto cast = spells::BattleCast(&battle, &moatCaster, spells::Mode::PASSIVE, battle.town->town->moatAbility.toSpell());
auto moatCaster = spells::SilentCaster(battle.sideToPlayer(BattleSide::DEFENDER), actualCaster);
auto cast = spells::BattleCast(&battle, &moatCaster, spells::Mode::PASSIVE, town->town->moatAbility.toSpell());
auto target = spells::Target();
cast.cast(gameHandler->spellEnv, target);
}
}
void BattleFlowProcessor::onBattleStarted(const BattleInfo & battle)
void BattleFlowProcessor::onBattleStarted(const CBattleInfoCallback & battle)
{
gameHandler->setBattle(&battle);
tryPlaceMoats(battle);
gameHandler->turnTimerHandler.onBattleStart(battle.battleID);
gameHandler->turnTimerHandler.onBattleStart(battle.getBattle()->getBattleID());
if (battle.tacticDistance == 0)
if (battle.battleGetTacticDist() == 0)
onTacticsEnded(battle);
}
void BattleFlowProcessor::trySummonGuardians(const BattleInfo & battle, const CStack * stack)
void BattleFlowProcessor::trySummonGuardians(const CBattleInfoCallback & battle, const CStack * stack)
{
if (!stack->hasBonusOfType(BonusType::SUMMON_GUARDIANS))
return;
std::shared_ptr<const Bonus> summonInfo = stack->getBonus(Selector::type()(BonusType::SUMMON_GUARDIANS));
auto accessibility = gameHandler->getAccesibility();
auto accessibility = battle.getAccesibility();
CreatureID creatureData = CreatureID(summonInfo->subtype);
std::vector<BattleHex> targetHexes;
const bool targetIsBig = stack->unitType()->isDoubleWide(); //target = creature to guard
@ -177,7 +178,7 @@ void BattleFlowProcessor::trySummonGuardians(const BattleInfo & battle, const CS
}
}
void BattleFlowProcessor::castOpeningSpells(const BattleInfo & battle)
void BattleFlowProcessor::castOpeningSpells(const CBattleInfoCallback & battle)
{
for (int i = 0; i < 2; ++i)
{
@ -202,12 +203,12 @@ void BattleFlowProcessor::castOpeningSpells(const BattleInfo & battle)
}
}
void BattleFlowProcessor::onTacticsEnded(const BattleInfo & battle)
void BattleFlowProcessor::onTacticsEnded(const CBattleInfoCallback & battle)
{
//initial stacks appearance triggers, e.g. built-in bonus spells
auto initialStacks = battle.stacks; //use temporary variable to outclude summoned stacks added to battle.stacks from processing
auto initialStacks = battle.battleGetAllStacks(true);
for (CStack * stack : initialStacks)
for (const CStack * stack : initialStacks)
{
trySummonGuardians(battle, stack);
stackEnchantedTrigger(battle, stack);
@ -223,14 +224,14 @@ void BattleFlowProcessor::onTacticsEnded(const BattleInfo & battle)
activateNextStack(battle);
}
void BattleFlowProcessor::startNextRound(const BattleInfo & battle, bool isFirstRound)
void BattleFlowProcessor::startNextRound(const CBattleInfoCallback & battle, bool isFirstRound)
{
BattleNextRound bnr;
bnr.round = battle.round + 1;
logGlobal->debug("Round %d", bnr.round);
logGlobal->debug("Next round starts");
gameHandler->sendAndApply(&bnr);
auto obstacles = battle.obstacles; //we copy container, because we're going to modify it
// operate on copy - removing obstacles will invalidate iterator on 'battle' container
auto obstacles = battle.battleGetAllObstacles();
for (auto &obstPtr : obstacles)
{
if (const SpellCreatedObstacle *sco = dynamic_cast<const SpellCreatedObstacle *>(obstPtr.get()))
@ -238,16 +239,14 @@ void BattleFlowProcessor::startNextRound(const BattleInfo & battle, bool isFirst
removeObstacle(battle, *obstPtr);
}
const BattleInfo & curB = *&battle;
for(auto stack : curB.stacks)
for(auto stack : battle.battleGetAllStacks(true))
{
if(stack->alive() && !isFirstRound)
stackEnchantedTrigger(battle, stack);
}
}
const CStack * BattleFlowProcessor::getNextStack(const BattleInfo & battle)
const CStack * BattleFlowProcessor::getNextStack(const CBattleInfoCallback & battle)
{
std::vector<battle::Units> q;
battle.battleGetTurnOrder(q, 1, 0, -1); //todo: get rid of "turn -1"
@ -283,7 +282,7 @@ const CStack * BattleFlowProcessor::getNextStack(const BattleInfo & battle)
return stack;
}
void BattleFlowProcessor::activateNextStack(const BattleInfo & battle)
void BattleFlowProcessor::activateNextStack(const CBattleInfoCallback & battle)
{
// Find next stack that requires manual control
for (;;)
@ -305,16 +304,17 @@ void BattleFlowProcessor::activateNextStack(const BattleInfo & battle)
BattleUnitsChanged removeGhosts;
for(auto stack : battle.stacks)
{
if(stack->ghostPending)
removeGhosts.changedStacks.emplace_back(stack->unitId(), UnitChanges::EOperation::REMOVE);
}
auto pendingGhosts = battle.battleGetStacksIf([](const CStack * stack){
return stack->ghostPending;
});
for(auto stack : pendingGhosts)
removeGhosts.changedStacks.emplace_back(stack->unitId(), UnitChanges::EOperation::REMOVE);
if(!removeGhosts.changedStacks.empty())
gameHandler->sendAndApply(&removeGhosts);
gameHandler->turnTimerHandler.onBattleNextStack(battle.battleID, *next);
gameHandler->turnTimerHandler.onBattleNextStack(battle.getBattle()->getBattleID(), *next);
if (!tryMakeAutomaticAction(battle, next))
{
@ -324,7 +324,7 @@ void BattleFlowProcessor::activateNextStack(const BattleInfo & battle)
}
}
bool BattleFlowProcessor::tryMakeAutomaticAction(const BattleInfo & battle, const CStack * next)
bool BattleFlowProcessor::tryMakeAutomaticAction(const CBattleInfoCallback & battle, const CStack * next)
{
// check for bad morale => freeze
int nextStackMorale = next->moraleVal();
@ -370,7 +370,7 @@ bool BattleFlowProcessor::tryMakeAutomaticAction(const BattleInfo & battle, cons
return true;
}
const CGHeroInstance * curOwner = gameHandler->battleGetOwnerHero(next);
const CGHeroInstance * curOwner = battle.battleGetOwnerHero(next);
const int stackCreatureId = next->unitType()->getId();
if ((stackCreatureId == CreatureID::ARROW_TOWERS || stackCreatureId == CreatureID::BALLISTA)
@ -385,7 +385,7 @@ bool BattleFlowProcessor::tryMakeAutomaticAction(const BattleInfo & battle, cons
const battle::Unit * target = nullptr;
for(auto & elem : battle.stacks)
for(auto & elem : battle.battleGetAllStacks(true))
{
if(elem->unitType()->getId() != CreatureID::CATAPULT
&& elem->unitOwner() != next->unitOwner()
@ -433,7 +433,7 @@ bool BattleFlowProcessor::tryMakeAutomaticAction(const BattleInfo & battle, cons
if (next->unitType()->getId() == CreatureID::FIRST_AID_TENT)
{
TStacks possibleStacks = gameHandler->battleGetStacksIf([=](const CStack * s)
TStacks possibleStacks = battle.battleGetStacksIf([=](const CStack * s)
{
return s->unitOwner() == next->unitOwner() && s->canBeHealed();
});
@ -470,7 +470,7 @@ bool BattleFlowProcessor::tryMakeAutomaticAction(const BattleInfo & battle, cons
return false;
}
bool BattleFlowProcessor::rollGoodMorale(const BattleInfo & battle, const CStack * next)
bool BattleFlowProcessor::rollGoodMorale(const CBattleInfoCallback & battle, const CStack * next)
{
//check for good morale
auto nextStackMorale = next->moraleVal();
@ -498,10 +498,10 @@ bool BattleFlowProcessor::rollGoodMorale(const BattleInfo & battle, const CStack
return false;
}
void BattleFlowProcessor::onActionMade(const BattleInfo & battle, const BattleAction &ba)
void BattleFlowProcessor::onActionMade(const CBattleInfoCallback & battle, const BattleAction &ba)
{
const CStack * actedStack = battle.battleGetStackByID(ba.stackNumber, false);
const CStack * activeStack = battle.battleGetStackByID(battle.getActiveStackID(), false);
const auto * actedStack = battle.battleGetStackByID(ba.stackNumber, false);
const auto * activeStack = battle.battleActiveUnit();
if (ba.actionType == EActionType::END_TACTIC_PHASE)
{
onTacticsEnded(battle);
@ -516,7 +516,7 @@ void BattleFlowProcessor::onActionMade(const BattleInfo & battle, const BattleAc
return;
// tactics - next stack will be selected by player
if(battle.tacticDistance != 0)
if(battle.battleGetTacticDist() != 0)
return;
if (ba.isUnitAction())
@ -545,7 +545,7 @@ void BattleFlowProcessor::onActionMade(const BattleInfo & battle, const BattleAc
activateNextStack(battle);
}
void BattleFlowProcessor::makeStackDoNothing(const BattleInfo & battle, const CStack * next)
void BattleFlowProcessor::makeStackDoNothing(const CBattleInfoCallback & battle, const CStack * next)
{
BattleAction doNothing;
doNothing.actionType = EActionType::NO_ACTION;
@ -555,7 +555,7 @@ void BattleFlowProcessor::makeStackDoNothing(const BattleInfo & battle, const CS
makeAutomaticAction(battle, next, doNothing);
}
bool BattleFlowProcessor::makeAutomaticAction(const BattleInfo & battle, const CStack *stack, BattleAction &ba)
bool BattleFlowProcessor::makeAutomaticAction(const CBattleInfoCallback & battle, const CStack *stack, BattleAction &ba)
{
BattleSetActiveStack bsa;
bsa.stack = stack->unitId();
@ -566,7 +566,7 @@ bool BattleFlowProcessor::makeAutomaticAction(const BattleInfo & battle, const C
return ret;
}
void BattleFlowProcessor::stackEnchantedTrigger(const BattleInfo & battle, const CStack * st)
void BattleFlowProcessor::stackEnchantedTrigger(const CBattleInfoCallback & battle, const CStack * st)
{
auto bl = *(st->getBonuses(Selector::type()(BonusType::ENCHANTED)));
for(auto b : bl)
@ -587,7 +587,7 @@ void BattleFlowProcessor::stackEnchantedTrigger(const BattleInfo & battle, const
if(val > 3)
{
for(auto s : battle.battleGetAllStacks())
if(gameHandler->battleMatchOwner(st, s, true) && s->isValidTarget()) //all allied
if(battle.battleMatchOwner(st, s, true) && s->isValidTarget()) //all allied
target.emplace_back(s);
}
else
@ -598,14 +598,14 @@ void BattleFlowProcessor::stackEnchantedTrigger(const BattleInfo & battle, const
}
}
void BattleFlowProcessor::removeObstacle(const BattleInfo & battle, const CObstacleInstance & obstacle)
void BattleFlowProcessor::removeObstacle(const CBattleInfoCallback & battle, const CObstacleInstance & obstacle)
{
BattleObstaclesChanged obsRem;
obsRem.changes.emplace_back(obstacle.uniqueID, ObstacleChanges::EOperation::REMOVE);
gameHandler->sendAndApply(&obsRem);
}
void BattleFlowProcessor::stackTurnTrigger(const BattleInfo & battle, const CStack *st)
void BattleFlowProcessor::stackTurnTrigger(const CBattleInfoCallback & battle, const CStack *st)
{
BattleTriggerEffect bte;
bte.stackID = st->unitId();
@ -662,7 +662,7 @@ void BattleFlowProcessor::stackTurnTrigger(const BattleInfo & battle, const CSta
if(st->hasBonusOfType(BonusType::MANA_DRAIN) && !st->drainedMana)
{
const PlayerColor opponent = battle.otherPlayer(battle.battleGetOwner(st));
const CGHeroInstance * opponentHero = battle.getHero(opponent);
const CGHeroInstance * opponentHero = battle.battleGetFightingHero(opponent);
if(opponentHero)
{
ui32 manaDrained = st->valOfBonuses(BonusType::MANA_DRAIN);
@ -679,9 +679,9 @@ void BattleFlowProcessor::stackTurnTrigger(const BattleInfo & battle, const CSta
if (st->isLiving() && !st->hasBonusOfType(BonusType::FEARLESS))
{
bool fearsomeCreature = false;
for (CStack * stack : battle.stacks)
for (const CStack * stack : battle.battleGetAllStacks(true))
{
if (gameHandler->battleMatchOwner(st, stack) && stack->alive() && stack->hasBonusOfType(BonusType::FEAR))
if (battle.battleMatchOwner(st, stack) && stack->alive() && stack->hasBonusOfType(BonusType::FEAR))
{
fearsomeCreature = true;
break;
@ -697,7 +697,7 @@ void BattleFlowProcessor::stackTurnTrigger(const BattleInfo & battle, const CSta
}
}
BonusList bl = *(st->getBonuses(Selector::type()(BonusType::ENCHANTER)));
int side = battle.whatSide(st->unitOwner());
int side = *battle.playerToSide(st->unitOwner());
if(st->canCast() && battle.battleGetEnchanterCounter(side) == 0)
{
bool cast = false;
@ -732,11 +732,10 @@ void BattleFlowProcessor::stackTurnTrigger(const BattleInfo & battle, const CSta
}
}
void BattleFlowProcessor::setActiveStack(const BattleInfo & battle, const CStack * stack)
void BattleFlowProcessor::setActiveStack(const CBattleInfoCallback & battle, const battle::Unit * stack)
{
assert(stack);
logGlobal->trace("Activating %s", stack->nodeName());
BattleSetActiveStack sas;
sas.stack = stack->unitId();
gameHandler->sendAndApply(&sas);

View File

@ -13,8 +13,12 @@ VCMI_LIB_NAMESPACE_BEGIN
class CStack;
struct BattleHex;
class BattleAction;
class BattleInfo;
class CBattleInfoCallback;
struct CObstacleInstance;
namespace battle
{
class Unit;
}
VCMI_LIB_NAMESPACE_END
class CGameHandler;
@ -26,31 +30,31 @@ class BattleFlowProcessor : boost::noncopyable
BattleProcessor * owner;
CGameHandler * gameHandler;
const CStack * getNextStack(const BattleInfo & battle);
const CStack * getNextStack(const CBattleInfoCallback & battle);
bool rollGoodMorale(const BattleInfo & battle, const CStack * stack);
bool tryMakeAutomaticAction(const BattleInfo & battle, const CStack * stack);
bool rollGoodMorale(const CBattleInfoCallback & battle, const CStack * stack);
bool tryMakeAutomaticAction(const CBattleInfoCallback & battle, const CStack * stack);
void summonGuardiansHelper(const BattleInfo & battle, std::vector<BattleHex> & output, const BattleHex & targetPosition, ui8 side, bool targetIsTwoHex);
void trySummonGuardians(const BattleInfo & battle, const CStack * stack);
void tryPlaceMoats(const BattleInfo & battle);
void castOpeningSpells(const BattleInfo & battle);
void activateNextStack(const BattleInfo & battle);
void startNextRound(const BattleInfo & battle, bool isFirstRound);
void summonGuardiansHelper(const CBattleInfoCallback & battle, std::vector<BattleHex> & output, const BattleHex & targetPosition, ui8 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 BattleInfo & battle, const CStack * stack);
void removeObstacle(const BattleInfo & battle, const CObstacleInstance & obstacle);
void stackTurnTrigger(const BattleInfo & battle, const CStack * stack);
void setActiveStack(const BattleInfo & battle, const CStack * stack);
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 BattleInfo & battle, const CStack * next);
bool makeAutomaticAction(const BattleInfo & battle, const CStack * stack, BattleAction & ba); //used when action is taken by stack without volition of player (eg. unguided catapult attack)
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);
void setGameHandler(CGameHandler * newGameHandler);
void onBattleStarted(const BattleInfo & battle);
void onTacticsEnded(const BattleInfo & battle);
void onActionMade(const BattleInfo & battle, const BattleAction & ba);
void onBattleStarted(const CBattleInfoCallback & battle);
void onTacticsEnded(const CBattleInfoCallback & battle);
void onActionMade(const CBattleInfoCallback & battle, const BattleAction & ba);
};

View File

@ -19,6 +19,7 @@
#include "../queries/BattleQueries.h"
#include "../../lib/TerrainHandler.h"
#include "../../lib/battle/CBattleInfoCallback.h"
#include "../../lib/battle/BattleInfo.h"
#include "../../lib/gameState/CGameState.h"
#include "../../lib/mapping/CMap.h"
@ -147,17 +148,17 @@ BattleID BattleProcessor::setupBattle(int3 tile, const CArmedInstance *armies[2]
return bs.battleID;
}
bool BattleProcessor::checkBattleStateChanges(const BattleInfo & battle)
bool BattleProcessor::checkBattleStateChanges(const CBattleInfoCallback & battle)
{
//check if drawbridge state need to be changes
if (gameHandler->battleGetSiegeLevel() > 0)
if (battle.battleGetSiegeLevel() > 0)
updateGateState(battle);
if (resultProcessor->battleIsEnding(battle))
return true;
//check if battle ended
if (auto result = gameHandler->battleIsFinished())
if (auto result = battle.battleIsFinished())
{
setBattleResult(battle, EBattleResult::NORMAL, *result);
return true;
@ -166,7 +167,7 @@ bool BattleProcessor::checkBattleStateChanges(const BattleInfo & battle)
return false;
}
void BattleProcessor::updateGateState(const BattleInfo & battle)
void BattleProcessor::updateGateState(const CBattleInfoCallback & battle)
{
// GATE_BRIDGE - leftmost tile, located over moat
// GATE_OUTER - central tile, mostly covered by gate image
@ -182,20 +183,20 @@ void BattleProcessor::updateGateState(const BattleInfo & battle)
// - if Force Field is cast here, bridge can't open (but can close, in any town)
// - deals moat damage to attacker if bridge is closed (fortress only)
bool hasForceFieldOnBridge = !gameHandler->battleGetAllObstaclesOnPos(BattleHex(BattleHex::GATE_BRIDGE), true).empty();
bool hasForceFieldOnBridge = !battle.battleGetAllObstaclesOnPos(BattleHex(BattleHex::GATE_BRIDGE), true).empty();
bool hasStackAtGateInner = battle.battleGetUnitByPos(BattleHex(BattleHex::GATE_INNER), false) != nullptr;
bool hasStackAtGateOuter = battle.battleGetUnitByPos(BattleHex(BattleHex::GATE_OUTER), false) != nullptr;
bool hasStackAtGateBridge = battle.battleGetUnitByPos(BattleHex(BattleHex::GATE_BRIDGE), false) != nullptr;
bool hasWideMoat = vstd::contains_if(gameHandler->battleGetAllObstaclesOnPos(BattleHex(BattleHex::GATE_BRIDGE), false), [](const std::shared_ptr<const CObstacleInstance> & obst)
bool hasWideMoat = vstd::contains_if(battle.battleGetAllObstaclesOnPos(BattleHex(BattleHex::GATE_BRIDGE), false), [](const std::shared_ptr<const CObstacleInstance> & obst)
{
return obst->obstacleType == CObstacleInstance::MOAT;
});
BattleUpdateGateState db;
db.state = battle.si.gateState;
db.battleID = battle.battleID;
db.state = battle.battleGetGateState();
db.battleID = battle.getBattle()->getBattleID();
if (battle.si.wallState.at(EWallPart::GATE) == EWallState::DESTROYED)
if (battle.battleGetWallState(EWallPart::GATE) == EWallState::DESTROYED)
{
db.state = EGateState::DESTROYED;
}
@ -219,7 +220,7 @@ void BattleProcessor::updateGateState(const BattleInfo & battle)
db.state = EGateState::CLOSED;
}
if (db.state != battle.si.gateState)
if (db.state != battle.battleGetGateState())
gameHandler->sendAndApply(&db);
}
@ -236,13 +237,13 @@ bool BattleProcessor::makePlayerBattleAction(const BattleID & battleID, PlayerCo
return result;
}
void BattleProcessor::setBattleResult(const BattleInfo & battle, EBattleResult resultType, int victoriusSide)
void BattleProcessor::setBattleResult(const CBattleInfoCallback & battle, EBattleResult resultType, int victoriusSide)
{
resultProcessor->setBattleResult(battle, resultType, victoriusSide);
resultProcessor->endBattle(battle);
}
bool BattleProcessor::makeAutomaticBattleAction(const BattleInfo & battle, const BattleAction &ba)
bool BattleProcessor::makeAutomaticBattleAction(const CBattleInfoCallback & battle, const BattleAction &ba)
{
return actionsProcessor->makeAutomaticBattleAction(battle, ba);
}

View File

@ -17,7 +17,7 @@ class CGTownInstance;
class CArmedInstance;
class BattleAction;
class int3;
class BattleInfo;
class CBattleInfoCallback;
struct BattleResult;
class BattleID;
VCMI_LIB_NAMESPACE_END
@ -40,16 +40,15 @@ class BattleProcessor : boost::noncopyable
std::unique_ptr<BattleFlowProcessor> flowProcessor;
std::unique_ptr<BattleResultProcessor> resultProcessor;
void onTacticsEnded();
void updateGateState(const BattleInfo & battle);
void updateGateState(const CBattleInfoCallback & battle);
void engageIntoBattle(PlayerColor player);
bool checkBattleStateChanges(const BattleInfo & battle);
bool checkBattleStateChanges(const CBattleInfoCallback & battle);
BattleID setupBattle(int3 tile, const CArmedInstance *armies[2], const CGHeroInstance *heroes[2], bool creatureBank, const CGTownInstance *town);
bool makeAutomaticBattleAction(const BattleInfo & battle, const BattleAction & ba);
bool makeAutomaticBattleAction(const CBattleInfoCallback & battle, const BattleAction & ba);
void setBattleResult(const BattleInfo & battle, EBattleResult resultType, int victoriusSide);
void setBattleResult(const CBattleInfoCallback & battle, EBattleResult resultType, int victoriusSide);
public:
explicit BattleProcessor(CGameHandler * gameHandler);

View File

@ -18,7 +18,9 @@
#include "../../lib/ArtifactUtils.h"
#include "../../lib/CStack.h"
#include "../../lib/GameSettings.h"
#include "../../lib/battle/BattleInfo.h"
#include "../../lib/battle/CBattleInfoCallback.h"
#include "../../lib/battle/IBattleState.h"
#include "../../lib/battle/SideInBattle.h"
#include "../../lib/gameState/CGameState.h"
#include "../../lib/mapObjects/CGTownInstance.h"
#include "../../lib/serializer/Cast.h"
@ -35,15 +37,19 @@ void BattleResultProcessor::setGameHandler(CGameHandler * newGameHandler)
gameHandler = newGameHandler;
}
CasualtiesAfterBattle::CasualtiesAfterBattle(const SideInBattle & battleSide, const BattleInfo * bat):
army(battleSide.armyObject)
CasualtiesAfterBattle::CasualtiesAfterBattle(const CBattleInfoCallback & battle, uint8_t sideInBattle):
army(battle.battleGetArmyObject(sideInBattle))
{
heroWithDeadCommander = ObjectInstanceID();
PlayerColor color = battleSide.color;
PlayerColor color = battle.sideToPlayer(sideInBattle);
for(CStack * st : bat->stacks)
for(const CStack * stConst : battle.battleGetAllStacks(true))
{
// Use const cast - in order to call non-const "takeResurrected" for proper calculation of casualties
// TODO: better solution
CStack * st = const_cast<CStack*>(stConst);
if(st->summoned) //don't take into account temporary summoned stacks
continue;
if(st->unitOwner() != color) //remove only our stacks
@ -181,11 +187,23 @@ FinishingBattleHelper::FinishingBattleHelper(std::shared_ptr<const CBattleQuery>
auto &result = *Query->result;
auto &info = *Query->bi;
winnerHero = result.winner != 0 ? info.sides[1].hero : info.sides[0].hero;
loserHero = result.winner != 0 ? info.sides[0].hero : info.sides[1].hero;
victor = info.sides[result.winner].color;
loser = info.sides[!result.winner].color;
if (result.winner == BattleSide::ATTACKER)
{
winnerHero = info.getSideHero(BattleSide::ATTACKER);
loserHero = info.getSideHero(BattleSide::DEFENDER);
victor = info.getSidePlayer(BattleSide::ATTACKER);
loser = info.getSidePlayer(BattleSide::DEFENDER);
}
else
{
winnerHero = info.getSideHero(BattleSide::DEFENDER);
loserHero = info.getSideHero(BattleSide::ATTACKER);
victor = info.getSidePlayer(BattleSide::DEFENDER);
loser = info.getSidePlayer(BattleSide::ATTACKER);
}
winnerSide = result.winner;
this->remainingBattleQueriesCount = remainingBattleQueriesCount;
}
@ -196,7 +214,7 @@ FinishingBattleHelper::FinishingBattleHelper()
remainingBattleQueriesCount = 0;
}
void BattleResultProcessor::endBattle(const BattleInfo & battle)
void BattleResultProcessor::endBattle(const CBattleInfoCallback & battle)
{
auto const & giveExp = [](BattleResult &r)
{
@ -215,9 +233,9 @@ void BattleResultProcessor::endBattle(const BattleInfo & battle)
LOG_TRACE(logGlobal);
auto * battleResult = battleResults.at(battle.battleID).get();
const auto * heroAttacker = battle.getSideHero(BattleSide::ATTACKER);
const auto * heroDefender = battle.getSideHero(BattleSide::DEFENDER);
auto * battleResult = battleResults.at(battle.getBattle()->getBattleID()).get();
const auto * heroAttacker = battle.battleGetFightingHero(BattleSide::ATTACKER);
const auto * heroDefender = battle.battleGetFightingHero(BattleSide::DEFENDER);
//Fill BattleResult structure with exp info
giveExp(*battleResult);
@ -235,11 +253,11 @@ void BattleResultProcessor::endBattle(const BattleInfo & battle)
if(heroDefender)
battleResult->exp[1] = heroDefender->calculateXp(battleResult->exp[1]);
auto battleQuery = std::dynamic_pointer_cast<CBattleQuery>(gameHandler->queries->topQuery(battle.sides[0].color));
auto battleQuery = std::dynamic_pointer_cast<CBattleQuery>(gameHandler->queries->topQuery(battle.sideToPlayer(0)));
if (!battleQuery)
{
logGlobal->error("Cannot find battle query!");
gameHandler->complain("Player " + boost::lexical_cast<std::string>(battle.sides[0].color) + " has no battle query at the top!");
gameHandler->complain("Player " + boost::lexical_cast<std::string>(battle.sideToPlayer(0)) + " has no battle query at the top!");
return;
}
@ -248,13 +266,13 @@ void BattleResultProcessor::endBattle(const BattleInfo & battle)
//Check how many battle gameHandler->queries were created (number of players blocked by battle)
const int queriedPlayers = battleQuery ? (int)boost::count(gameHandler->queries->allQueries(), battleQuery) : 0;
assert(finishingBattles.count(battle.battleID) == 0);
finishingBattles[battle.battleID] = std::make_unique<FinishingBattleHelper>(battleQuery, queriedPlayers);
assert(finishingBattles.count(battle.getBattle()->getBattleID()) == 0);
finishingBattles[battle.getBattle()->getBattleID()] = std::make_unique<FinishingBattleHelper>(battleQuery, queriedPlayers);
// in battles against neutrals, 1st player can ask to replay battle manually
if (!battle.sides[1].color.isValidPlayer())
if (!battle.sideToPlayer(1).isValidPlayer())
{
auto battleDialogQuery = std::make_shared<CBattleDialogQuery>(gameHandler, &battle);
auto battleDialogQuery = std::make_shared<CBattleDialogQuery>(gameHandler, battle.getBattle());
battleResult->queryID = battleDialogQuery->queryID;
gameHandler->queries->addQuery(battleDialogQuery);
}
@ -275,21 +293,24 @@ void BattleResultProcessor::endBattle(const BattleInfo & battle)
endBattleConfirm(battle);
}
void BattleResultProcessor::endBattleConfirm(const BattleInfo & battle)
void BattleResultProcessor::endBattleConfirm(const CBattleInfoCallback & battle)
{
auto battleQuery = std::dynamic_pointer_cast<CBattleQuery>(gameHandler->queries->topQuery(battle.sides.at(0).color));
auto battleQuery = std::dynamic_pointer_cast<CBattleQuery>(gameHandler->queries->topQuery(battle.sideToPlayer(0)));
if(!battleQuery)
{
logGlobal->trace("No battle query, battle end was confirmed by another player");
return;
}
auto * battleResult = battleResults.at(battle.battleID).get();
auto * finishingBattle = finishingBattles.at(battle.battleID).get();
auto * battleResult = battleResults.at(battle.getBattle()->getBattleID()).get();
auto * finishingBattle = finishingBattles.at(battle.getBattle()->getBattleID()).get();
const EBattleResult result = battleResult->result;
CasualtiesAfterBattle cab1(battle.sides.at(0), &battle), cab2(battle.sides.at(1), &battle); //calculate casualties before deleting battle
//calculate casualties before deleting battle
CasualtiesAfterBattle cab1(battle, BattleSide::ATTACKER);
CasualtiesAfterBattle cab2(battle, BattleSide::DEFENDER);
ChangeSpells cs; //for Eagle Eye
if(!finishingBattle->isDraw() && finishingBattle->winnerHero)
@ -297,7 +318,7 @@ void BattleResultProcessor::endBattleConfirm(const BattleInfo & battle)
if (int eagleEyeLevel = finishingBattle->winnerHero->valOfBonuses(BonusType::LEARN_BATTLE_SPELL_LEVEL_LIMIT, -1))
{
double eagleEyeChance = finishingBattle->winnerHero->valOfBonuses(BonusType::LEARN_BATTLE_SPELL_CHANCE, 0);
for(auto & spellId : battle.sides.at(!battleResult->winner).usedSpellsHistory)
for(auto & spellId : battle.getBattle()->getUsedSpells(battle.otherSide(battleResult->winner)))
{
auto spell = spellId.toSpell(VLC->spells());
if(spell && spell->getLevel() <= eagleEyeLevel && !finishingBattle->winnerHero->spellbookContainsSpell(spell->getId()) && gameHandler->getRandomGenerator().nextInt(99) < eagleEyeChance)
@ -365,7 +386,10 @@ void BattleResultProcessor::endBattleConfirm(const BattleInfo & battle)
}
}
}
for (auto armySlot : battle.sides.at(!battleResult->winner).armyObject->stacks)
auto loser = battle.otherSide(battleResult->winner);
for (auto armySlot : battle.battleGetArmyObject(loser)->stacks)
{
auto artifactsWorn = armySlot.second->artifactsWorn;
for (auto artSlot : artifactsWorn)
@ -466,10 +490,10 @@ void BattleResultProcessor::endBattleConfirm(const BattleInfo & battle)
gameHandler->changePrimSkill(finishingBattle->winnerHero, PrimarySkill::EXPERIENCE, battleResult->exp[finishingBattle->winnerSide]);
BattleResultAccepted raccepted;
raccepted.heroResult[0].army = const_cast<CArmedInstance*>(battle.sides.at(0).armyObject);
raccepted.heroResult[1].army = const_cast<CArmedInstance*>(battle.sides.at(1).armyObject);
raccepted.heroResult[0].hero = const_cast<CGHeroInstance*>(battle.sides.at(0).hero);
raccepted.heroResult[1].hero = const_cast<CGHeroInstance*>(battle.sides.at(1).hero);
raccepted.heroResult[0].army = const_cast<CArmedInstance*>(battle.battleGetArmyObject(0));
raccepted.heroResult[1].army = const_cast<CArmedInstance*>(battle.battleGetArmyObject(1));
raccepted.heroResult[0].hero = const_cast<CGHeroInstance*>(battle.battleGetFightingHero(0));
raccepted.heroResult[1].hero = const_cast<CGHeroInstance*>(battle.battleGetFightingHero(1));
raccepted.heroResult[0].exp = battleResult->exp[0];
raccepted.heroResult[1].exp = battleResult->exp[1];
raccepted.winnerSide = finishingBattle->winnerSide;
@ -479,15 +503,15 @@ void BattleResultProcessor::endBattleConfirm(const BattleInfo & battle)
//--> continuation (battleAfterLevelUp) occurs after level-up gameHandler->queries are handled or on removing query
}
void BattleResultProcessor::battleAfterLevelUp(const BattleInfo & battle, const BattleResult & result)
void BattleResultProcessor::battleAfterLevelUp(const CBattleInfoCallback & battle, const BattleResult & result)
{
LOG_TRACE(logGlobal);
assert(finishingBattles.count(battle.battleID) != 0);
if(finishingBattles.count(battle.battleID) == 0)
assert(finishingBattles.count(battle.getBattle()->getBattleID()) != 0);
if(finishingBattles.count(battle.getBattle()->getBattleID()) == 0)
return;
auto & finishingBattle = finishingBattles[battle.battleID];
auto & finishingBattle = finishingBattles[battle.getBattle()->getBattleID()];
finishingBattle->remainingBattleQueriesCount--;
logGlobal->trace("Decremented gameHandler->queries count to %d", finishingBattle->remainingBattleQueriesCount);
@ -517,8 +541,6 @@ void BattleResultProcessor::battleAfterLevelUp(const BattleInfo & battle, const
resultsApplied.player2 = finishingBattle->loser;
gameHandler->sendAndApply(&resultsApplied);
gameHandler->setBattle(nullptr);
//handle victory/loss of engaged players
std::set<PlayerColor> playerColors = {finishingBattle->loser, finishingBattle->victor};
gameHandler->checkVictoryLossConditions(playerColors);
@ -539,23 +561,29 @@ void BattleResultProcessor::battleAfterLevelUp(const BattleInfo & battle, const
gameHandler->heroPool->onHeroEscaped(finishingBattle->victor, finishingBattle->winnerHero);
}
finishingBattles.erase(battle.battleID);
battleResults.erase(battle.battleID);
finishingBattles.erase(battle.getBattle()->getBattleID());
battleResults.erase(battle.getBattle()->getBattleID());
}
void BattleResultProcessor::setBattleResult(const BattleInfo & battle, EBattleResult resultType, int victoriusSide)
void BattleResultProcessor::setBattleResult(const CBattleInfoCallback & battle, EBattleResult resultType, int victoriusSide)
{
assert(battleResults.count(battle.battleID) == 0);
assert(battleResults.count(battle.getBattle()->getBattleID()) == 0);
battleResults[battle.battleID] = std::make_unique<BattleResult>();
battleResults[battle.getBattle()->getBattleID()] = std::make_unique<BattleResult>();
auto & battleResult = battleResults[battle.battleID];
auto & battleResult = battleResults[battle.getBattle()->getBattleID()];
battleResult->result = resultType;
battleResult->winner = victoriusSide; //surrendering side loses
battle.calculateCasualties(battleResult->casualties);
for(const auto & st : battle.battleGetAllStacks(true)) //setting casualties
{
si32 killed = st->getKilled();
if(killed > 0)
battleResult->casualties[st->unitSide()][st->creatureId()] += killed;
}
}
bool BattleResultProcessor::battleIsEnding(const BattleInfo & battle) const
bool BattleResultProcessor::battleIsEnding(const CBattleInfoCallback & battle) const
{
return battleResults.count(battle.battleID) != 0;
return battleResults.count(battle.getBattle()->getBattleID()) != 0;
}

View File

@ -31,7 +31,7 @@ struct CasualtiesAfterBattle
TSummoned summoned;
ObjectInstanceID heroWithDeadCommander; //TODO: unify stack locations
CasualtiesAfterBattle(const SideInBattle & battleSide, const BattleInfo * bat);
CasualtiesAfterBattle(const CBattleInfoCallback & battle, uint8_t sideInBattle);
void updateArmy(CGameHandler * gh);
};
@ -71,10 +71,10 @@ public:
explicit BattleResultProcessor(BattleProcessor * owner);
void setGameHandler(CGameHandler * newGameHandler);
bool battleIsEnding(const BattleInfo & battle) const;
bool battleIsEnding(const CBattleInfoCallback & battle) const;
void setBattleResult(const BattleInfo & battle, EBattleResult resultType, int victoriusSide);
void endBattle(const BattleInfo & battle); //ends battle
void endBattleConfirm(const BattleInfo & battle);
void battleAfterLevelUp(const BattleInfo & battle, const BattleResult & result);
void setBattleResult(const CBattleInfoCallback & battle, EBattleResult resultType, int victoriusSide);
void endBattle(const CBattleInfoCallback & battle); //ends battle
void endBattleConfirm(const CBattleInfoCallback & battle);
void battleAfterLevelUp(const CBattleInfoCallback & battle, const BattleResult & result);
};

View File

View File

View File

@ -14,7 +14,7 @@
#include "../CGameHandler.h"
#include "../battles/BattleProcessor.h"
#include "../../lib/battle/BattleInfo.h"
#include "../../lib/battle/IBattleState.h"
void CBattleQuery::notifyObjectAboutRemoval(const CObjectVisitQuery & objectVisit) const
{
@ -22,16 +22,15 @@ void CBattleQuery::notifyObjectAboutRemoval(const CObjectVisitQuery & objectVisi
objectVisit.visitedObject->battleFinished(objectVisit.visitingHero, *result);
}
CBattleQuery::CBattleQuery(CGameHandler * owner, const BattleInfo * Bi):
CGhQuery(owner)
CBattleQuery::CBattleQuery(CGameHandler * owner, const IBattleInfo * bi):
CGhQuery(owner),
bi(bi)
{
belligerents[0] = Bi->sides[0].armyObject;
belligerents[1] = Bi->sides[1].armyObject;
belligerents[0] = bi->getSideArmy(0);
belligerents[1] = bi->getSideArmy(1);
bi = Bi;
for(auto & side : bi->sides)
addPlayer(side.color);
addPlayer(bi->getSidePlayer(0));
addPlayer(bi->getSidePlayer(1));
}
CBattleQuery::CBattleQuery(CGameHandler * owner):
@ -49,16 +48,15 @@ bool CBattleQuery::blocksPack(const CPack * pack) const
void CBattleQuery::onRemoval(PlayerColor color)
{
if(result)
gh->battles->battleAfterLevelUp(bi->battleID, *result);
gh->battles->battleAfterLevelUp(bi->getBattleID(), *result);
}
CBattleDialogQuery::CBattleDialogQuery(CGameHandler * owner, const BattleInfo * Bi):
CDialogQuery(owner)
CBattleDialogQuery::CBattleDialogQuery(CGameHandler * owner, const IBattleInfo * bi):
CDialogQuery(owner),
bi(bi)
{
bi = Bi;
for(auto & side : bi->sides)
addPlayer(side.color);
addPlayer(bi->getSidePlayer(0));
addPlayer(bi->getSidePlayer(1));
}
void CBattleDialogQuery::onRemoval(PlayerColor color)
@ -66,10 +64,18 @@ void CBattleDialogQuery::onRemoval(PlayerColor color)
assert(answer);
if(*answer == 1)
{
gh->startBattlePrimary(bi->sides[0].armyObject, bi->sides[1].armyObject, bi->tile, bi->sides[0].hero, bi->sides[1].hero, bi->creatureBank, bi->town);
gh->startBattlePrimary(
bi->getSideArmy(0),
bi->getSideArmy(1),
bi->getLocation(),
bi->getSideHero(0),
bi->getSideHero(1),
bi->isCreatureBank(),
bi->getDefendedTown()
);
}
else
{
gh->battles->endBattleConfirm(bi->battleID);
gh->battles->endBattleConfirm(bi->getBattleID());
}
}

View File

@ -13,17 +13,21 @@
#include "../../lib/NetPacks.h"
VCMI_LIB_NAMESPACE_BEGIN
class IBattleInfo;
VCMI_LIB_NAMESPACE_END
class CBattleQuery : public CGhQuery
{
public:
std::array<const CArmedInstance *,2> belligerents;
std::array<int, 2> initialHeroMana;
const BattleInfo *bi;
const IBattleInfo *bi;
std::optional<BattleResult> result;
CBattleQuery(CGameHandler * owner);
CBattleQuery(CGameHandler * owner, const BattleInfo * Bi); //TODO
CBattleQuery(CGameHandler * owner, const IBattleInfo * Bi); //TODO
virtual void notifyObjectAboutRemoval(const CObjectVisitQuery &objectVisit) const override;
virtual bool blocksPack(const CPack *pack) const override;
virtual void onRemoval(PlayerColor color) override;
@ -32,9 +36,9 @@ public:
class CBattleDialogQuery : public CDialogQuery
{
public:
CBattleDialogQuery(CGameHandler * owner, const BattleInfo * Bi);
CBattleDialogQuery(CGameHandler * owner, const IBattleInfo * Bi);
const BattleInfo * bi;
const IBattleInfo * bi;
virtual void onRemoval(PlayerColor color) override;
};