mirror of
https://github.com/vcmi/vcmi.git
synced 2025-01-12 02:28:11 +02:00
Added support for concurrent battles to gamestate and server
This commit is contained in:
parent
a21de70516
commit
fc4dfda00f
@ -201,8 +201,10 @@ int32_t CGameInfoCallback::getSpellCost(const spells::Spell * sp, const CGHeroIn
|
||||
//boost::shared_lock<boost::shared_mutex> lock(*gs->mx);
|
||||
ERROR_RET_VAL_IF(!canGetFullInfo(caster), "Cannot get info about caster!", -1);
|
||||
//if there is a battle
|
||||
if(gs->curB)
|
||||
return gs->curB->battleGetSpellCost(sp, caster);
|
||||
auto casterBattle = gs->getBattle(caster->getOwner());
|
||||
|
||||
if(casterBattle)
|
||||
return casterBattle->battleGetSpellCost(sp, caster);
|
||||
|
||||
//if there is no battle
|
||||
return caster->getSpellCost(sp);
|
||||
@ -303,7 +305,9 @@ bool CGameInfoCallback::getHeroInfo(const CGObjectInstance * hero, InfoAboutHero
|
||||
|
||||
if (infoLevel == InfoAboutHero::EInfoLevel::BASIC)
|
||||
{
|
||||
if(gs->curB && gs->curB->playerHasAccessToHeroInfo(*player, h)) //if it's battle we can get enemy hero full data
|
||||
auto ourBattle = gs->getBattle(*player);
|
||||
|
||||
if(ourBattle && ourBattle->playerHasAccessToHeroInfo(*player, h)) //if it's battle we can get enemy hero full data
|
||||
infoLevel = InfoAboutHero::EInfoLevel::INBATTLE;
|
||||
else
|
||||
ERROR_RET_VAL_IF(!isVisible(h->visitablePos()), "That hero is not visible!", false);
|
||||
|
@ -46,7 +46,7 @@ class CGDwelling;
|
||||
class CGTeleport;
|
||||
class CGTownInstance;
|
||||
|
||||
class DLL_LINKAGE IGameInfoCallback
|
||||
class DLL_LINKAGE IGameInfoCallback : boost::noncopyable
|
||||
{
|
||||
public:
|
||||
//TODO: all other public methods of CGameInfoCallback
|
||||
|
@ -1483,12 +1483,14 @@ struct DLL_LINKAGE BattleStart : public CPackForClient
|
||||
{
|
||||
void applyGs(CGameState * gs) const;
|
||||
|
||||
BattleID battleID = BattleID::NONE;
|
||||
BattleInfo * info = nullptr;
|
||||
|
||||
virtual void visitTyped(ICPackVisitor & visitor) override;
|
||||
|
||||
template <typename Handler> void serialize(Handler & h, const int version)
|
||||
{
|
||||
h & battleID;
|
||||
h & info;
|
||||
}
|
||||
};
|
||||
@ -1496,12 +1498,15 @@ struct DLL_LINKAGE BattleStart : public CPackForClient
|
||||
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;
|
||||
}
|
||||
};
|
||||
@ -1510,6 +1515,7 @@ struct DLL_LINKAGE BattleSetActiveStack : public CPackForClient
|
||||
{
|
||||
void applyGs(CGameState * gs) const;
|
||||
|
||||
BattleID battleID = BattleID::NONE;
|
||||
ui32 stack = 0;
|
||||
ui8 askPlayerInterface = true;
|
||||
|
||||
@ -1517,6 +1523,7 @@ struct DLL_LINKAGE BattleSetActiveStack : public CPackForClient
|
||||
|
||||
template <typename Handler> void serialize(Handler & h, const int version)
|
||||
{
|
||||
h & battleID;
|
||||
h & stack;
|
||||
h & askPlayerInterface;
|
||||
}
|
||||
@ -1542,11 +1549,14 @@ struct DLL_LINKAGE BattleResultAccepted : public CPackForClient
|
||||
h & exp;
|
||||
}
|
||||
};
|
||||
|
||||
BattleID battleID = BattleID::NONE;
|
||||
std::array<HeroBattleResults, 2> heroResult;
|
||||
ui8 winnerSide;
|
||||
|
||||
template <typename Handler> void serialize(Handler & h, const int version)
|
||||
{
|
||||
h & battleID;
|
||||
h & heroResult;
|
||||
h & winnerSide;
|
||||
}
|
||||
@ -1556,6 +1566,7 @@ struct DLL_LINKAGE BattleResult : public Query
|
||||
{
|
||||
void applyFirstCl(CClient * cl);
|
||||
|
||||
BattleID battleID = BattleID::NONE;
|
||||
EBattleResult result = EBattleResult::NORMAL;
|
||||
ui8 winner = 2; //0 - attacker, 1 - defender, [2 - draw (should be possible?)]
|
||||
std::map<ui32, si32> casualties[2]; //first => casualties of attackers - map crid => number
|
||||
@ -1566,6 +1577,7 @@ struct DLL_LINKAGE BattleResult : public Query
|
||||
|
||||
template <typename Handler> void serialize(Handler & h, const int version)
|
||||
{
|
||||
h & battleID;
|
||||
h & queryID;
|
||||
h & result;
|
||||
h & winner;
|
||||
@ -1578,6 +1590,7 @@ struct DLL_LINKAGE BattleResult : public Query
|
||||
|
||||
struct DLL_LINKAGE BattleLogMessage : public CPackForClient
|
||||
{
|
||||
BattleID battleID = BattleID::NONE;
|
||||
std::vector<MetaString> lines;
|
||||
|
||||
void applyGs(CGameState * gs);
|
||||
@ -1587,12 +1600,14 @@ struct DLL_LINKAGE BattleLogMessage : public CPackForClient
|
||||
|
||||
template <typename Handler> void serialize(Handler & h, const int version)
|
||||
{
|
||||
h & battleID;
|
||||
h & lines;
|
||||
}
|
||||
};
|
||||
|
||||
struct DLL_LINKAGE BattleStackMoved : public CPackForClient
|
||||
{
|
||||
BattleID battleID = BattleID::NONE;
|
||||
ui32 stack = 0;
|
||||
std::vector<BattleHex> tilesToMove;
|
||||
int distance = 0;
|
||||
@ -1605,6 +1620,7 @@ struct DLL_LINKAGE BattleStackMoved : public CPackForClient
|
||||
|
||||
template <typename Handler> void serialize(Handler & h, const int version)
|
||||
{
|
||||
h & battleID;
|
||||
h & stack;
|
||||
h & tilesToMove;
|
||||
h & distance;
|
||||
@ -1617,12 +1633,14 @@ struct DLL_LINKAGE BattleUnitsChanged : public CPackForClient
|
||||
void applyGs(CGameState * gs);
|
||||
void applyBattle(IBattleState * battleState);
|
||||
|
||||
BattleID battleID = BattleID::NONE;
|
||||
std::vector<UnitChanges> changedStacks;
|
||||
|
||||
virtual void visitTyped(ICPackVisitor & visitor) override;
|
||||
|
||||
template <typename Handler> void serialize(Handler & h, const int version)
|
||||
{
|
||||
h & battleID;
|
||||
h & changedStacks;
|
||||
}
|
||||
};
|
||||
@ -1632,6 +1650,7 @@ struct BattleStackAttacked
|
||||
DLL_LINKAGE void applyGs(CGameState * gs);
|
||||
DLL_LINKAGE void applyBattle(IBattleState * battleState);
|
||||
|
||||
BattleID battleID = BattleID::NONE;
|
||||
ui32 stackAttacked = 0, attackerID = 0;
|
||||
ui32 killedAmount = 0;
|
||||
int64_t damageAmount = 0;
|
||||
@ -1668,6 +1687,7 @@ struct BattleStackAttacked
|
||||
|
||||
template <typename Handler> void serialize(Handler & h, const int version)
|
||||
{
|
||||
h & battleID;
|
||||
h & stackAttacked;
|
||||
h & attackerID;
|
||||
h & newState;
|
||||
@ -1687,6 +1707,7 @@ struct DLL_LINKAGE BattleAttack : public CPackForClient
|
||||
void applyGs(CGameState * gs);
|
||||
BattleUnitsChanged attackerChanges;
|
||||
|
||||
BattleID battleID = BattleID::NONE;
|
||||
std::vector<BattleStackAttacked> bsa;
|
||||
ui32 stackAttacking = 0;
|
||||
ui32 flags = 0; //uses Eflags (below)
|
||||
@ -1732,6 +1753,7 @@ struct DLL_LINKAGE BattleAttack : public CPackForClient
|
||||
|
||||
template <typename Handler> void serialize(Handler & h, const int version)
|
||||
{
|
||||
h & battleID;
|
||||
h & bsa;
|
||||
h & stackAttacking;
|
||||
h & flags;
|
||||
@ -1751,12 +1773,14 @@ struct DLL_LINKAGE StartAction : public CPackForClient
|
||||
void applyFirstCl(CClient * cl);
|
||||
void applyGs(CGameState * gs);
|
||||
|
||||
BattleID battleID = BattleID::NONE;
|
||||
BattleAction ba;
|
||||
|
||||
virtual void visitTyped(ICPackVisitor & visitor) override;
|
||||
|
||||
template <typename Handler> void serialize(Handler & h, const int version)
|
||||
{
|
||||
h & battleID;
|
||||
h & ba;
|
||||
}
|
||||
};
|
||||
@ -1765,14 +1789,19 @@ struct DLL_LINKAGE EndAction : public CPackForClient
|
||||
{
|
||||
virtual void visitTyped(ICPackVisitor & visitor) override;
|
||||
|
||||
BattleID battleID = BattleID::NONE;
|
||||
|
||||
template <typename Handler> void serialize(Handler & h, const int version)
|
||||
{
|
||||
h & battleID;
|
||||
}
|
||||
};
|
||||
|
||||
struct DLL_LINKAGE BattleSpellCast : public CPackForClient
|
||||
{
|
||||
void applyGs(CGameState * gs) const;
|
||||
|
||||
BattleID battleID = BattleID::NONE;
|
||||
bool activeCast = true;
|
||||
ui8 side = 0; //which hero did cast spell: 0 - attacker, 1 - defender
|
||||
SpellID spellID; //id of spell
|
||||
@ -1788,6 +1817,7 @@ struct DLL_LINKAGE BattleSpellCast : public CPackForClient
|
||||
|
||||
template <typename Handler> void serialize(Handler & h, const int version)
|
||||
{
|
||||
h & battleID;
|
||||
h & side;
|
||||
h & spellID;
|
||||
h & manaGained;
|
||||
@ -1805,6 +1835,8 @@ struct DLL_LINKAGE SetStackEffect : public CPackForClient
|
||||
{
|
||||
void applyGs(CGameState * gs);
|
||||
void applyBattle(IBattleState * battleState);
|
||||
|
||||
BattleID battleID = BattleID::NONE;
|
||||
std::vector<std::pair<ui32, std::vector<Bonus>>> toAdd;
|
||||
std::vector<std::pair<ui32, std::vector<Bonus>>> toUpdate;
|
||||
std::vector<std::pair<ui32, std::vector<Bonus>>> toRemove;
|
||||
@ -1813,6 +1845,7 @@ struct DLL_LINKAGE SetStackEffect : public CPackForClient
|
||||
|
||||
template <typename Handler> void serialize(Handler & h, const int version)
|
||||
{
|
||||
h & battleID;
|
||||
h & toAdd;
|
||||
h & toUpdate;
|
||||
h & toRemove;
|
||||
@ -1824,23 +1857,27 @@ struct DLL_LINKAGE StacksInjured : public CPackForClient
|
||||
void applyGs(CGameState * gs);
|
||||
void applyBattle(IBattleState * battleState);
|
||||
|
||||
BattleID battleID = BattleID::NONE;
|
||||
std::vector<BattleStackAttacked> stacks;
|
||||
|
||||
virtual void visitTyped(ICPackVisitor & visitor) override;
|
||||
|
||||
template <typename Handler> void serialize(Handler & h, const int version)
|
||||
{
|
||||
h & battleID;
|
||||
h & stacks;
|
||||
}
|
||||
};
|
||||
|
||||
struct DLL_LINKAGE BattleResultsApplied : public CPackForClient
|
||||
{
|
||||
BattleID battleID = BattleID::NONE;
|
||||
PlayerColor player1, player2;
|
||||
virtual void visitTyped(ICPackVisitor & visitor) override;
|
||||
|
||||
template <typename Handler> void serialize(Handler & h, const int version)
|
||||
{
|
||||
h & battleID;
|
||||
h & player1;
|
||||
h & player2;
|
||||
}
|
||||
@ -1851,12 +1888,14 @@ struct DLL_LINKAGE BattleObstaclesChanged : public CPackForClient
|
||||
void applyGs(CGameState * gs);
|
||||
void applyBattle(IBattleState * battleState);
|
||||
|
||||
BattleID battleID = BattleID::NONE;
|
||||
std::vector<ObstacleChanges> changes;
|
||||
|
||||
virtual void visitTyped(ICPackVisitor & visitor) override;
|
||||
|
||||
template <typename Handler> void serialize(Handler & h, const int version)
|
||||
{
|
||||
h & battleID;
|
||||
h & changes;
|
||||
}
|
||||
};
|
||||
@ -1883,6 +1922,7 @@ struct DLL_LINKAGE CatapultAttack : public CPackForClient
|
||||
void applyGs(CGameState * gs);
|
||||
void applyBattle(IBattleState * battleState);
|
||||
|
||||
BattleID battleID = BattleID::NONE;
|
||||
std::vector< AttackInfo > attackedParts;
|
||||
int attacker = -1; //if -1, then a spell caused this
|
||||
|
||||
@ -1890,6 +1930,7 @@ struct DLL_LINKAGE CatapultAttack : public CPackForClient
|
||||
|
||||
template <typename Handler> void serialize(Handler & h, const int version)
|
||||
{
|
||||
h & battleID;
|
||||
h & attackedParts;
|
||||
h & attacker;
|
||||
}
|
||||
@ -1901,6 +1942,7 @@ struct DLL_LINKAGE BattleSetStackProperty : public CPackForClient
|
||||
|
||||
void applyGs(CGameState * gs) const;
|
||||
|
||||
BattleID battleID = BattleID::NONE;
|
||||
int stackID = 0;
|
||||
BattleStackProperty which = CASTS;
|
||||
int val = 0;
|
||||
@ -1908,6 +1950,7 @@ struct DLL_LINKAGE BattleSetStackProperty : public CPackForClient
|
||||
|
||||
template <typename Handler> void serialize(Handler & h, const int version)
|
||||
{
|
||||
h & battleID;
|
||||
h & stackID;
|
||||
h & which;
|
||||
h & val;
|
||||
@ -1923,6 +1966,7 @@ struct DLL_LINKAGE BattleTriggerEffect : public CPackForClient
|
||||
{
|
||||
void applyGs(CGameState * gs) const; //effect
|
||||
|
||||
BattleID battleID = BattleID::NONE;
|
||||
int stackID = 0;
|
||||
int effect = 0; //use corresponding Bonus type
|
||||
int val = 0;
|
||||
@ -1930,6 +1974,7 @@ struct DLL_LINKAGE BattleTriggerEffect : public CPackForClient
|
||||
|
||||
template <typename Handler> void serialize(Handler & h, const int version)
|
||||
{
|
||||
h & battleID;
|
||||
h & stackID;
|
||||
h & effect;
|
||||
h & val;
|
||||
@ -1944,9 +1989,11 @@ struct DLL_LINKAGE BattleUpdateGateState : public CPackForClient
|
||||
{
|
||||
void applyGs(CGameState * gs) const;
|
||||
|
||||
BattleID battleID = BattleID::NONE;
|
||||
EGateState state = EGateState::NONE;
|
||||
template <typename Handler> void serialize(Handler & h, const int version)
|
||||
{
|
||||
h & battleID;
|
||||
h & state;
|
||||
}
|
||||
|
||||
@ -2533,6 +2580,7 @@ struct DLL_LINKAGE MakeAction : public CPackForServer
|
||||
{
|
||||
}
|
||||
BattleAction ba;
|
||||
BattleID battleID;
|
||||
|
||||
virtual void visitTyped(ICPackVisitor & visitor) override;
|
||||
|
||||
@ -2540,6 +2588,7 @@ struct DLL_LINKAGE MakeAction : public CPackForServer
|
||||
{
|
||||
h & static_cast<CPackForServer &>(*this);
|
||||
h & ba;
|
||||
h & battleID;
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -34,11 +34,8 @@
|
||||
#include "campaign/CampaignState.h"
|
||||
#include "GameSettings.h"
|
||||
|
||||
|
||||
VCMI_LIB_NAMESPACE_BEGIN
|
||||
|
||||
#define THROW_IF_NO_BATTLE if (!gs->curB) throw std::runtime_error("Trying to apply pack when no battle!");
|
||||
|
||||
void CPack::visit(ICPackVisitor & visitor)
|
||||
{
|
||||
visitBasic(visitor);
|
||||
@ -962,7 +959,7 @@ void GiveBonus::applyGs(CGameState *gs)
|
||||
break;
|
||||
case ETarget::BATTLE:
|
||||
assert(Bonus::OneBattle(&bonus));
|
||||
cbsn = dynamic_cast<CBonusSystemNode*>(gs->curB.get());
|
||||
cbsn = dynamic_cast<CBonusSystemNode*>(gs->getBattle(BattleID(id)));
|
||||
break;
|
||||
}
|
||||
|
||||
@ -2115,26 +2112,29 @@ void CommanderLevelUp::applyGs(CGameState * gs) const
|
||||
|
||||
void BattleStart::applyGs(CGameState * gs) const
|
||||
{
|
||||
gs->curB = info;
|
||||
gs->curB->localInit();
|
||||
assert(battleID == gs->nextBattleID);
|
||||
|
||||
gs->currentBattles.emplace_back(info);
|
||||
|
||||
info->battleID = gs->nextBattleID;
|
||||
info->localInit();
|
||||
|
||||
vstd::next(gs->nextBattleID, 1);
|
||||
}
|
||||
|
||||
void BattleNextRound::applyGs(CGameState * gs) const
|
||||
{
|
||||
THROW_IF_NO_BATTLE
|
||||
gs->curB->nextRound(round);
|
||||
gs->getBattle(battleID)->nextRound(round);
|
||||
}
|
||||
|
||||
void BattleSetActiveStack::applyGs(CGameState * gs) const
|
||||
{
|
||||
THROW_IF_NO_BATTLE
|
||||
gs->curB->nextTurn(stack);
|
||||
gs->getBattle(battleID)->nextTurn(stack);
|
||||
}
|
||||
|
||||
void BattleTriggerEffect::applyGs(CGameState * gs) const
|
||||
{
|
||||
THROW_IF_NO_BATTLE
|
||||
CStack * st = gs->curB->getStack(stackID);
|
||||
CStack * st = gs->getBattle(battleID)->getStack(stackID);
|
||||
assert(st);
|
||||
switch(static_cast<BonusType>(effect))
|
||||
{
|
||||
@ -2173,8 +2173,8 @@ void BattleTriggerEffect::applyGs(CGameState * gs) const
|
||||
|
||||
void BattleUpdateGateState::applyGs(CGameState * gs) const
|
||||
{
|
||||
if(gs->curB)
|
||||
gs->curB->si.gateState = state;
|
||||
if(gs->getBattle(battleID))
|
||||
gs->getBattle(battleID)->si.gateState = state;
|
||||
}
|
||||
|
||||
void BattleResultAccepted::applyGs(CGameState * gs) const
|
||||
@ -2214,7 +2214,13 @@ void BattleResultAccepted::applyGs(CGameState * gs) const
|
||||
CBonusSystemNode::treeHasChanged();
|
||||
}
|
||||
|
||||
gs->curB.dellNull();
|
||||
auto currentBattle = boost::range::find_if(gs->currentBattles, [&](const auto & battle)
|
||||
{
|
||||
return battle->battleID == battleID;
|
||||
});
|
||||
|
||||
assert(currentBattle != gs->currentBattles.end());
|
||||
gs->currentBattles.erase(currentBattle);
|
||||
}
|
||||
|
||||
void BattleLogMessage::applyGs(CGameState *gs)
|
||||
@ -2229,8 +2235,7 @@ void BattleLogMessage::applyBattle(IBattleState * battleState)
|
||||
|
||||
void BattleStackMoved::applyGs(CGameState *gs)
|
||||
{
|
||||
THROW_IF_NO_BATTLE
|
||||
applyBattle(gs->curB);
|
||||
applyBattle(gs->getBattle(battleID));
|
||||
}
|
||||
|
||||
void BattleStackMoved::applyBattle(IBattleState * battleState)
|
||||
@ -2240,8 +2245,7 @@ void BattleStackMoved::applyBattle(IBattleState * battleState)
|
||||
|
||||
void BattleStackAttacked::applyGs(CGameState * gs)
|
||||
{
|
||||
THROW_IF_NO_BATTLE
|
||||
applyBattle(gs->curB);
|
||||
applyBattle(gs->getBattle(battleID));
|
||||
}
|
||||
|
||||
void BattleStackAttacked::applyBattle(IBattleState * battleState)
|
||||
@ -2251,8 +2255,7 @@ void BattleStackAttacked::applyBattle(IBattleState * battleState)
|
||||
|
||||
void BattleAttack::applyGs(CGameState * gs)
|
||||
{
|
||||
THROW_IF_NO_BATTLE
|
||||
CStack * attacker = gs->curB->getStack(stackAttacking);
|
||||
CStack * attacker = gs->getBattle(battleID)->getStack(stackAttacking);
|
||||
assert(attacker);
|
||||
|
||||
attackerChanges.applyGs(gs);
|
||||
@ -2265,17 +2268,15 @@ void BattleAttack::applyGs(CGameState * gs)
|
||||
|
||||
void StartAction::applyGs(CGameState *gs)
|
||||
{
|
||||
THROW_IF_NO_BATTLE
|
||||
|
||||
CStack *st = gs->curB->getStack(ba.stackNumber);
|
||||
CStack *st = gs->getBattle(battleID)->getStack(ba.stackNumber);
|
||||
|
||||
if(ba.actionType == EActionType::END_TACTIC_PHASE)
|
||||
{
|
||||
gs->curB->tacticDistance = 0;
|
||||
gs->getBattle(battleID)->tacticDistance = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
if(gs->curB->tacticDistance)
|
||||
if(gs->getBattle(battleID)->tacticDistance)
|
||||
{
|
||||
// moves in tactics phase do not affect creature status
|
||||
// (tactics stack queue is managed by client)
|
||||
@ -2310,27 +2311,24 @@ void StartAction::applyGs(CGameState *gs)
|
||||
else
|
||||
{
|
||||
if(ba.actionType == EActionType::HERO_SPELL)
|
||||
gs->curB->sides[ba.side].usedSpellsHistory.push_back(ba.spell);
|
||||
gs->getBattle(battleID)->sides[ba.side].usedSpellsHistory.push_back(ba.spell);
|
||||
}
|
||||
}
|
||||
|
||||
void BattleSpellCast::applyGs(CGameState * gs) const
|
||||
{
|
||||
THROW_IF_NO_BATTLE
|
||||
|
||||
if(castByHero)
|
||||
{
|
||||
if(side < 2)
|
||||
{
|
||||
gs->curB->sides[side].castSpellsCount++;
|
||||
gs->getBattle(battleID)->sides[side].castSpellsCount++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SetStackEffect::applyGs(CGameState *gs)
|
||||
{
|
||||
THROW_IF_NO_BATTLE
|
||||
applyBattle(gs->curB);
|
||||
applyBattle(gs->getBattle(battleID));
|
||||
}
|
||||
|
||||
void SetStackEffect::applyBattle(IBattleState * battleState)
|
||||
@ -2348,8 +2346,7 @@ void SetStackEffect::applyBattle(IBattleState * battleState)
|
||||
|
||||
void StacksInjured::applyGs(CGameState *gs)
|
||||
{
|
||||
THROW_IF_NO_BATTLE
|
||||
applyBattle(gs->curB);
|
||||
applyBattle(gs->getBattle(battleID));
|
||||
}
|
||||
|
||||
void StacksInjured::applyBattle(IBattleState * battleState)
|
||||
@ -2360,8 +2357,7 @@ void StacksInjured::applyBattle(IBattleState * battleState)
|
||||
|
||||
void BattleUnitsChanged::applyGs(CGameState *gs)
|
||||
{
|
||||
THROW_IF_NO_BATTLE
|
||||
applyBattle(gs->curB);
|
||||
applyBattle(gs->getBattle(battleID));
|
||||
}
|
||||
|
||||
void BattleUnitsChanged::applyBattle(IBattleState * battleState)
|
||||
@ -2391,8 +2387,7 @@ void BattleUnitsChanged::applyBattle(IBattleState * battleState)
|
||||
|
||||
void BattleObstaclesChanged::applyGs(CGameState * gs)
|
||||
{
|
||||
THROW_IF_NO_BATTLE;
|
||||
applyBattle(gs->curB);
|
||||
applyBattle(gs->getBattle(battleID));
|
||||
}
|
||||
|
||||
void BattleObstaclesChanged::applyBattle(IBattleState * battleState)
|
||||
@ -2423,8 +2418,7 @@ CatapultAttack::~CatapultAttack() = default;
|
||||
|
||||
void CatapultAttack::applyGs(CGameState * gs)
|
||||
{
|
||||
THROW_IF_NO_BATTLE
|
||||
applyBattle(gs->curB);
|
||||
applyBattle(gs->getBattle(battleID));
|
||||
}
|
||||
|
||||
void CatapultAttack::visitTyped(ICPackVisitor & visitor)
|
||||
@ -2450,8 +2444,7 @@ void CatapultAttack::applyBattle(IBattleState * battleState)
|
||||
|
||||
void BattleSetStackProperty::applyGs(CGameState * gs) const
|
||||
{
|
||||
THROW_IF_NO_BATTLE
|
||||
CStack * stack = gs->curB->getStack(stackID);
|
||||
CStack * stack = gs->getBattle(battleID)->getStack(stackID);
|
||||
switch(which)
|
||||
{
|
||||
case CASTS:
|
||||
@ -2464,7 +2457,7 @@ void BattleSetStackProperty::applyGs(CGameState * gs) const
|
||||
}
|
||||
case ENCHANTER_COUNTER:
|
||||
{
|
||||
auto & counter = gs->curB->sides[gs->curB->whatSide(stack->unitOwner())].enchanterCounter;
|
||||
auto & counter = gs->getBattle(battleID)->sides[gs->getBattle(battleID)->whatSide(stack->unitOwner())].enchanterCounter;
|
||||
if(absolute)
|
||||
counter = val;
|
||||
else
|
||||
|
@ -26,7 +26,7 @@
|
||||
VCMI_LIB_NAMESPACE_BEGIN
|
||||
|
||||
///BattleInfo
|
||||
std::pair< std::vector<BattleHex>, int > BattleInfo::getPath(BattleHex start, BattleHex dest, const battle::Unit * stack)
|
||||
std::pair< std::vector<BattleHex>, int > BattleInfo::getPath(BattleHex start, BattleHex dest, const battle::Unit * stack) const
|
||||
{
|
||||
auto reachability = getReachability(stack);
|
||||
|
||||
|
@ -26,6 +26,8 @@ class BattleField;
|
||||
class DLL_LINKAGE BattleInfo : public CBonusSystemNode, public CBattleInfoCallback, public IBattleState
|
||||
{
|
||||
public:
|
||||
BattleID battleID = BattleID(0);
|
||||
|
||||
enum BattleSide
|
||||
{
|
||||
ATTACKER = 0,
|
||||
@ -49,6 +51,7 @@ public:
|
||||
|
||||
template <typename Handler> void serialize(Handler &h, const int version)
|
||||
{
|
||||
h & battleID;
|
||||
h & sides;
|
||||
h & round;
|
||||
h & activeStack;
|
||||
@ -137,7 +140,7 @@ public:
|
||||
using CBattleInfoEssentials::battleGetFightingHero;
|
||||
CGHeroInstance * battleGetFightingHero(ui8 side) const;
|
||||
|
||||
std::pair< std::vector<BattleHex>, int > getPath(BattleHex start, BattleHex dest, const battle::Unit * stack); //returned value: pair<path, length>; length may be different than number of elements in path since flying creatures jump between distant hexes
|
||||
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)
|
||||
|
||||
|
@ -38,6 +38,7 @@
|
||||
|
||||
VCMI_LIB_NAMESPACE_BEGIN
|
||||
|
||||
const BattleID BattleID::NONE = BattleID(-1);
|
||||
const QueryID QueryID::NONE = QueryID(-1);
|
||||
const HeroTypeID HeroTypeID::NONE = HeroTypeID(-1);
|
||||
const ObjectInstanceID ObjectInstanceID::NONE = ObjectInstanceID(-1);
|
||||
|
@ -180,6 +180,12 @@ public:
|
||||
DLL_LINKAGE static const QueryID NONE;
|
||||
};
|
||||
|
||||
class BattleID : public Identifier<QueryID>
|
||||
{
|
||||
public:
|
||||
using Identifier<QueryID>::Identifier;
|
||||
DLL_LINKAGE static const BattleID NONE;
|
||||
};
|
||||
class ObjectInstanceID : public Identifier<ObjectInstanceID>
|
||||
{
|
||||
public:
|
||||
|
@ -395,7 +395,6 @@ CGameState::CGameState()
|
||||
|
||||
CGameState::~CGameState()
|
||||
{
|
||||
curB.dellNull();
|
||||
map.dellNull();
|
||||
}
|
||||
|
||||
@ -1218,11 +1217,41 @@ void CGameState::initVisitingAndGarrisonedHeroes()
|
||||
}
|
||||
}
|
||||
|
||||
const BattleInfo * CGameState::getBattle(const PlayerColor & player) const
|
||||
{
|
||||
if (!player.isValidPlayer())
|
||||
return nullptr;
|
||||
|
||||
for (const auto & battlePtr : currentBattles)
|
||||
if (battlePtr->sides[0].color == player || battlePtr->sides[1].color == player)
|
||||
return battlePtr.get();
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const BattleInfo * CGameState::getBattle(const BattleID & battle) const
|
||||
{
|
||||
for (const auto & battlePtr : currentBattles)
|
||||
if (battlePtr->battleID == battle)
|
||||
return battlePtr.get();
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
BattleInfo * CGameState::getBattle(const BattleID & battle)
|
||||
{
|
||||
for (const auto & battlePtr : currentBattles)
|
||||
if (battlePtr->battleID == battle)
|
||||
return battlePtr.get();
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
BattleField CGameState::battleGetBattlefieldType(int3 tile, CRandomGenerator & rand)
|
||||
{
|
||||
if(!tile.valid() && curB)
|
||||
tile = curB->tile;
|
||||
else if(!tile.valid() && !curB)
|
||||
assert(tile.valid());
|
||||
|
||||
if(!tile.valid())
|
||||
return BattleField::NONE;
|
||||
|
||||
const TerrainTile &t = map->getTile(tile);
|
||||
|
@ -83,6 +83,11 @@ class DLL_LINKAGE CGameState : public CNonConstInfoCallback
|
||||
friend class CGameStateCampaign;
|
||||
|
||||
public:
|
||||
/// List of currently ongoing battles
|
||||
std::vector<std::unique_ptr<BattleInfo>> currentBattles;
|
||||
/// ID that can be allocated to next battle
|
||||
BattleID nextBattleID = BattleID(0);
|
||||
|
||||
//we have here all heroes available on this map that are not hired
|
||||
std::unique_ptr<TavernHeroesPool> heroesPool;
|
||||
|
||||
@ -98,7 +103,6 @@ public:
|
||||
void updateOnLoad(StartInfo * si);
|
||||
|
||||
ConstTransitivePtr<StartInfo> scenarioOps, initialOpts; //second one is a copy of settings received from pregame (not randomized)
|
||||
ConstTransitivePtr<BattleInfo> curB; //current battle
|
||||
ui32 day; //total number of days in game
|
||||
ConstTransitivePtr<CMap> map;
|
||||
std::map<PlayerColor, PlayerState> players;
|
||||
@ -124,6 +128,13 @@ public:
|
||||
std::vector<CGObjectInstance*> guardingCreatures (int3 pos) const;
|
||||
void updateRumor();
|
||||
|
||||
/// Returns battle in which selected player is engaged, or nullptr if none.
|
||||
/// Can NOT be used with neutral player, use battle by ID instead
|
||||
const BattleInfo * getBattle(const PlayerColor & player) const;
|
||||
/// Returns battle by its unique identifier, or nullptr if not found
|
||||
const BattleInfo * getBattle(const BattleID & battle) const;
|
||||
BattleInfo * getBattle(const BattleID & battle);
|
||||
|
||||
// ----- victory, loss condition checks -----
|
||||
|
||||
EVictoryLossCheckResult checkForVictoryAndLoss(const PlayerColor & player) const;
|
||||
|
@ -40,6 +40,7 @@
|
||||
#include "../lib/VCMI_Lib.h"
|
||||
#include "../lib/int3.h"
|
||||
|
||||
#include "../lib/battle/BattleInfo.h"
|
||||
#include "../lib/filesystem/FileInfo.h"
|
||||
#include "../lib/filesystem/Filesystem.h"
|
||||
#include "../lib/gameState/CGameState.h"
|
||||
@ -1007,6 +1008,7 @@ void CGameHandler::run(bool resume)
|
||||
clockLast += clockDuration;
|
||||
turnTimerHandler.update(timePassed);
|
||||
boost::this_thread::sleep_for(boost::chrono::milliseconds(100));
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -276,7 +276,7 @@ void ApplyGhNetPackVisitor::visitMakeAction(MakeAction & pack)
|
||||
{
|
||||
gh.throwIfWrongPlayer(&pack);
|
||||
|
||||
result = gh.battles->makePlayerBattleAction(pack.player, pack.ba);
|
||||
result = gh.battles->makePlayerBattleAction(pack.battleID, pack.player, pack.ba);
|
||||
}
|
||||
|
||||
void ApplyGhNetPackVisitor::visitDigWithHero(DigWithHero & pack)
|
||||
|
@ -91,8 +91,8 @@ void TurnTimerHandler::update(int waitTime)
|
||||
if(gs->isPlayerMakingTurn(player))
|
||||
onPlayerMakingTurn(player, waitTime);
|
||||
|
||||
if(gs->curB)
|
||||
onBattleLoop(waitTime);
|
||||
for (auto & battle : gs->currentBattles)
|
||||
onBattleLoop(battle->battleID, waitTime);
|
||||
}
|
||||
}
|
||||
|
||||
@ -140,11 +140,11 @@ void TurnTimerHandler::onPlayerMakingTurn(PlayerColor player, int waitTime)
|
||||
}
|
||||
}
|
||||
|
||||
bool TurnTimerHandler::isPvpBattle() const
|
||||
bool TurnTimerHandler::isPvpBattle(const BattleID & battleID) const
|
||||
{
|
||||
const auto * gs = gameHandler.gameState();
|
||||
auto attacker = gs->curB->getSidePlayer(BattleSide::ATTACKER);
|
||||
auto defender = gs->curB->getSidePlayer(BattleSide::DEFENDER);
|
||||
auto attacker = gs->getBattle(battleID)->getSidePlayer(BattleSide::ATTACKER);
|
||||
auto defender = gs->getBattle(battleID)->getSidePlayer(BattleSide::DEFENDER);
|
||||
if(attacker.isValidPlayer() && defender.isValidPlayer())
|
||||
{
|
||||
const auto * attackerState = gameHandler.getPlayerState(attacker);
|
||||
@ -155,18 +155,18 @@ bool TurnTimerHandler::isPvpBattle() const
|
||||
return false;
|
||||
}
|
||||
|
||||
void TurnTimerHandler::onBattleStart()
|
||||
void TurnTimerHandler::onBattleStart(const BattleID & battleID)
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> guard(mx);
|
||||
const auto * gs = gameHandler.gameState();
|
||||
const auto * si = gameHandler.getStartInfo();
|
||||
if(!si || !gs || !gs->curB)
|
||||
if(!si || !gs)
|
||||
return;
|
||||
|
||||
auto attacker = gs->curB->getSidePlayer(BattleSide::ATTACKER);
|
||||
auto defender = gs->curB->getSidePlayer(BattleSide::DEFENDER);
|
||||
auto attacker = gs->getBattle(battleID)->getSidePlayer(BattleSide::ATTACKER);
|
||||
auto defender = gs->getBattle(battleID)->getSidePlayer(BattleSide::DEFENDER);
|
||||
|
||||
bool pvpBattle = isPvpBattle();
|
||||
bool pvpBattle = isPvpBattle(battleID);
|
||||
|
||||
for(auto i : {attacker, defender})
|
||||
{
|
||||
@ -183,18 +183,24 @@ void TurnTimerHandler::onBattleStart()
|
||||
}
|
||||
}
|
||||
|
||||
void TurnTimerHandler::onBattleEnd()
|
||||
void TurnTimerHandler::onBattleEnd(const BattleID & battleID)
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> guard(mx);
|
||||
const auto * gs = gameHandler.gameState();
|
||||
const auto * si = gameHandler.getStartInfo();
|
||||
if(!si || !gs || !gs->curB)
|
||||
if(!si || !gs)
|
||||
{
|
||||
assert(0);
|
||||
return;
|
||||
}
|
||||
|
||||
auto attacker = gs->curB->getSidePlayer(BattleSide::ATTACKER);
|
||||
auto defender = gs->curB->getSidePlayer(BattleSide::DEFENDER);
|
||||
if (!si->turnTimerInfo.isBattleEnabled())
|
||||
return;
|
||||
|
||||
bool pvpBattle = isPvpBattle();
|
||||
auto attacker = gs->getBattle(battleID)->getSidePlayer(BattleSide::ATTACKER);
|
||||
auto defender = gs->getBattle(battleID)->getSidePlayer(BattleSide::DEFENDER);
|
||||
|
||||
bool pvpBattle = isPvpBattle(battleID);
|
||||
|
||||
for(auto i : {attacker, defender})
|
||||
{
|
||||
@ -216,15 +222,21 @@ void TurnTimerHandler::onBattleEnd()
|
||||
}
|
||||
}
|
||||
|
||||
void TurnTimerHandler::onBattleNextStack(const CStack & stack)
|
||||
void TurnTimerHandler::onBattleNextStack(const BattleID & battleID, const CStack & stack)
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> guard(mx);
|
||||
const auto * gs = gameHandler.gameState();
|
||||
const auto * si = gameHandler.getStartInfo();
|
||||
if(!si || !gs || !gs->curB || !si->turnTimerInfo.isBattleEnabled())
|
||||
if(!si || !gs || !gs->getBattle(battleID))
|
||||
{
|
||||
assert(0);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!si->turnTimerInfo.isBattleEnabled())
|
||||
return;
|
||||
|
||||
if(isPvpBattle())
|
||||
if(isPvpBattle(battleID))
|
||||
{
|
||||
auto player = stack.getOwner();
|
||||
|
||||
@ -237,29 +249,32 @@ void TurnTimerHandler::onBattleNextStack(const CStack & stack)
|
||||
}
|
||||
}
|
||||
|
||||
void TurnTimerHandler::onBattleLoop(int waitTime)
|
||||
void TurnTimerHandler::onBattleLoop(const BattleID & battleID, int waitTime)
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> guard(mx);
|
||||
const auto * gs = gameHandler.gameState();
|
||||
const auto * si = gameHandler.getStartInfo();
|
||||
if(!si || !gs || !gs->curB || !si->turnTimerInfo.isBattleEnabled())
|
||||
if(!si || !gs || !si->turnTimerInfo.isBattleEnabled())
|
||||
{
|
||||
assert(0);
|
||||
return;
|
||||
}
|
||||
|
||||
ui8 side = 0;
|
||||
const CStack * stack = nullptr;
|
||||
bool isTactisPhase = gs->curB->battleTacticDist() > 0;
|
||||
bool isTactisPhase = gs->getBattle(battleID)->battleTacticDist() > 0;
|
||||
|
||||
if(isTactisPhase)
|
||||
side = gs->curB->battleGetTacticsSide();
|
||||
side = gs->getBattle(battleID)->battleGetTacticsSide();
|
||||
else
|
||||
{
|
||||
stack = gs->curB->battleGetStackByID(gs->curB->getActiveStackID());
|
||||
stack = gs->getBattle(battleID)->battleGetStackByID(gs->getBattle(battleID)->getActiveStackID());
|
||||
if(!stack || !stack->getOwner().isValidPlayer())
|
||||
return;
|
||||
side = stack->unitSide();
|
||||
}
|
||||
|
||||
auto player = gs->curB->getSidePlayer(side);
|
||||
auto player = gs->getBattle(battleID)->getSidePlayer(side);
|
||||
if(!player.isValidPlayer())
|
||||
return;
|
||||
|
||||
@ -270,7 +285,7 @@ void TurnTimerHandler::onBattleLoop(int waitTime)
|
||||
auto & timer = timers[player];
|
||||
if(timer.isActive && timer.isBattle && !timerCountDown(timer.creatureTimer, si->turnTimerInfo.creatureTimer, player, waitTime))
|
||||
{
|
||||
if(isPvpBattle())
|
||||
if(isPvpBattle(battleID))
|
||||
{
|
||||
if(timer.battleTimer > 0)
|
||||
{
|
||||
@ -289,7 +304,7 @@ void TurnTimerHandler::onBattleLoop(int waitTime)
|
||||
doNothing.actionType = EActionType::DEFEND;
|
||||
doNothing.stackNumber = stack->unitId();
|
||||
}
|
||||
gameHandler.battles->makePlayerBattleAction(player, doNothing);
|
||||
gameHandler.battles->makePlayerBattleAction(battleID, player, doNothing);
|
||||
}
|
||||
}
|
||||
else
|
||||
@ -311,7 +326,7 @@ void TurnTimerHandler::onBattleLoop(int waitTime)
|
||||
BattleAction retreat;
|
||||
retreat.side = side;
|
||||
retreat.actionType = EActionType::RETREAT; //harsh punishment
|
||||
gameHandler.battles->makePlayerBattleAction(player, retreat);
|
||||
gameHandler.battles->makePlayerBattleAction(battleID, player, retreat);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -16,6 +16,7 @@ VCMI_LIB_NAMESPACE_BEGIN
|
||||
|
||||
class CStack;
|
||||
class PlayerColor;
|
||||
class BattleID;
|
||||
|
||||
VCMI_LIB_NAMESPACE_END
|
||||
|
||||
@ -33,10 +34,10 @@ class TurnTimerHandler
|
||||
std::recursive_mutex mx;
|
||||
|
||||
void onPlayerMakingTurn(PlayerColor player, int waitTime);
|
||||
void onBattleLoop(int waitTime);
|
||||
void onBattleLoop(const BattleID & battleID, int waitTime);
|
||||
|
||||
bool timerCountDown(int & timerToApply, int initialTimer, PlayerColor player, int waitTime);
|
||||
bool isPvpBattle() const;
|
||||
bool isPvpBattle(const BattleID & battleID) const;
|
||||
void sendTimerUpdate(PlayerColor player);
|
||||
|
||||
public:
|
||||
@ -44,9 +45,9 @@ public:
|
||||
|
||||
void onGameplayStart(PlayerColor player);
|
||||
void onPlayerGetTurn(PlayerColor player);
|
||||
void onBattleStart();
|
||||
void onBattleNextStack(const CStack & stack);
|
||||
void onBattleEnd();
|
||||
void onBattleStart(const BattleID & battle);
|
||||
void onBattleNextStack(const BattleID & battle, const CStack & stack);
|
||||
void onBattleEnd(const BattleID & battleID);
|
||||
void update(int waitTime);
|
||||
void setTimerEnabled(PlayerColor player, bool enabled);
|
||||
void setEndTurnAllowed(PlayerColor player, bool enabled);
|
||||
|
@ -37,42 +37,42 @@ void BattleActionProcessor::setGameHandler(CGameHandler * newGameHandler)
|
||||
gameHandler = newGameHandler;
|
||||
}
|
||||
|
||||
bool BattleActionProcessor::doEmptyAction(const BattleAction & ba)
|
||||
bool BattleActionProcessor::doEmptyAction(const BattleInfo & battle, const BattleAction & ba)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
bool BattleActionProcessor::doEndTacticsAction(const BattleAction & ba)
|
||||
bool BattleActionProcessor::doEndTacticsAction(const BattleInfo & battle, const BattleAction & ba)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
bool BattleActionProcessor::doWaitAction(const BattleAction & ba)
|
||||
bool BattleActionProcessor::doWaitAction(const BattleInfo & battle, const BattleAction & ba)
|
||||
{
|
||||
const CStack * stack = gameHandler->battleGetStackByID(ba.stackNumber);
|
||||
|
||||
if (!canStackAct(stack))
|
||||
if (!canStackAct(battle, stack))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool BattleActionProcessor::doRetreatAction(const BattleAction & ba)
|
||||
bool BattleActionProcessor::doRetreatAction(const BattleInfo & battle, const BattleAction & ba)
|
||||
{
|
||||
if (!gameHandler->gameState()->curB->battleCanFlee(gameHandler->gameState()->curB->sides.at(ba.side).color))
|
||||
if (!battle.battleCanFlee(battle.sides.at(ba.side).color))
|
||||
{
|
||||
gameHandler->complain("Cannot retreat!");
|
||||
return false;
|
||||
}
|
||||
|
||||
owner->setBattleResult(EBattleResult::ESCAPE, !ba.side);
|
||||
owner->setBattleResult(battle, EBattleResult::ESCAPE, !ba.side);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool BattleActionProcessor::doSurrenderAction(const BattleAction & ba)
|
||||
bool BattleActionProcessor::doSurrenderAction(const BattleInfo & battle, const BattleAction & ba)
|
||||
{
|
||||
PlayerColor player = gameHandler->gameState()->curB->sides.at(ba.side).color;
|
||||
int cost = gameHandler->gameState()->curB->battleGetSurrenderCost(player);
|
||||
PlayerColor player = battle.sides.at(ba.side).color;
|
||||
int cost = battle.battleGetSurrenderCost(player);
|
||||
if (cost < 0)
|
||||
{
|
||||
gameHandler->complain("Cannot surrender!");
|
||||
@ -86,13 +86,13 @@ bool BattleActionProcessor::doSurrenderAction(const BattleAction & ba)
|
||||
}
|
||||
|
||||
gameHandler->giveResource(player, EGameResID::GOLD, -cost);
|
||||
owner->setBattleResult(EBattleResult::SURRENDER, !ba.side);
|
||||
owner->setBattleResult(battle, EBattleResult::SURRENDER, !ba.side);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool BattleActionProcessor::doHeroSpellAction(const BattleAction & ba)
|
||||
bool BattleActionProcessor::doHeroSpellAction(const BattleInfo & battle, const BattleAction & ba)
|
||||
{
|
||||
const CGHeroInstance *h = gameHandler->gameState()->curB->battleGetFightingHero(ba.side);
|
||||
const CGHeroInstance *h = battle.battleGetFightingHero(ba.side);
|
||||
if (!h)
|
||||
{
|
||||
logGlobal->error("Wrong caster!");
|
||||
@ -106,7 +106,7 @@ bool BattleActionProcessor::doHeroSpellAction(const BattleAction & ba)
|
||||
return false;
|
||||
}
|
||||
|
||||
spells::BattleCast parameters(gameHandler->gameState()->curB, h, spells::Mode::HERO, s);
|
||||
spells::BattleCast parameters(&battle, h, spells::Mode::HERO, s);
|
||||
|
||||
spells::detail::ProblemImpl problem;
|
||||
|
||||
@ -122,17 +122,17 @@ bool BattleActionProcessor::doHeroSpellAction(const BattleAction & ba)
|
||||
return false;
|
||||
}
|
||||
|
||||
parameters.cast(gameHandler->spellEnv, ba.getTarget(gameHandler->gameState()->curB));
|
||||
parameters.cast(gameHandler->spellEnv, ba.getTarget(&battle));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool BattleActionProcessor::doWalkAction(const BattleAction & ba)
|
||||
bool BattleActionProcessor::doWalkAction(const BattleInfo & battle, const BattleAction & ba)
|
||||
{
|
||||
const CStack * stack = gameHandler->battleGetStackByID(ba.stackNumber);
|
||||
battle::Target target = ba.getTarget(gameHandler->gameState()->curB);
|
||||
battle::Target target = ba.getTarget(&battle);
|
||||
|
||||
if (!canStackAct(stack))
|
||||
if (!canStackAct(battle, stack))
|
||||
return false;
|
||||
|
||||
if(target.size() < 1)
|
||||
@ -141,7 +141,7 @@ bool BattleActionProcessor::doWalkAction(const BattleAction & ba)
|
||||
return false;
|
||||
}
|
||||
|
||||
int walkedTiles = moveStack(ba.stackNumber, target.at(0).hexValue); //move
|
||||
int walkedTiles = moveStack(battle, ba.stackNumber, target.at(0).hexValue); //move
|
||||
if (!walkedTiles)
|
||||
{
|
||||
gameHandler->complain("Stack failed movement!");
|
||||
@ -150,11 +150,11 @@ bool BattleActionProcessor::doWalkAction(const BattleAction & ba)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool BattleActionProcessor::doDefendAction(const BattleAction & ba)
|
||||
bool BattleActionProcessor::doDefendAction(const BattleInfo & battle, const BattleAction & ba)
|
||||
{
|
||||
const CStack * stack = gameHandler->battleGetStackByID(ba.stackNumber);
|
||||
|
||||
if (!canStackAct(stack))
|
||||
if (!canStackAct(battle, stack))
|
||||
return false;
|
||||
|
||||
//defensive stance, TODO: filter out spell boosts from bonus (stone skin etc.)
|
||||
@ -199,12 +199,12 @@ bool BattleActionProcessor::doDefendAction(const BattleAction & ba)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool BattleActionProcessor::doAttackAction(const BattleAction & ba)
|
||||
bool BattleActionProcessor::doAttackAction(const BattleInfo & battle, const BattleAction & ba)
|
||||
{
|
||||
const CStack * stack = gameHandler->battleGetStackByID(ba.stackNumber);
|
||||
battle::Target target = ba.getTarget(gameHandler->gameState()->curB);
|
||||
battle::Target target = ba.getTarget(&battle);
|
||||
|
||||
if (!canStackAct(stack))
|
||||
if (!canStackAct(battle, stack))
|
||||
return false;
|
||||
|
||||
if(target.size() < 2)
|
||||
@ -215,7 +215,7 @@ bool BattleActionProcessor::doAttackAction(const BattleAction & ba)
|
||||
|
||||
BattleHex attackPos = target.at(0).hexValue;
|
||||
BattleHex destinationTile = target.at(1).hexValue;
|
||||
const CStack * destinationStack = gameHandler->gameState()->curB->battleGetStackByPos(destinationTile, true);
|
||||
const CStack * destinationStack = battle.battleGetStackByPos(destinationTile, true);
|
||||
|
||||
if(!destinationStack)
|
||||
{
|
||||
@ -224,7 +224,7 @@ bool BattleActionProcessor::doAttackAction(const BattleAction & ba)
|
||||
}
|
||||
|
||||
BattleHex startingPos = stack->getPosition();
|
||||
int distance = moveStack(ba.stackNumber, attackPos);
|
||||
int distance = moveStack(battle, ba.stackNumber, attackPos);
|
||||
|
||||
logGlobal->trace("%s will attack %s", stack->nodeName(), destinationStack->nodeName());
|
||||
|
||||
@ -256,7 +256,7 @@ bool BattleActionProcessor::doAttackAction(const BattleAction & ba)
|
||||
int totalAttacks = stack->totalAttacks.getMeleeValue();
|
||||
|
||||
//TODO: move to CUnitState
|
||||
const auto * attackingHero = gameHandler->gameState()->curB->battleGetFightingHero(ba.side);
|
||||
const auto * attackingHero = battle.battleGetFightingHero(ba.side);
|
||||
if(attackingHero)
|
||||
{
|
||||
totalAttacks += attackingHero->valOfBonuses(BonusType::HERO_GRANTS_ATTACKS, stack->creatureIndex());
|
||||
@ -269,13 +269,13 @@ bool BattleActionProcessor::doAttackAction(const BattleAction & ba)
|
||||
//first strike
|
||||
if(i == 0 && firstStrike && retaliation)
|
||||
{
|
||||
makeAttack(destinationStack, stack, 0, stack->getPosition(), true, false, true);
|
||||
makeAttack(battle, destinationStack, stack, 0, stack->getPosition(), true, false, true);
|
||||
}
|
||||
|
||||
//move can cause death, eg. by walking into the moat, first strike can cause death or paralysis/petrification
|
||||
if(stack->alive() && !stack->hasBonusOfType(BonusType::NOT_ACTIVE) && destinationStack->alive())
|
||||
{
|
||||
makeAttack(stack, destinationStack, (i ? 0 : distance), destinationTile, i==0, false, false);//no distance travelled on second attack
|
||||
makeAttack(battle, stack, destinationStack, (i ? 0 : distance), destinationTile, i==0, false, false);//no distance travelled on second attack
|
||||
}
|
||||
|
||||
//counterattack
|
||||
@ -285,7 +285,7 @@ bool BattleActionProcessor::doAttackAction(const BattleAction & ba)
|
||||
&& (i == 0 && !firstStrike)
|
||||
&& retaliation && destinationStack->ableToRetaliate())
|
||||
{
|
||||
makeAttack(destinationStack, stack, 0, stack->getPosition(), true, false, true);
|
||||
makeAttack(battle, destinationStack, stack, 0, stack->getPosition(), true, false, true);
|
||||
}
|
||||
}
|
||||
|
||||
@ -296,18 +296,18 @@ bool BattleActionProcessor::doAttackAction(const BattleAction & ba)
|
||||
&& startingPos == target.at(2).hexValue
|
||||
&& stack->alive())
|
||||
{
|
||||
moveStack(ba.stackNumber, startingPos);
|
||||
moveStack(battle, ba.stackNumber, startingPos);
|
||||
//NOTE: curStack->unitId() == ba.stackNumber (rev 1431)
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool BattleActionProcessor::doShootAction(const BattleAction & ba)
|
||||
bool BattleActionProcessor::doShootAction(const BattleInfo & battle, const BattleAction & ba)
|
||||
{
|
||||
const CStack * stack = gameHandler->battleGetStackByID(ba.stackNumber);
|
||||
battle::Target target = ba.getTarget(gameHandler->gameState()->curB);
|
||||
battle::Target target = ba.getTarget(&battle);
|
||||
|
||||
if (!canStackAct(stack))
|
||||
if (!canStackAct(battle, stack))
|
||||
return false;
|
||||
|
||||
if(target.size() < 1)
|
||||
@ -318,9 +318,9 @@ bool BattleActionProcessor::doShootAction(const BattleAction & ba)
|
||||
|
||||
auto destination = target.at(0).hexValue;
|
||||
|
||||
const CStack * destinationStack = gameHandler->gameState()->curB->battleGetStackByPos(destination);
|
||||
const CStack * destinationStack = battle.battleGetStackByPos(destination);
|
||||
|
||||
if (!gameHandler->gameState()->curB->battleCanShoot(stack, destination))
|
||||
if (!battle.battleCanShoot(stack, destination))
|
||||
{
|
||||
gameHandler->complain("Cannot shoot!");
|
||||
return false;
|
||||
@ -332,23 +332,23 @@ bool BattleActionProcessor::doShootAction(const BattleAction & ba)
|
||||
return false;
|
||||
}
|
||||
|
||||
makeAttack(stack, destinationStack, 0, destination, true, true, false);
|
||||
makeAttack(battle, stack, destinationStack, 0, destination, true, true, false);
|
||||
|
||||
//ranged counterattack
|
||||
if (destinationStack->hasBonusOfType(BonusType::RANGED_RETALIATION)
|
||||
&& !stack->hasBonusOfType(BonusType::BLOCKS_RANGED_RETALIATION)
|
||||
&& destinationStack->ableToRetaliate()
|
||||
&& gameHandler->gameState()->curB->battleCanShoot(destinationStack, stack->getPosition())
|
||||
&& battle.battleCanShoot(destinationStack, stack->getPosition())
|
||||
&& stack->alive()) //attacker may have died (fire shield)
|
||||
{
|
||||
makeAttack(destinationStack, stack, 0, stack->getPosition(), true, true, true);
|
||||
makeAttack(battle, destinationStack, stack, 0, stack->getPosition(), true, true, true);
|
||||
}
|
||||
//allow more than one additional attack
|
||||
|
||||
int totalRangedAttacks = stack->totalAttacks.getRangedValue();
|
||||
|
||||
//TODO: move to CUnitState
|
||||
const auto * attackingHero = gameHandler->gameState()->curB->battleGetFightingHero(ba.side);
|
||||
const auto * attackingHero = battle.battleGetFightingHero(ba.side);
|
||||
if(attackingHero)
|
||||
{
|
||||
totalRangedAttacks += attackingHero->valOfBonuses(BonusType::HERO_GRANTS_ATTACKS, stack->creatureIndex());
|
||||
@ -362,19 +362,19 @@ bool BattleActionProcessor::doShootAction(const BattleAction & ba)
|
||||
&& stack->shots.canUse()
|
||||
)
|
||||
{
|
||||
makeAttack(stack, destinationStack, 0, destination, false, true, false);
|
||||
makeAttack(battle, stack, destinationStack, 0, destination, false, true, false);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool BattleActionProcessor::doCatapultAction(const BattleAction & ba)
|
||||
bool BattleActionProcessor::doCatapultAction(const BattleInfo & battle, const BattleAction & ba)
|
||||
{
|
||||
const CStack * stack = gameHandler->gameState()->curB->battleGetStackByID(ba.stackNumber);
|
||||
battle::Target target = ba.getTarget(gameHandler->gameState()->curB);
|
||||
const CStack * stack = battle.battleGetStackByID(ba.stackNumber);
|
||||
battle::Target target = ba.getTarget(&battle);
|
||||
|
||||
if (!canStackAct(stack))
|
||||
if (!canStackAct(battle, stack))
|
||||
return false;
|
||||
|
||||
std::shared_ptr<const Bonus> catapultAbility = stack->getBonusLocalFirst(Selector::type()(BonusType::CATAPULT));
|
||||
@ -385,7 +385,7 @@ bool BattleActionProcessor::doCatapultAction(const BattleAction & ba)
|
||||
else
|
||||
{
|
||||
const CSpell * spell = SpellID(catapultAbility->subtype).toSpell();
|
||||
spells::BattleCast parameters(gameHandler->gameState()->curB, stack, spells::Mode::SPELL_LIKE_ATTACK, spell); //We can shot infinitely by catapult
|
||||
spells::BattleCast parameters(&battle, stack, spells::Mode::SPELL_LIKE_ATTACK, spell); //We can shot infinitely by catapult
|
||||
auto shotLevel = stack->valOfBonuses(Selector::typeSubtype(BonusType::CATAPULT_EXTRA_SHOTS, catapultAbility->subtype));
|
||||
parameters.setSpellLevel(shotLevel);
|
||||
parameters.cast(gameHandler->spellEnv, target);
|
||||
@ -393,13 +393,13 @@ bool BattleActionProcessor::doCatapultAction(const BattleAction & ba)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool BattleActionProcessor::doUnitSpellAction(const BattleAction & ba)
|
||||
bool BattleActionProcessor::doUnitSpellAction(const BattleInfo & battle, const BattleAction & ba)
|
||||
{
|
||||
const CStack * stack = gameHandler->gameState()->curB->battleGetStackByID(ba.stackNumber);
|
||||
battle::Target target = ba.getTarget(gameHandler->gameState()->curB);
|
||||
const CStack * stack = battle.battleGetStackByID(ba.stackNumber);
|
||||
battle::Target target = ba.getTarget(&battle);
|
||||
SpellID spellID = ba.spell;
|
||||
|
||||
if (!canStackAct(stack))
|
||||
if (!canStackAct(battle, stack))
|
||||
return false;
|
||||
|
||||
std::shared_ptr<const Bonus> randSpellcaster = stack->getBonus(Selector::type()(BonusType::RANDOM_SPELLCASTER));
|
||||
@ -414,7 +414,7 @@ bool BattleActionProcessor::doUnitSpellAction(const BattleAction & ba)
|
||||
else
|
||||
{
|
||||
const CSpell * spell = SpellID(spellID).toSpell();
|
||||
spells::BattleCast parameters(gameHandler->gameState()->curB, stack, spells::Mode::CREATURE_ACTIVE, spell);
|
||||
spells::BattleCast parameters(&battle, stack, spells::Mode::CREATURE_ACTIVE, spell);
|
||||
int32_t spellLvl = 0;
|
||||
if(spellcaster)
|
||||
vstd::amax(spellLvl, spellcaster->val);
|
||||
@ -426,12 +426,12 @@ bool BattleActionProcessor::doUnitSpellAction(const BattleAction & ba)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool BattleActionProcessor::doHealAction(const BattleAction & ba)
|
||||
bool BattleActionProcessor::doHealAction(const BattleInfo & battle, const BattleAction & ba)
|
||||
{
|
||||
const CStack * stack = gameHandler->gameState()->curB->battleGetStackByID(ba.stackNumber);
|
||||
battle::Target target = ba.getTarget(gameHandler->gameState()->curB);
|
||||
const CStack * stack = battle.battleGetStackByID(ba.stackNumber);
|
||||
battle::Target target = ba.getTarget(&battle);
|
||||
|
||||
if (!canStackAct(stack))
|
||||
if (!canStackAct(battle, stack))
|
||||
return false;
|
||||
|
||||
if(target.size() < 1)
|
||||
@ -446,7 +446,7 @@ bool BattleActionProcessor::doHealAction(const BattleAction & ba)
|
||||
if(target.at(0).unitValue)
|
||||
destStack = target.at(0).unitValue;
|
||||
else
|
||||
destStack = gameHandler->gameState()->curB->battleGetUnitByPos(target.at(0).hexValue);
|
||||
destStack = battle.battleGetUnitByPos(target.at(0).hexValue);
|
||||
|
||||
if(stack == nullptr || destStack == nullptr || !healerAbility || healerAbility->subtype < 0)
|
||||
{
|
||||
@ -455,7 +455,7 @@ bool BattleActionProcessor::doHealAction(const BattleAction & ba)
|
||||
else
|
||||
{
|
||||
const CSpell * spell = SpellID(healerAbility->subtype).toSpell();
|
||||
spells::BattleCast parameters(gameHandler->gameState()->curB, stack, spells::Mode::SPELL_LIKE_ATTACK, spell); //We can heal infinitely by first aid tent
|
||||
spells::BattleCast parameters(&battle, stack, spells::Mode::SPELL_LIKE_ATTACK, spell); //We can heal infinitely by first aid tent
|
||||
auto dest = battle::Destination(destStack, target.at(0).hexValue);
|
||||
parameters.setSpellLevel(0);
|
||||
parameters.cast(gameHandler->spellEnv, {dest});
|
||||
@ -463,7 +463,7 @@ bool BattleActionProcessor::doHealAction(const BattleAction & ba)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool BattleActionProcessor::canStackAct(const CStack * stack)
|
||||
bool BattleActionProcessor::canStackAct(const BattleInfo & battle, const CStack * stack)
|
||||
{
|
||||
if (!stack)
|
||||
{
|
||||
@ -486,7 +486,7 @@ bool BattleActionProcessor::canStackAct(const CStack * stack)
|
||||
}
|
||||
else
|
||||
{
|
||||
if (stack->unitId() != gameHandler->gameState()->curB->getActiveStackID())
|
||||
if (stack->unitId() != battle.getActiveStackID())
|
||||
{
|
||||
gameHandler->complain("Action has to be about active stack!");
|
||||
return false;
|
||||
@ -495,46 +495,46 @@ bool BattleActionProcessor::canStackAct(const CStack * stack)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool BattleActionProcessor::dispatchBattleAction(const BattleAction & ba)
|
||||
bool BattleActionProcessor::dispatchBattleAction(const BattleInfo & battle, const BattleAction & ba)
|
||||
{
|
||||
switch(ba.actionType)
|
||||
{
|
||||
case EActionType::BAD_MORALE:
|
||||
case EActionType::NO_ACTION:
|
||||
return doEmptyAction(ba);
|
||||
return doEmptyAction(battle, ba);
|
||||
case EActionType::END_TACTIC_PHASE:
|
||||
return doEndTacticsAction(ba);
|
||||
return doEndTacticsAction(battle, ba);
|
||||
case EActionType::RETREAT:
|
||||
return doRetreatAction(ba);
|
||||
return doRetreatAction(battle, ba);
|
||||
case EActionType::SURRENDER:
|
||||
return doSurrenderAction(ba);
|
||||
return doSurrenderAction(battle, ba);
|
||||
case EActionType::HERO_SPELL:
|
||||
return doHeroSpellAction(ba);
|
||||
return doHeroSpellAction(battle, ba);
|
||||
case EActionType::WALK:
|
||||
return doWalkAction(ba);
|
||||
return doWalkAction(battle, ba);
|
||||
case EActionType::WAIT:
|
||||
return doWaitAction(ba);
|
||||
return doWaitAction(battle, ba);
|
||||
case EActionType::DEFEND:
|
||||
return doDefendAction(ba);
|
||||
return doDefendAction(battle, ba);
|
||||
case EActionType::WALK_AND_ATTACK:
|
||||
return doAttackAction(ba);
|
||||
return doAttackAction(battle, ba);
|
||||
case EActionType::SHOOT:
|
||||
return doShootAction(ba);
|
||||
return doShootAction(battle, ba);
|
||||
case EActionType::CATAPULT:
|
||||
return doCatapultAction(ba);
|
||||
return doCatapultAction(battle, ba);
|
||||
case EActionType::MONSTER_SPELL:
|
||||
return doUnitSpellAction(ba);
|
||||
return doUnitSpellAction(battle, ba);
|
||||
case EActionType::STACK_HEAL:
|
||||
return doHealAction(ba);
|
||||
return doHealAction(battle, ba);
|
||||
}
|
||||
gameHandler->complain("Unrecognized action type received!!");
|
||||
return false;
|
||||
}
|
||||
|
||||
bool BattleActionProcessor::makeBattleActionImpl(const BattleAction &ba)
|
||||
bool BattleActionProcessor::makeBattleActionImpl(const BattleInfo & battle, const BattleAction &ba)
|
||||
{
|
||||
logGlobal->trace("Making action: %s", ba.toString());
|
||||
const CStack * stack = gameHandler->gameState()->curB->battleGetStackByID(ba.stackNumber);
|
||||
const CStack * stack = battle.battleGetStackByID(ba.stackNumber);
|
||||
|
||||
// for these events client does not expects StartAction/EndAction wrapper
|
||||
if (!ba.isBattleEndAction())
|
||||
@ -543,7 +543,7 @@ bool BattleActionProcessor::makeBattleActionImpl(const BattleAction &ba)
|
||||
gameHandler->sendAndApply(&startAction);
|
||||
}
|
||||
|
||||
bool result = dispatchBattleAction(ba);
|
||||
bool result = dispatchBattleAction(battle, ba);
|
||||
|
||||
if (!ba.isBattleEndAction())
|
||||
{
|
||||
@ -557,19 +557,19 @@ bool BattleActionProcessor::makeBattleActionImpl(const BattleAction &ba)
|
||||
return result;
|
||||
}
|
||||
|
||||
int BattleActionProcessor::moveStack(int stack, BattleHex dest)
|
||||
int BattleActionProcessor::moveStack(const BattleInfo & battle, int stack, BattleHex dest)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
const CStack *curStack = gameHandler->battleGetStackByID(stack);
|
||||
const CStack *stackAtEnd = gameHandler->gameState()->curB->battleGetStackByPos(dest);
|
||||
const CStack *stackAtEnd = battle.battleGetStackByPos(dest);
|
||||
|
||||
assert(curStack);
|
||||
assert(dest < GameConstants::BFIELD_SIZE);
|
||||
|
||||
if (gameHandler->gameState()->curB->tacticDistance)
|
||||
if (battle.tacticDistance)
|
||||
{
|
||||
assert(gameHandler->gameState()->curB->isInTacticRange(dest));
|
||||
assert(battle.isInTacticRange(dest));
|
||||
}
|
||||
|
||||
auto start = curStack->getPosition();
|
||||
@ -600,7 +600,7 @@ int BattleActionProcessor::moveStack(int stack, BattleHex dest)
|
||||
}
|
||||
|
||||
bool canUseGate = false;
|
||||
auto dbState = gameHandler->gameState()->curB->si.gateState;
|
||||
auto dbState = battle.si.gateState;
|
||||
if(gameHandler->battleGetSiegeLevel() > 0 && curStack->unitSide() == BattleSide::DEFENDER &&
|
||||
dbState != EGateState::DESTROYED &&
|
||||
dbState != EGateState::BLOCKED)
|
||||
@ -608,13 +608,13 @@ int BattleActionProcessor::moveStack(int stack, BattleHex dest)
|
||||
canUseGate = true;
|
||||
}
|
||||
|
||||
std::pair< std::vector<BattleHex>, int > path = gameHandler->gameState()->curB->getPath(start, dest, curStack);
|
||||
std::pair< std::vector<BattleHex>, int > path = battle.getPath(start, dest, curStack);
|
||||
|
||||
ret = path.second;
|
||||
|
||||
int creSpeed = curStack->speed(0, true);
|
||||
|
||||
if (gameHandler->gameState()->curB->tacticDistance > 0 && creSpeed > 0)
|
||||
if (battle.tacticDistance > 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)
|
||||
@ -826,7 +826,7 @@ int BattleActionProcessor::moveStack(int stack, BattleHex dest)
|
||||
else if (curStack->getPosition() == gateMayCloseAtHex)
|
||||
{
|
||||
gateMayCloseAtHex = BattleHex();
|
||||
owner->updateGateState();
|
||||
owner->updateGateState(battle);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -848,10 +848,10 @@ int BattleActionProcessor::moveStack(int stack, BattleHex dest)
|
||||
return ret;
|
||||
}
|
||||
|
||||
void BattleActionProcessor::makeAttack(const CStack * attacker, const CStack * defender, int distance, BattleHex targetHex, bool first, bool ranged, bool counter)
|
||||
void BattleActionProcessor::makeAttack(const BattleInfo & battle, const CStack * attacker, const CStack * defender, int distance, BattleHex targetHex, bool first, bool ranged, bool counter)
|
||||
{
|
||||
if(first && !counter)
|
||||
handleAttackBeforeCasting(ranged, attacker, defender);
|
||||
handleAttackBeforeCasting(battle, ranged, attacker, defender);
|
||||
|
||||
FireShieldInfo fireShield;
|
||||
BattleAttack bat;
|
||||
@ -891,7 +891,7 @@ void BattleActionProcessor::makeAttack(const CStack * attacker, const CStack * d
|
||||
bat.flags |= BattleAttack::DEATH_BLOW;
|
||||
}
|
||||
|
||||
const auto * owner = gameHandler->gameState()->curB->getHero(attacker->unitOwner());
|
||||
const auto * owner = battle.getHero(attacker->unitOwner());
|
||||
if(owner)
|
||||
{
|
||||
int chance = owner->valOfBonuses(BonusType::BONUS_DAMAGE_CHANCE, attacker->creatureIndex());
|
||||
@ -903,14 +903,14 @@ void BattleActionProcessor::makeAttack(const CStack * attacker, const CStack * d
|
||||
|
||||
// only primary target
|
||||
if(defender->alive())
|
||||
drainedLife += applyBattleEffects(bat, attackerState, fireShield, defender, distance, false);
|
||||
drainedLife += applyBattleEffects(battle, bat, attackerState, fireShield, defender, distance, false);
|
||||
|
||||
//multiple-hex normal attack
|
||||
std::set<const CStack*> attackedCreatures = gameHandler->gameState()->curB->getAttackedCreatures(attacker, targetHex, bat.shot()); //creatures other than primary target
|
||||
std::set<const CStack*> attackedCreatures = battle.getAttackedCreatures(attacker, targetHex, bat.shot()); //creatures other than primary target
|
||||
for(const CStack * stack : attackedCreatures)
|
||||
{
|
||||
if(stack != defender && stack->alive()) //do not hit same stack twice
|
||||
drainedLife += applyBattleEffects(bat, attackerState, fireShield, stack, distance, true);
|
||||
drainedLife += applyBattleEffects(battle, bat, attackerState, fireShield, stack, distance, true);
|
||||
}
|
||||
|
||||
std::shared_ptr<const Bonus> bonus = attacker->getBonusLocalFirst(Selector::type()(BonusType::SPELL_LIKE_ATTACK));
|
||||
@ -927,7 +927,7 @@ void BattleActionProcessor::makeAttack(const CStack * attacker, const CStack * d
|
||||
battle::Target target;
|
||||
target.emplace_back(defender, targetHex);
|
||||
|
||||
spells::BattleCast event(gameHandler->gameState()->curB, attacker, spells::Mode::SPELL_LIKE_ATTACK, spell);
|
||||
spells::BattleCast event(&battle, attacker, spells::Mode::SPELL_LIKE_ATTACK, spell);
|
||||
event.setSpellLevel(bonus->val);
|
||||
|
||||
auto attackedCreatures = spell->battleMechanics(&event)->getAffectedStacks(target);
|
||||
@ -938,7 +938,7 @@ void BattleActionProcessor::makeAttack(const CStack * attacker, const CStack * d
|
||||
{
|
||||
if(stack != defender && stack->alive()) //do not hit same stack twice
|
||||
{
|
||||
drainedLife += applyBattleEffects(bat, attackerState, fireShield, stack, distance, true);
|
||||
drainedLife += applyBattleEffects(battle, bat, attackerState, fireShield, stack, distance, true);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1012,7 +1012,7 @@ void BattleActionProcessor::makeAttack(const CStack * attacker, const CStack * d
|
||||
const CStack * actor = item.first;
|
||||
int64_t rawDamage = item.second;
|
||||
|
||||
const CGHeroInstance * actorOwner = gameHandler->gameState()->curB->getHero(actor->unitOwner());
|
||||
const CGHeroInstance * actorOwner = battle.getHero(actor->unitOwner());
|
||||
|
||||
if(actorOwner)
|
||||
{
|
||||
@ -1055,10 +1055,10 @@ void BattleActionProcessor::makeAttack(const CStack * attacker, const CStack * d
|
||||
|
||||
gameHandler->sendAndApply(&blm);
|
||||
|
||||
handleAfterAttackCasting(ranged, attacker, defender);
|
||||
handleAfterAttackCasting(battle, ranged, attacker, defender);
|
||||
}
|
||||
|
||||
void BattleActionProcessor::attackCasting(bool ranged, BonusType attackMode, const battle::Unit * attacker, const battle::Unit * defender)
|
||||
void BattleActionProcessor::attackCasting(const BattleInfo & battle, bool ranged, BonusType attackMode, const battle::Unit * attacker, const battle::Unit * defender)
|
||||
{
|
||||
if(attacker->hasBonusOfType(attackMode))
|
||||
{
|
||||
@ -1104,7 +1104,7 @@ void BattleActionProcessor::attackCasting(bool ranged, BonusType attackMode, con
|
||||
spells::Target target;
|
||||
target.emplace_back(defender);
|
||||
|
||||
spells::BattleCast parameters(gameHandler->gameState()->curB, &caster, spells::Mode::PASSIVE, spell);
|
||||
spells::BattleCast parameters(&battle, &caster, spells::Mode::PASSIVE, spell);
|
||||
|
||||
auto m = spell->battleMechanics(¶meters);
|
||||
|
||||
@ -1126,17 +1126,17 @@ void BattleActionProcessor::attackCasting(bool ranged, BonusType attackMode, con
|
||||
}
|
||||
}
|
||||
|
||||
void BattleActionProcessor::handleAttackBeforeCasting(bool ranged, const CStack * attacker, const CStack * defender)
|
||||
void BattleActionProcessor::handleAttackBeforeCasting(const BattleInfo & battle, bool ranged, const CStack * attacker, const CStack * defender)
|
||||
{
|
||||
attackCasting(ranged, BonusType::SPELL_BEFORE_ATTACK, attacker, defender); //no death stare / acid breath needed?
|
||||
attackCasting(battle, ranged, BonusType::SPELL_BEFORE_ATTACK, attacker, defender); //no death stare / acid breath needed?
|
||||
}
|
||||
|
||||
void BattleActionProcessor::handleAfterAttackCasting(bool ranged, const CStack * attacker, const CStack * defender)
|
||||
void BattleActionProcessor::handleAfterAttackCasting(const BattleInfo & battle, bool ranged, const CStack * attacker, const CStack * defender)
|
||||
{
|
||||
if(!attacker->alive() || !defender->alive()) // can be already dead
|
||||
return;
|
||||
|
||||
attackCasting(ranged, BonusType::SPELL_AFTER_ATTACK, attacker, defender);
|
||||
attackCasting(battle, ranged, BonusType::SPELL_AFTER_ATTACK, attacker, defender);
|
||||
|
||||
if(!defender->alive())
|
||||
{
|
||||
@ -1169,7 +1169,7 @@ void BattleActionProcessor::handleAfterAttackCasting(bool ranged, const CStack *
|
||||
|
||||
spells::AbilityCaster caster(attacker, 0);
|
||||
|
||||
spells::BattleCast parameters(gameHandler->gameState()->curB, &caster, spells::Mode::PASSIVE, spell);
|
||||
spells::BattleCast parameters(&battle, &caster, spells::Mode::PASSIVE, spell);
|
||||
spells::Target target;
|
||||
target.emplace_back(defender);
|
||||
parameters.setEffectValue(staredCreatures);
|
||||
@ -1194,7 +1194,7 @@ void BattleActionProcessor::handleAfterAttackCasting(bool ranged, const CStack *
|
||||
|
||||
spells::AbilityCaster caster(attacker, 0);
|
||||
|
||||
spells::BattleCast parameters(gameHandler->gameState()->curB, &caster, spells::Mode::PASSIVE, spell);
|
||||
spells::BattleCast parameters(&battle, &caster, spells::Mode::PASSIVE, spell);
|
||||
spells::Target target;
|
||||
target.emplace_back(defender);
|
||||
|
||||
@ -1221,7 +1221,7 @@ void BattleActionProcessor::handleAfterAttackCasting(bool ranged, const CStack *
|
||||
return;
|
||||
|
||||
battle::UnitInfo resurrectInfo;
|
||||
resurrectInfo.id = gameHandler->gameState()->curB->battleNextUnitId();
|
||||
resurrectInfo.id = battle.battleNextUnitId();
|
||||
resurrectInfo.summoned = false;
|
||||
resurrectInfo.position = defender->getPosition();
|
||||
resurrectInfo.side = defender->unitSide();
|
||||
@ -1286,7 +1286,7 @@ void BattleActionProcessor::handleAfterAttackCasting(bool ranged, const CStack *
|
||||
}
|
||||
}
|
||||
|
||||
int64_t BattleActionProcessor::applyBattleEffects(BattleAttack & bat, std::shared_ptr<battle::CUnitState> attackerState, FireShieldInfo & fireShield, const CStack * def, int distance, bool secondary)
|
||||
int64_t BattleActionProcessor::applyBattleEffects(const BattleInfo & battle, BattleAttack & bat, std::shared_ptr<battle::CUnitState> attackerState, FireShieldInfo & fireShield, const CStack * def, int distance, bool secondary)
|
||||
{
|
||||
BattleStackAttacked bsa;
|
||||
if(secondary)
|
||||
@ -1302,8 +1302,8 @@ int64_t BattleActionProcessor::applyBattleEffects(BattleAttack & bat, std::share
|
||||
bai.luckyStrike = bat.lucky();
|
||||
bai.unluckyStrike = bat.unlucky();
|
||||
|
||||
auto range = gameHandler->gameState()->curB->calculateDmgRange(bai);
|
||||
bsa.damageAmount = gameHandler->gameState()->curB->getActualDamage(range.damage, attackerState->getCount(), gameHandler->getRandomGenerator());
|
||||
auto range = battle.calculateDmgRange(bai);
|
||||
bsa.damageAmount = battle.getActualDamage(range.damage, attackerState->getCount(), gameHandler->getRandomGenerator());
|
||||
CStack::prepareAttacked(bsa, gameHandler->getRandomGenerator(), bai.defender->acquireState()); //calculate casualties
|
||||
}
|
||||
|
||||
@ -1390,22 +1390,17 @@ void BattleActionProcessor::addGenericKilledLog(BattleLogMessage & blm, const CS
|
||||
}
|
||||
}
|
||||
|
||||
bool BattleActionProcessor::makeAutomaticBattleAction(const BattleAction & ba)
|
||||
bool BattleActionProcessor::makeAutomaticBattleAction(const BattleInfo & battle, const BattleAction & ba)
|
||||
{
|
||||
return makeBattleActionImpl(ba);
|
||||
return makeBattleActionImpl(battle, ba);
|
||||
}
|
||||
|
||||
bool BattleActionProcessor::makePlayerBattleAction(PlayerColor player, const BattleAction &ba)
|
||||
bool BattleActionProcessor::makePlayerBattleAction(const BattleInfo & battle, PlayerColor player, const BattleAction &ba)
|
||||
{
|
||||
const BattleInfo * battle = gameHandler->gameState()->curB;
|
||||
|
||||
if(!battle && gameHandler->complain("Can not make action - there is no battle ongoing!"))
|
||||
return false;
|
||||
|
||||
if (ba.side != 0 && ba.side != 1 && gameHandler->complain("Can not make action - invalid battle side!"))
|
||||
return false;
|
||||
|
||||
if(battle->tacticDistance != 0)
|
||||
if(battle.tacticDistance != 0)
|
||||
{
|
||||
if(!ba.isTacticsAction())
|
||||
{
|
||||
@ -1413,7 +1408,7 @@ bool BattleActionProcessor::makePlayerBattleAction(PlayerColor player, const Bat
|
||||
return false;
|
||||
}
|
||||
|
||||
if(player != battle->sides[ba.side].color)
|
||||
if(player != battle.sides[ba.side].color)
|
||||
{
|
||||
gameHandler->complain("Can not make actions in battles you are not part of!");
|
||||
return false;
|
||||
@ -1421,21 +1416,21 @@ bool BattleActionProcessor::makePlayerBattleAction(PlayerColor player, const Bat
|
||||
}
|
||||
else
|
||||
{
|
||||
if (ba.isUnitAction() && ba.stackNumber != battle->getActiveStackID())
|
||||
if (ba.isUnitAction() && ba.stackNumber != battle.getActiveStackID())
|
||||
{
|
||||
gameHandler->complain("Can not make actions - stack is not active!");
|
||||
return false;
|
||||
}
|
||||
|
||||
auto active = battle->battleActiveUnit();
|
||||
auto active = battle.battleActiveUnit();
|
||||
if(!active && gameHandler->complain("No active unit in battle!"))
|
||||
return false;
|
||||
|
||||
auto unitOwner = battle->battleGetOwner(active);
|
||||
auto unitOwner = battle.battleGetOwner(active);
|
||||
|
||||
if(player != unitOwner && gameHandler->complain("Can not make actions in battles you are not part of!"))
|
||||
return false;
|
||||
}
|
||||
|
||||
return makeBattleActionImpl(ba);
|
||||
return makeBattleActionImpl(battle, ba);
|
||||
}
|
||||
|
@ -14,6 +14,7 @@ VCMI_LIB_NAMESPACE_BEGIN
|
||||
struct BattleLogMessage;
|
||||
struct BattleAttack;
|
||||
class BattleAction;
|
||||
class BattleInfo;
|
||||
struct BattleHex;
|
||||
class CStack;
|
||||
class PlayerColor;
|
||||
@ -38,42 +39,42 @@ class BattleActionProcessor : boost::noncopyable
|
||||
BattleProcessor * owner;
|
||||
CGameHandler * gameHandler;
|
||||
|
||||
int moveStack(int stack, BattleHex dest); //returned value - travelled distance
|
||||
void makeAttack(const CStack * attacker, const CStack * defender, int distance, BattleHex targetHex, bool first, bool ranged, bool counter);
|
||||
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);
|
||||
|
||||
void handleAttackBeforeCasting(bool ranged, const CStack * attacker, const CStack * defender);
|
||||
void handleAfterAttackCasting(bool ranged, const CStack * attacker, const CStack * defender);
|
||||
void attackCasting(bool ranged, BonusType attackMode, const battle::Unit * attacker, const battle::Unit * defender);
|
||||
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);
|
||||
|
||||
// damage, drain life & fire shield; returns amount of drained life
|
||||
int64_t applyBattleEffects(BattleAttack & bat, std::shared_ptr<battle::CUnitState> attackerState, FireShieldInfo & fireShield, const CStack * def, int distance, bool secondary);
|
||||
int64_t applyBattleEffects(const BattleInfo & 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 CStack * stack);
|
||||
bool canStackAct(const BattleInfo & battle, const CStack * stack);
|
||||
|
||||
bool doEmptyAction(const BattleAction & ba);
|
||||
bool doEndTacticsAction(const BattleAction & ba);
|
||||
bool doRetreatAction(const BattleAction & ba);
|
||||
bool doSurrenderAction(const BattleAction & ba);
|
||||
bool doHeroSpellAction(const BattleAction & ba);
|
||||
bool doWalkAction(const BattleAction & ba);
|
||||
bool doWaitAction(const BattleAction & ba);
|
||||
bool doDefendAction(const BattleAction & ba);
|
||||
bool doAttackAction(const BattleAction & ba);
|
||||
bool doShootAction(const BattleAction & ba);
|
||||
bool doCatapultAction(const BattleAction & ba);
|
||||
bool doUnitSpellAction(const BattleAction & ba);
|
||||
bool doHealAction(const BattleAction & ba);
|
||||
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 dispatchBattleAction(const BattleAction & ba);
|
||||
bool makeBattleActionImpl(const BattleAction & ba);
|
||||
bool dispatchBattleAction(const BattleInfo & battle, const BattleAction & ba);
|
||||
bool makeBattleActionImpl(const BattleInfo & battle, const BattleAction & ba);
|
||||
|
||||
public:
|
||||
explicit BattleActionProcessor(BattleProcessor * owner);
|
||||
void setGameHandler(CGameHandler * newGameHandler);
|
||||
|
||||
bool makeAutomaticBattleAction(const BattleAction & ba);
|
||||
bool makePlayerBattleAction(PlayerColor player, const BattleAction & ba);
|
||||
bool makeAutomaticBattleAction(const BattleInfo & battle, const BattleAction & ba);
|
||||
bool makePlayerBattleAction(const BattleInfo & battle, PlayerColor player, const BattleAction & ba);
|
||||
};
|
||||
|
@ -35,7 +35,7 @@ void BattleFlowProcessor::setGameHandler(CGameHandler * newGameHandler)
|
||||
gameHandler = newGameHandler;
|
||||
}
|
||||
|
||||
void BattleFlowProcessor::summonGuardiansHelper(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 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
|
||||
{
|
||||
int x = targetPosition.getX();
|
||||
int y = targetPosition.getY();
|
||||
@ -110,34 +110,33 @@ void BattleFlowProcessor::summonGuardiansHelper(std::vector<BattleHex> & output,
|
||||
}
|
||||
}
|
||||
|
||||
void BattleFlowProcessor::tryPlaceMoats()
|
||||
void BattleFlowProcessor::tryPlaceMoats(const BattleInfo & battle)
|
||||
{
|
||||
//Moat should be initialized here, because only here we can use spellcasting
|
||||
if (gameHandler->gameState()->curB->town && gameHandler->gameState()->curB->town->fortLevel() >= CGTownInstance::CITADEL)
|
||||
if (battle.town && battle.town->fortLevel() >= CGTownInstance::CITADEL)
|
||||
{
|
||||
const auto * h = gameHandler->gameState()->curB->battleGetFightingHero(BattleSide::DEFENDER);
|
||||
const auto * h = battle.battleGetFightingHero(BattleSide::DEFENDER);
|
||||
const auto * actualCaster = h ? static_cast<const spells::Caster*>(h) : nullptr;
|
||||
auto moatCaster = spells::SilentCaster(gameHandler->gameState()->curB->getSidePlayer(BattleSide::DEFENDER), actualCaster);
|
||||
auto cast = spells::BattleCast(gameHandler->gameState()->curB, &moatCaster, spells::Mode::PASSIVE, gameHandler->gameState()->curB->town->town->moatAbility.toSpell());
|
||||
auto moatCaster = spells::SilentCaster(battle.getSidePlayer(BattleSide::DEFENDER), actualCaster);
|
||||
auto cast = spells::BattleCast(&battle, &moatCaster, spells::Mode::PASSIVE, battle.town->town->moatAbility.toSpell());
|
||||
auto target = spells::Target();
|
||||
cast.cast(gameHandler->spellEnv, target);
|
||||
}
|
||||
}
|
||||
|
||||
void BattleFlowProcessor::onBattleStarted()
|
||||
void BattleFlowProcessor::onBattleStarted(const BattleInfo & battle)
|
||||
{
|
||||
gameHandler->setBattle(gameHandler->gameState()->curB);
|
||||
assert(gameHandler->gameState()->curB);
|
||||
gameHandler->setBattle(&battle);
|
||||
|
||||
tryPlaceMoats();
|
||||
tryPlaceMoats(battle);
|
||||
|
||||
gameHandler->turnTimerHandler.onBattleStart();
|
||||
gameHandler->turnTimerHandler.onBattleStart(battle.battleID);
|
||||
|
||||
if (gameHandler->gameState()->curB->tacticDistance == 0)
|
||||
onTacticsEnded();
|
||||
if (battle.tacticDistance == 0)
|
||||
onTacticsEnded(battle);
|
||||
}
|
||||
|
||||
void BattleFlowProcessor::trySummonGuardians(const CStack * stack)
|
||||
void BattleFlowProcessor::trySummonGuardians(const BattleInfo & battle, const CStack * stack)
|
||||
{
|
||||
if (!stack->hasBonusOfType(BonusType::SUMMON_GUARDIANS))
|
||||
return;
|
||||
@ -156,14 +155,14 @@ void BattleFlowProcessor::trySummonGuardians(const CStack * stack)
|
||||
if (!guardianIsBig)
|
||||
targetHexes = stack->getSurroundingHexes();
|
||||
else
|
||||
summonGuardiansHelper(targetHexes, stack->getPosition(), stack->unitSide(), targetIsBig);
|
||||
summonGuardiansHelper(battle, targetHexes, stack->getPosition(), stack->unitSide(), targetIsBig);
|
||||
|
||||
for(auto hex : targetHexes)
|
||||
{
|
||||
if(accessibility.accessible(hex, guardianIsBig, stack->unitSide())) //without this multiple creatures can occupy one hex
|
||||
{
|
||||
battle::UnitInfo info;
|
||||
info.id = gameHandler->gameState()->curB->battleNextUnitId();
|
||||
info.id = battle.battleNextUnitId();
|
||||
info.count = std::max(1, (int)(stack->getCount() * 0.01 * summonInfo->val));
|
||||
info.type = creatureData;
|
||||
info.side = stack->unitSide();
|
||||
@ -178,11 +177,11 @@ void BattleFlowProcessor::trySummonGuardians(const CStack * stack)
|
||||
}
|
||||
}
|
||||
|
||||
void BattleFlowProcessor::castOpeningSpells()
|
||||
void BattleFlowProcessor::castOpeningSpells(const BattleInfo & battle)
|
||||
{
|
||||
for (int i = 0; i < 2; ++i)
|
||||
{
|
||||
auto h = gameHandler->gameState()->curB->battleGetFightingHero(i);
|
||||
auto h = battle.battleGetFightingHero(i);
|
||||
if (!h)
|
||||
continue;
|
||||
|
||||
@ -194,7 +193,7 @@ void BattleFlowProcessor::castOpeningSpells()
|
||||
|
||||
const CSpell * spell = SpellID(b->subtype).toSpell();
|
||||
|
||||
spells::BattleCast parameters(gameHandler->gameState()->curB, &caster, spells::Mode::PASSIVE, spell);
|
||||
spells::BattleCast parameters(&battle, &caster, spells::Mode::PASSIVE, spell);
|
||||
parameters.setSpellLevel(3);
|
||||
parameters.setEffectDuration(b->val);
|
||||
parameters.massive = true;
|
||||
@ -203,55 +202,55 @@ void BattleFlowProcessor::castOpeningSpells()
|
||||
}
|
||||
}
|
||||
|
||||
void BattleFlowProcessor::onTacticsEnded()
|
||||
void BattleFlowProcessor::onTacticsEnded(const BattleInfo & battle)
|
||||
{
|
||||
//initial stacks appearance triggers, e.g. built-in bonus spells
|
||||
auto initialStacks = gameHandler->gameState()->curB->stacks; //use temporary variable to outclude summoned stacks added to gameHandler->gameState()->curB->stacks from processing
|
||||
auto initialStacks = battle.stacks; //use temporary variable to outclude summoned stacks added to battle.stacks from processing
|
||||
|
||||
for (CStack * stack : initialStacks)
|
||||
{
|
||||
trySummonGuardians(stack);
|
||||
stackEnchantedTrigger(stack);
|
||||
trySummonGuardians(battle, stack);
|
||||
stackEnchantedTrigger(battle, stack);
|
||||
}
|
||||
|
||||
castOpeningSpells();
|
||||
castOpeningSpells(battle);
|
||||
|
||||
// it is possible that due to opening spells one side was eliminated -> check for end of battle
|
||||
if (owner->checkBattleStateChanges())
|
||||
if (owner->checkBattleStateChanges(battle))
|
||||
return;
|
||||
|
||||
startNextRound(true);
|
||||
activateNextStack();
|
||||
startNextRound(battle, true);
|
||||
activateNextStack(battle);
|
||||
}
|
||||
|
||||
void BattleFlowProcessor::startNextRound(bool isFirstRound)
|
||||
void BattleFlowProcessor::startNextRound(const BattleInfo & battle, bool isFirstRound)
|
||||
{
|
||||
BattleNextRound bnr;
|
||||
bnr.round = gameHandler->gameState()->curB->round + 1;
|
||||
bnr.round = battle.round + 1;
|
||||
logGlobal->debug("Round %d", bnr.round);
|
||||
gameHandler->sendAndApply(&bnr);
|
||||
|
||||
auto obstacles = gameHandler->gameState()->curB->obstacles; //we copy container, because we're going to modify it
|
||||
auto obstacles = battle.obstacles; //we copy container, because we're going to modify it
|
||||
for (auto &obstPtr : obstacles)
|
||||
{
|
||||
if (const SpellCreatedObstacle *sco = dynamic_cast<const SpellCreatedObstacle *>(obstPtr.get()))
|
||||
if (sco->turnsRemaining == 0)
|
||||
removeObstacle(*obstPtr);
|
||||
removeObstacle(battle, *obstPtr);
|
||||
}
|
||||
|
||||
const BattleInfo & curB = *gameHandler->gameState()->curB;
|
||||
const BattleInfo & curB = *&battle;
|
||||
|
||||
for(auto stack : curB.stacks)
|
||||
{
|
||||
if(stack->alive() && !isFirstRound)
|
||||
stackEnchantedTrigger(stack);
|
||||
stackEnchantedTrigger(battle, stack);
|
||||
}
|
||||
}
|
||||
|
||||
const CStack * BattleFlowProcessor::getNextStack()
|
||||
const CStack * BattleFlowProcessor::getNextStack(const BattleInfo & battle)
|
||||
{
|
||||
std::vector<battle::Units> q;
|
||||
gameHandler->gameState()->curB->battleGetTurnOrder(q, 1, 0, -1); //todo: get rid of "turn -1"
|
||||
battle.battleGetTurnOrder(q, 1, 0, -1); //todo: get rid of "turn -1"
|
||||
|
||||
if(q.empty())
|
||||
return nullptr;
|
||||
@ -284,31 +283,29 @@ const CStack * BattleFlowProcessor::getNextStack()
|
||||
return stack;
|
||||
}
|
||||
|
||||
void BattleFlowProcessor::activateNextStack()
|
||||
void BattleFlowProcessor::activateNextStack(const BattleInfo & battle)
|
||||
{
|
||||
const auto & curB = *gameHandler->gameState()->curB;
|
||||
|
||||
// Find next stack that requires manual control
|
||||
for (;;)
|
||||
{
|
||||
// battle has ended
|
||||
if (owner->checkBattleStateChanges())
|
||||
if (owner->checkBattleStateChanges(battle))
|
||||
return;
|
||||
|
||||
const CStack * next = getNextStack();
|
||||
const CStack * next = getNextStack(battle);
|
||||
|
||||
if (!next)
|
||||
{
|
||||
// No stacks to move - start next round
|
||||
startNextRound(false);
|
||||
next = getNextStack();
|
||||
startNextRound(battle, false);
|
||||
next = getNextStack(battle);
|
||||
if (!next)
|
||||
throw std::runtime_error("Failed to find valid stack to act!");
|
||||
}
|
||||
|
||||
BattleUnitsChanged removeGhosts;
|
||||
|
||||
for(auto stack : curB.stacks)
|
||||
for(auto stack : battle.stacks)
|
||||
{
|
||||
if(stack->ghostPending)
|
||||
removeGhosts.changedStacks.emplace_back(stack->unitId(), UnitChanges::EOperation::REMOVE);
|
||||
@ -317,20 +314,18 @@ void BattleFlowProcessor::activateNextStack()
|
||||
if(!removeGhosts.changedStacks.empty())
|
||||
gameHandler->sendAndApply(&removeGhosts);
|
||||
|
||||
gameHandler->turnTimerHandler.onBattleNextStack(*next);
|
||||
gameHandler->turnTimerHandler.onBattleNextStack(battle.battleID, *next);
|
||||
|
||||
if (!tryMakeAutomaticAction(next))
|
||||
if (!tryMakeAutomaticAction(battle, next))
|
||||
{
|
||||
setActiveStack(next);
|
||||
setActiveStack(battle, next);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool BattleFlowProcessor::tryMakeAutomaticAction(const CStack * next)
|
||||
bool BattleFlowProcessor::tryMakeAutomaticAction(const BattleInfo & battle, const CStack * next)
|
||||
{
|
||||
const auto & curB = *gameHandler->gameState()->curB;
|
||||
|
||||
// check for bad morale => freeze
|
||||
int nextStackMorale = next->moraleVal();
|
||||
if(!next->hadMorale && !next->waited() && nextStackMorale < 0)
|
||||
@ -346,7 +341,7 @@ bool BattleFlowProcessor::tryMakeAutomaticAction(const CStack * next)
|
||||
ba.side = next->unitSide();
|
||||
ba.stackNumber = next->unitId();
|
||||
|
||||
makeAutomaticAction(next, ba);
|
||||
makeAutomaticAction(battle, next, ba);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@ -354,7 +349,7 @@ bool BattleFlowProcessor::tryMakeAutomaticAction(const CStack * next)
|
||||
if (next->hasBonusOfType(BonusType::ATTACKS_NEAREST_CREATURE)) //while in berserk
|
||||
{
|
||||
logGlobal->trace("Handle Berserk effect");
|
||||
std::pair<const battle::Unit *, BattleHex> attackInfo = curB.getNearestStack(next);
|
||||
std::pair<const battle::Unit *, BattleHex> attackInfo = battle.getNearestStack(next);
|
||||
if (attackInfo.first != nullptr)
|
||||
{
|
||||
BattleAction attack;
|
||||
@ -364,12 +359,12 @@ bool BattleFlowProcessor::tryMakeAutomaticAction(const CStack * next)
|
||||
attack.aimToHex(attackInfo.second);
|
||||
attack.aimToUnit(attackInfo.first);
|
||||
|
||||
makeAutomaticAction(next, attack);
|
||||
makeAutomaticAction(battle, next, attack);
|
||||
logGlobal->trace("Attacked nearest target %s", attackInfo.first->getDescription());
|
||||
}
|
||||
else
|
||||
{
|
||||
makeStackDoNothing(next);
|
||||
makeStackDoNothing(battle, next);
|
||||
logGlobal->trace("No target found");
|
||||
}
|
||||
return true;
|
||||
@ -390,12 +385,12 @@ bool BattleFlowProcessor::tryMakeAutomaticAction(const CStack * next)
|
||||
|
||||
const battle::Unit * target = nullptr;
|
||||
|
||||
for(auto & elem : gameHandler->gameState()->curB->stacks)
|
||||
for(auto & elem : battle.stacks)
|
||||
{
|
||||
if(elem->unitType()->getId() != CreatureID::CATAPULT
|
||||
&& elem->unitOwner() != next->unitOwner()
|
||||
&& elem->isValidTarget()
|
||||
&& gameHandler->gameState()->curB->battleCanShoot(next, elem->getPosition()))
|
||||
&& battle.battleCanShoot(next, elem->getPosition()))
|
||||
{
|
||||
target = elem;
|
||||
break;
|
||||
@ -404,23 +399,23 @@ bool BattleFlowProcessor::tryMakeAutomaticAction(const CStack * next)
|
||||
|
||||
if(target == nullptr)
|
||||
{
|
||||
makeStackDoNothing(next);
|
||||
makeStackDoNothing(battle, next);
|
||||
}
|
||||
else
|
||||
{
|
||||
attack.aimToUnit(target);
|
||||
makeAutomaticAction(next, attack);
|
||||
makeAutomaticAction(battle, next, attack);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
if (next->unitType()->getId() == CreatureID::CATAPULT)
|
||||
{
|
||||
const auto & attackableBattleHexes = curB.getAttackableBattleHexes();
|
||||
const auto & attackableBattleHexes = battle.getAttackableBattleHexes();
|
||||
|
||||
if (attackableBattleHexes.empty())
|
||||
{
|
||||
makeStackDoNothing(next);
|
||||
makeStackDoNothing(battle, next);
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -431,7 +426,7 @@ bool BattleFlowProcessor::tryMakeAutomaticAction(const CStack * next)
|
||||
attack.side = next->unitSide();
|
||||
attack.stackNumber = next->unitId();
|
||||
|
||||
makeAutomaticAction(next, attack);
|
||||
makeAutomaticAction(battle, next, attack);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@ -445,7 +440,7 @@ bool BattleFlowProcessor::tryMakeAutomaticAction(const CStack * next)
|
||||
|
||||
if (possibleStacks.empty())
|
||||
{
|
||||
makeStackDoNothing(next);
|
||||
makeStackDoNothing(battle, next);
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -460,22 +455,22 @@ bool BattleFlowProcessor::tryMakeAutomaticAction(const CStack * next)
|
||||
heal.side = next->unitSide();
|
||||
heal.stackNumber = next->unitId();
|
||||
|
||||
makeAutomaticAction(next, heal);
|
||||
makeAutomaticAction(battle, next, heal);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
stackTurnTrigger(next); //various effects
|
||||
stackTurnTrigger(battle, next); //various effects
|
||||
|
||||
if(next->fear)
|
||||
{
|
||||
makeStackDoNothing(next); //end immediately if stack was affected by fear
|
||||
makeStackDoNothing(battle, next); //end immediately if stack was affected by fear
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool BattleFlowProcessor::rollGoodMorale(const CStack * next)
|
||||
bool BattleFlowProcessor::rollGoodMorale(const BattleInfo & battle, const CStack * next)
|
||||
{
|
||||
//check for good morale
|
||||
auto nextStackMorale = next->moraleVal();
|
||||
@ -503,27 +498,25 @@ bool BattleFlowProcessor::rollGoodMorale(const CStack * next)
|
||||
return false;
|
||||
}
|
||||
|
||||
void BattleFlowProcessor::onActionMade(const BattleAction &ba)
|
||||
void BattleFlowProcessor::onActionMade(const BattleInfo & battle, const BattleAction &ba)
|
||||
{
|
||||
const auto & battle = gameHandler->gameState()->curB;
|
||||
|
||||
const CStack * actedStack = battle.battleGetStackByID(ba.stackNumber, false);
|
||||
const CStack * activeStack = battle.battleGetStackByID(battle.getActiveStackID(), false);
|
||||
if (ba.actionType == EActionType::END_TACTIC_PHASE)
|
||||
{
|
||||
onTacticsEnded();
|
||||
onTacticsEnded(battle);
|
||||
return;
|
||||
}
|
||||
|
||||
const CStack * actedStack = battle->battleGetStackByID(ba.stackNumber, false);
|
||||
const CStack * activeStack = battle->battleGetStackByID(battle->getActiveStackID(), false);
|
||||
|
||||
//we're after action, all results applied
|
||||
|
||||
// check whether action has ended the battle
|
||||
if(owner->checkBattleStateChanges())
|
||||
if(owner->checkBattleStateChanges(battle))
|
||||
return;
|
||||
|
||||
// tactics - next stack will be selected by player
|
||||
if(battle->tacticDistance != 0)
|
||||
if(battle.tacticDistance != 0)
|
||||
return;
|
||||
|
||||
if (ba.isUnitAction())
|
||||
@ -531,10 +524,10 @@ void BattleFlowProcessor::onActionMade(const BattleAction &ba)
|
||||
assert(activeStack != nullptr);
|
||||
assert(actedStack != nullptr);
|
||||
|
||||
if (rollGoodMorale(actedStack))
|
||||
if (rollGoodMorale(battle, actedStack))
|
||||
{
|
||||
// Good morale - same stack makes 2nd turn
|
||||
setActiveStack(actedStack);
|
||||
setActiveStack(battle, actedStack);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@ -544,36 +537,36 @@ void BattleFlowProcessor::onActionMade(const BattleAction &ba)
|
||||
{
|
||||
// this is action made by hero AND unit is alive (e.g. not killed by casted spell)
|
||||
// keep current active stack for next action
|
||||
setActiveStack(activeStack);
|
||||
setActiveStack(battle, activeStack);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
activateNextStack();
|
||||
activateNextStack(battle);
|
||||
}
|
||||
|
||||
void BattleFlowProcessor::makeStackDoNothing(const CStack * next)
|
||||
void BattleFlowProcessor::makeStackDoNothing(const BattleInfo & battle, const CStack * next)
|
||||
{
|
||||
BattleAction doNothing;
|
||||
doNothing.actionType = EActionType::NO_ACTION;
|
||||
doNothing.side = next->unitSide();
|
||||
doNothing.stackNumber = next->unitId();
|
||||
|
||||
makeAutomaticAction(next, doNothing);
|
||||
makeAutomaticAction(battle, next, doNothing);
|
||||
}
|
||||
|
||||
bool BattleFlowProcessor::makeAutomaticAction(const CStack *stack, BattleAction &ba)
|
||||
bool BattleFlowProcessor::makeAutomaticAction(const BattleInfo & battle, const CStack *stack, BattleAction &ba)
|
||||
{
|
||||
BattleSetActiveStack bsa;
|
||||
bsa.stack = stack->unitId();
|
||||
bsa.askPlayerInterface = false;
|
||||
gameHandler->sendAndApply(&bsa);
|
||||
|
||||
bool ret = owner->makeAutomaticBattleAction(ba);
|
||||
bool ret = owner->makeAutomaticBattleAction(battle, ba);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void BattleFlowProcessor::stackEnchantedTrigger(const CStack * st)
|
||||
void BattleFlowProcessor::stackEnchantedTrigger(const BattleInfo & battle, const CStack * st)
|
||||
{
|
||||
auto bl = *(st->getBonuses(Selector::type()(BonusType::ENCHANTED)));
|
||||
for(auto b : bl)
|
||||
@ -585,7 +578,7 @@ void BattleFlowProcessor::stackEnchantedTrigger(const CStack * st)
|
||||
const int32_t val = bl.valOfBonuses(Selector::typeSubtype(b->type, b->subtype));
|
||||
const int32_t level = ((val > 3) ? (val - 3) : val);
|
||||
|
||||
spells::BattleCast battleCast(gameHandler->gameState()->curB, st, spells::Mode::PASSIVE, sp);
|
||||
spells::BattleCast battleCast(&battle, st, spells::Mode::PASSIVE, sp);
|
||||
//this makes effect accumulate for at most 50 turns by default, but effect may be permanent and last till the end of battle
|
||||
battleCast.setEffectDuration(50);
|
||||
battleCast.setSpellLevel(level);
|
||||
@ -593,7 +586,7 @@ void BattleFlowProcessor::stackEnchantedTrigger(const CStack * st)
|
||||
|
||||
if(val > 3)
|
||||
{
|
||||
for(auto s : gameHandler->gameState()->curB->battleGetAllStacks())
|
||||
for(auto s : battle.battleGetAllStacks())
|
||||
if(gameHandler->battleMatchOwner(st, s, true) && s->isValidTarget()) //all allied
|
||||
target.emplace_back(s);
|
||||
}
|
||||
@ -605,14 +598,14 @@ void BattleFlowProcessor::stackEnchantedTrigger(const CStack * st)
|
||||
}
|
||||
}
|
||||
|
||||
void BattleFlowProcessor::removeObstacle(const CObstacleInstance & obstacle)
|
||||
void BattleFlowProcessor::removeObstacle(const BattleInfo & battle, const CObstacleInstance & obstacle)
|
||||
{
|
||||
BattleObstaclesChanged obsRem;
|
||||
obsRem.changes.emplace_back(obstacle.uniqueID, ObstacleChanges::EOperation::REMOVE);
|
||||
gameHandler->sendAndApply(&obsRem);
|
||||
}
|
||||
|
||||
void BattleFlowProcessor::stackTurnTrigger(const CStack *st)
|
||||
void BattleFlowProcessor::stackTurnTrigger(const BattleInfo & battle, const CStack *st)
|
||||
{
|
||||
BattleTriggerEffect bte;
|
||||
bte.stackID = st->unitId();
|
||||
@ -626,13 +619,13 @@ void BattleFlowProcessor::stackTurnTrigger(const CStack *st)
|
||||
{
|
||||
bool unbind = true;
|
||||
BonusList bl = *(st->getBonuses(Selector::type()(BonusType::BIND_EFFECT)));
|
||||
auto adjacent = gameHandler->gameState()->curB->battleAdjacentUnits(st);
|
||||
auto adjacent = battle.battleAdjacentUnits(st);
|
||||
|
||||
for (auto b : bl)
|
||||
{
|
||||
if(b->additionalInfo != CAddInfo::NONE)
|
||||
{
|
||||
const CStack * stack = gameHandler->gameState()->curB->battleGetStackByID(b->additionalInfo[0]); //binding stack must be alive and adjacent
|
||||
const CStack * stack = battle.battleGetStackByID(b->additionalInfo[0]); //binding stack must be alive and adjacent
|
||||
if(stack)
|
||||
{
|
||||
if(vstd::contains(adjacent, stack)) //binding stack is still present
|
||||
@ -668,8 +661,8 @@ void BattleFlowProcessor::stackTurnTrigger(const CStack *st)
|
||||
}
|
||||
if(st->hasBonusOfType(BonusType::MANA_DRAIN) && !st->drainedMana)
|
||||
{
|
||||
const PlayerColor opponent = gameHandler->gameState()->curB->otherPlayer(gameHandler->gameState()->curB->battleGetOwner(st));
|
||||
const CGHeroInstance * opponentHero = gameHandler->gameState()->curB->getHero(opponent);
|
||||
const PlayerColor opponent = battle.otherPlayer(battle.battleGetOwner(st));
|
||||
const CGHeroInstance * opponentHero = battle.getHero(opponent);
|
||||
if(opponentHero)
|
||||
{
|
||||
ui32 manaDrained = st->valOfBonuses(BonusType::MANA_DRAIN);
|
||||
@ -686,7 +679,7 @@ void BattleFlowProcessor::stackTurnTrigger(const CStack *st)
|
||||
if (st->isLiving() && !st->hasBonusOfType(BonusType::FEARLESS))
|
||||
{
|
||||
bool fearsomeCreature = false;
|
||||
for (CStack * stack : gameHandler->gameState()->curB->stacks)
|
||||
for (CStack * stack : battle.stacks)
|
||||
{
|
||||
if (gameHandler->battleMatchOwner(st, stack) && stack->alive() && stack->hasBonusOfType(BonusType::FEAR))
|
||||
{
|
||||
@ -704,8 +697,8 @@ void BattleFlowProcessor::stackTurnTrigger(const CStack *st)
|
||||
}
|
||||
}
|
||||
BonusList bl = *(st->getBonuses(Selector::type()(BonusType::ENCHANTER)));
|
||||
int side = gameHandler->gameState()->curB->whatSide(st->unitOwner());
|
||||
if(st->canCast() && gameHandler->gameState()->curB->battleGetEnchanterCounter(side) == 0)
|
||||
int side = battle.whatSide(st->unitOwner());
|
||||
if(st->canCast() && battle.battleGetEnchanterCounter(side) == 0)
|
||||
{
|
||||
bool cast = false;
|
||||
while(!bl.empty() && !cast)
|
||||
@ -717,7 +710,7 @@ void BattleFlowProcessor::stackTurnTrigger(const CStack *st)
|
||||
{
|
||||
return b == bonus.get();
|
||||
});
|
||||
spells::BattleCast parameters(gameHandler->gameState()->curB, st, spells::Mode::ENCHANTER, spell);
|
||||
spells::BattleCast parameters(&battle, st, spells::Mode::ENCHANTER, spell);
|
||||
parameters.setSpellLevel(bonus->val);
|
||||
parameters.massive = true;
|
||||
parameters.smart = true;
|
||||
@ -739,7 +732,7 @@ void BattleFlowProcessor::stackTurnTrigger(const CStack *st)
|
||||
}
|
||||
}
|
||||
|
||||
void BattleFlowProcessor::setActiveStack(const CStack * stack)
|
||||
void BattleFlowProcessor::setActiveStack(const BattleInfo & battle, const CStack * stack)
|
||||
{
|
||||
assert(stack);
|
||||
|
||||
|
@ -13,6 +13,7 @@ VCMI_LIB_NAMESPACE_BEGIN
|
||||
class CStack;
|
||||
struct BattleHex;
|
||||
class BattleAction;
|
||||
class BattleInfo;
|
||||
struct CObstacleInstance;
|
||||
VCMI_LIB_NAMESPACE_END
|
||||
|
||||
@ -25,31 +26,31 @@ class BattleFlowProcessor : boost::noncopyable
|
||||
BattleProcessor * owner;
|
||||
CGameHandler * gameHandler;
|
||||
|
||||
const CStack * getNextStack();
|
||||
const CStack * getNextStack(const BattleInfo & battle);
|
||||
|
||||
bool rollGoodMorale(const CStack * stack);
|
||||
bool tryMakeAutomaticAction(const CStack * stack);
|
||||
bool rollGoodMorale(const BattleInfo & battle, const CStack * stack);
|
||||
bool tryMakeAutomaticAction(const BattleInfo & battle, const CStack * stack);
|
||||
|
||||
void summonGuardiansHelper(std::vector<BattleHex> & output, const BattleHex & targetPosition, ui8 side, bool targetIsTwoHex);
|
||||
void trySummonGuardians(const CStack * stack);
|
||||
void tryPlaceMoats();
|
||||
void castOpeningSpells();
|
||||
void activateNextStack();
|
||||
void startNextRound(bool isFirstRound);
|
||||
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 stackEnchantedTrigger(const CStack * stack);
|
||||
void removeObstacle(const CObstacleInstance & obstacle);
|
||||
void stackTurnTrigger(const CStack * stack);
|
||||
void setActiveStack(const CStack * stack);
|
||||
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 makeStackDoNothing(const CStack * next);
|
||||
bool makeAutomaticAction(const CStack * stack, BattleAction & ba); //used when action is taken by stack without volition of player (eg. unguided catapult attack)
|
||||
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)
|
||||
|
||||
public:
|
||||
explicit BattleFlowProcessor(BattleProcessor * owner);
|
||||
void setGameHandler(CGameHandler * newGameHandler);
|
||||
|
||||
void onBattleStarted();
|
||||
void onTacticsEnded();
|
||||
void onActionMade(const BattleAction & ba);
|
||||
void onBattleStarted(const BattleInfo & battle);
|
||||
void onTacticsEnded(const BattleInfo & battle);
|
||||
void onActionMade(const BattleInfo & battle, const BattleAction & ba);
|
||||
};
|
||||
|
@ -54,8 +54,8 @@ void BattleProcessor::startBattlePrimary(const CArmedInstance *army1, const CArm
|
||||
const CGHeroInstance *hero1, const CGHeroInstance *hero2, bool creatureBank,
|
||||
const CGTownInstance *town) //use hero=nullptr for no hero
|
||||
{
|
||||
if(gameHandler->gameState()->curB)
|
||||
gameHandler->gameState()->curB.dellNull();
|
||||
assert(gameHandler->gameState()->getBattle(army1->getOwner()) == nullptr);
|
||||
assert(gameHandler->gameState()->getBattle(army2->getOwner()) == nullptr);
|
||||
|
||||
engageIntoBattle(army1->tempOwner);
|
||||
engageIntoBattle(army2->tempOwner);
|
||||
@ -67,10 +67,12 @@ void BattleProcessor::startBattlePrimary(const CArmedInstance *army1, const CArm
|
||||
heroes[0] = hero1;
|
||||
heroes[1] = hero2;
|
||||
|
||||
resultProcessor->setupBattle();
|
||||
setupBattle(tile, armies, heroes, creatureBank, town); //initializes stacks, places creatures on battlefield, blocks and informs player interfaces
|
||||
auto battleID = setupBattle(tile, armies, heroes, creatureBank, town); //initializes stacks, places creatures on battlefield, blocks and informs player interfaces
|
||||
|
||||
auto lastBattleQuery = std::dynamic_pointer_cast<CBattleQuery>(gameHandler->queries->topQuery(gameHandler->gameState()->curB->sides[0].color));
|
||||
const auto * battle = gameHandler->gameState()->getBattle(battleID);
|
||||
assert(battle);
|
||||
|
||||
auto lastBattleQuery = std::dynamic_pointer_cast<CBattleQuery>(gameHandler->queries->topQuery(battle->sides[0].color));
|
||||
|
||||
//existing battle query for retying auto-combat
|
||||
if(lastBattleQuery)
|
||||
@ -86,13 +88,13 @@ void BattleProcessor::startBattlePrimary(const CArmedInstance *army1, const CArm
|
||||
}
|
||||
}
|
||||
|
||||
lastBattleQuery->bi = gameHandler->gameState()->curB;
|
||||
lastBattleQuery->bi = battle;
|
||||
lastBattleQuery->result = std::nullopt;
|
||||
lastBattleQuery->belligerents[0] = gameHandler->gameState()->curB->sides[0].armyObject;
|
||||
lastBattleQuery->belligerents[1] = gameHandler->gameState()->curB->sides[1].armyObject;
|
||||
lastBattleQuery->belligerents[0] = battle->sides[0].armyObject;
|
||||
lastBattleQuery->belligerents[1] = battle->sides[1].armyObject;
|
||||
}
|
||||
|
||||
auto nextBattleQuery = std::make_shared<CBattleQuery>(gameHandler, gameHandler->gameState()->curB);
|
||||
auto nextBattleQuery = std::make_shared<CBattleQuery>(gameHandler, battle);
|
||||
for(int i : {0, 1})
|
||||
{
|
||||
if(heroes[i])
|
||||
@ -102,7 +104,7 @@ void BattleProcessor::startBattlePrimary(const CArmedInstance *army1, const CArm
|
||||
}
|
||||
gameHandler->queries->addQuery(nextBattleQuery);
|
||||
|
||||
flowProcessor->onBattleStarted();
|
||||
flowProcessor->onBattleStarted(*battle);
|
||||
}
|
||||
|
||||
void BattleProcessor::startBattleI(const CArmedInstance *army1, const CArmedInstance *army2, int3 tile, bool creatureBank)
|
||||
@ -118,7 +120,7 @@ void BattleProcessor::startBattleI(const CArmedInstance *army1, const CArmedInst
|
||||
startBattleI(army1, army2, army2->visitablePos(), creatureBank);
|
||||
}
|
||||
|
||||
void BattleProcessor::setupBattle(int3 tile, const CArmedInstance *armies[2], const CGHeroInstance *heroes[2], bool creatureBank, const CGTownInstance *town)
|
||||
BattleID BattleProcessor::setupBattle(int3 tile, const CArmedInstance *armies[2], const CGHeroInstance *heroes[2], bool creatureBank, const CGTownInstance *town)
|
||||
{
|
||||
const auto & t = *gameHandler->getTile(tile);
|
||||
TerrainId terrain = t.terType->getId();
|
||||
@ -132,6 +134,7 @@ void BattleProcessor::setupBattle(int3 tile, const CArmedInstance *armies[2], co
|
||||
//send info about battles
|
||||
BattleStart bs;
|
||||
bs.info = BattleInfo::setupBattle(tile, terrain, terType, armies, heroes, creatureBank, town);
|
||||
bs.battleID = gameHandler->gameState()->nextBattleID;
|
||||
|
||||
engageIntoBattle(bs.info->sides[0].color);
|
||||
engageIntoBattle(bs.info->sides[1].color);
|
||||
@ -140,28 +143,30 @@ void BattleProcessor::setupBattle(int3 tile, const CArmedInstance *armies[2], co
|
||||
bs.info->replayAllowed = lastBattleQuery == nullptr && !bs.info->sides[1].color.isValidPlayer();
|
||||
|
||||
gameHandler->sendAndApply(&bs);
|
||||
|
||||
return bs.battleID;
|
||||
}
|
||||
|
||||
bool BattleProcessor::checkBattleStateChanges()
|
||||
bool BattleProcessor::checkBattleStateChanges(const BattleInfo & battle)
|
||||
{
|
||||
//check if drawbridge state need to be changes
|
||||
if (gameHandler->battleGetSiegeLevel() > 0)
|
||||
updateGateState();
|
||||
updateGateState(battle);
|
||||
|
||||
if (resultProcessor->battleIsEnding())
|
||||
if (resultProcessor->battleIsEnding(battle))
|
||||
return true;
|
||||
|
||||
//check if battle ended
|
||||
if (auto result = gameHandler->battleIsFinished())
|
||||
{
|
||||
setBattleResult(EBattleResult::NORMAL, *result);
|
||||
setBattleResult(battle, EBattleResult::NORMAL, *result);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void BattleProcessor::updateGateState()
|
||||
void BattleProcessor::updateGateState(const BattleInfo & battle)
|
||||
{
|
||||
// GATE_BRIDGE - leftmost tile, located over moat
|
||||
// GATE_OUTER - central tile, mostly covered by gate image
|
||||
@ -178,17 +183,19 @@ void BattleProcessor::updateGateState()
|
||||
// - deals moat damage to attacker if bridge is closed (fortress only)
|
||||
|
||||
bool hasForceFieldOnBridge = !gameHandler->battleGetAllObstaclesOnPos(BattleHex(BattleHex::GATE_BRIDGE), true).empty();
|
||||
bool hasStackAtGateInner = gameHandler->gameState()->curB->battleGetUnitByPos(BattleHex(BattleHex::GATE_INNER), false) != nullptr;
|
||||
bool hasStackAtGateOuter = gameHandler->gameState()->curB->battleGetUnitByPos(BattleHex(BattleHex::GATE_OUTER), false) != nullptr;
|
||||
bool hasStackAtGateBridge = gameHandler->gameState()->curB->battleGetUnitByPos(BattleHex(BattleHex::GATE_BRIDGE), false) != nullptr;
|
||||
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)
|
||||
{
|
||||
return obst->obstacleType == CObstacleInstance::MOAT;
|
||||
});
|
||||
|
||||
BattleUpdateGateState db;
|
||||
db.state = gameHandler->gameState()->curB->si.gateState;
|
||||
if (gameHandler->gameState()->curB->si.wallState[EWallPart::GATE] == EWallState::DESTROYED)
|
||||
db.state = battle.si.gateState;
|
||||
db.battleID = battle.battleID;
|
||||
|
||||
if (battle.si.wallState.at(EWallPart::GATE) == EWallState::DESTROYED)
|
||||
{
|
||||
db.state = EGateState::DESTROYED;
|
||||
}
|
||||
@ -212,37 +219,54 @@ void BattleProcessor::updateGateState()
|
||||
db.state = EGateState::CLOSED;
|
||||
}
|
||||
|
||||
if (db.state != gameHandler->gameState()->curB->si.gateState)
|
||||
if (db.state != battle.si.gateState)
|
||||
gameHandler->sendAndApply(&db);
|
||||
}
|
||||
|
||||
bool BattleProcessor::makePlayerBattleAction(PlayerColor player, const BattleAction &ba)
|
||||
bool BattleProcessor::makePlayerBattleAction(const BattleID & battleID, PlayerColor player, const BattleAction &ba)
|
||||
{
|
||||
bool result = actionsProcessor->makePlayerBattleAction(player, ba);
|
||||
if (!resultProcessor->battleIsEnding() && gameHandler->gameState()->curB != nullptr)
|
||||
flowProcessor->onActionMade(ba);
|
||||
const auto * battle = gameHandler->gameState()->getBattle(battleID);
|
||||
|
||||
if (!battle)
|
||||
return false;
|
||||
|
||||
bool result = actionsProcessor->makePlayerBattleAction(*battle, player, ba);
|
||||
if (gameHandler->gameState()->getBattle(battleID) != nullptr && !resultProcessor->battleIsEnding(*battle))
|
||||
flowProcessor->onActionMade(*battle, ba);
|
||||
return result;
|
||||
}
|
||||
|
||||
void BattleProcessor::setBattleResult(EBattleResult resultType, int victoriusSide)
|
||||
void BattleProcessor::setBattleResult(const BattleInfo & battle, EBattleResult resultType, int victoriusSide)
|
||||
{
|
||||
resultProcessor->setBattleResult(resultType, victoriusSide);
|
||||
resultProcessor->endBattle(gameHandler->gameState()->curB->tile, gameHandler->gameState()->curB->battleGetFightingHero(0), gameHandler->gameState()->curB->battleGetFightingHero(1));
|
||||
resultProcessor->setBattleResult(battle, resultType, victoriusSide);
|
||||
resultProcessor->endBattle(battle);
|
||||
}
|
||||
|
||||
bool BattleProcessor::makeAutomaticBattleAction(const BattleAction &ba)
|
||||
bool BattleProcessor::makeAutomaticBattleAction(const BattleInfo & battle, const BattleAction &ba)
|
||||
{
|
||||
return actionsProcessor->makeAutomaticBattleAction(ba);
|
||||
return actionsProcessor->makeAutomaticBattleAction(battle, ba);
|
||||
}
|
||||
|
||||
void BattleProcessor::endBattleConfirm(const BattleInfo * battleInfo)
|
||||
void BattleProcessor::endBattleConfirm(const BattleID & battleID)
|
||||
{
|
||||
resultProcessor->endBattleConfirm(battleInfo);
|
||||
auto battle = gameHandler->gameState()->getBattle(battleID);
|
||||
assert(battle);
|
||||
|
||||
if (!battle)
|
||||
return;
|
||||
|
||||
resultProcessor->endBattleConfirm(*battle);
|
||||
}
|
||||
|
||||
void BattleProcessor::battleAfterLevelUp(const BattleResult &result)
|
||||
void BattleProcessor::battleAfterLevelUp(const BattleID & battleID, const BattleResult &result)
|
||||
{
|
||||
resultProcessor->battleAfterLevelUp(result);
|
||||
auto battle = gameHandler->gameState()->getBattle(battleID);
|
||||
assert(battle);
|
||||
|
||||
if (!battle)
|
||||
return;
|
||||
|
||||
resultProcessor->battleAfterLevelUp(*battle, result);
|
||||
}
|
||||
|
||||
void BattleProcessor::setGameHandler(CGameHandler * newGameHandler)
|
||||
|
@ -19,6 +19,7 @@ class BattleAction;
|
||||
class int3;
|
||||
class BattleInfo;
|
||||
struct BattleResult;
|
||||
class BattleID;
|
||||
VCMI_LIB_NAMESPACE_END
|
||||
|
||||
class CGameHandler;
|
||||
@ -40,15 +41,15 @@ class BattleProcessor : boost::noncopyable
|
||||
std::unique_ptr<BattleResultProcessor> resultProcessor;
|
||||
|
||||
void onTacticsEnded();
|
||||
void updateGateState();
|
||||
void updateGateState(const BattleInfo & battle);
|
||||
void engageIntoBattle(PlayerColor player);
|
||||
|
||||
bool checkBattleStateChanges();
|
||||
void setupBattle(int3 tile, const CArmedInstance *armies[2], const CGHeroInstance *heroes[2], bool creatureBank, const CGTownInstance *town);
|
||||
bool checkBattleStateChanges(const BattleInfo & battle);
|
||||
BattleID setupBattle(int3 tile, const CArmedInstance *armies[2], const CGHeroInstance *heroes[2], bool creatureBank, const CGTownInstance *town);
|
||||
|
||||
bool makeAutomaticBattleAction(const BattleAction & ba);
|
||||
bool makeAutomaticBattleAction(const BattleInfo & battle, const BattleAction & ba);
|
||||
|
||||
void setBattleResult(EBattleResult resultType, int victoriusSide);
|
||||
void setBattleResult(const BattleInfo & battle, EBattleResult resultType, int victoriusSide);
|
||||
|
||||
public:
|
||||
explicit BattleProcessor(CGameHandler * gameHandler);
|
||||
@ -65,12 +66,12 @@ public:
|
||||
void startBattleI(const CArmedInstance *army1, const CArmedInstance *army2, bool creatureBank = false);
|
||||
|
||||
/// Processing of incoming battle action netpack
|
||||
bool makePlayerBattleAction(PlayerColor player, const BattleAction & ba);
|
||||
bool makePlayerBattleAction(const BattleID & battleID, PlayerColor player, const BattleAction & ba);
|
||||
|
||||
/// Applies results of a battle once player agrees to them
|
||||
void endBattleConfirm(const BattleInfo * battleInfo);
|
||||
void endBattleConfirm(const BattleID & battleID);
|
||||
/// Applies results of a battle after potential levelup
|
||||
void battleAfterLevelUp(const BattleResult & result);
|
||||
void battleAfterLevelUp(const BattleID & battleID, const BattleResult & result);
|
||||
|
||||
template <typename Handler> void serialize(Handler &h, const int version)
|
||||
{
|
||||
|
@ -196,7 +196,7 @@ FinishingBattleHelper::FinishingBattleHelper()
|
||||
remainingBattleQueriesCount = 0;
|
||||
}
|
||||
|
||||
void BattleResultProcessor::endBattle(int3 tile, const CGHeroInstance * heroAttacker, const CGHeroInstance * heroDefender)
|
||||
void BattleResultProcessor::endBattle(const BattleInfo & battle)
|
||||
{
|
||||
auto const & giveExp = [](BattleResult &r)
|
||||
{
|
||||
@ -215,6 +215,10 @@ void BattleResultProcessor::endBattle(int3 tile, const CGHeroInstance * heroAtta
|
||||
|
||||
LOG_TRACE(logGlobal);
|
||||
|
||||
auto * battleResult = battleResults.at(battle.battleID).get();
|
||||
const auto * heroAttacker = battle.getSideHero(BattleSide::ATTACKER);
|
||||
const auto * heroDefender = battle.getSideHero(BattleSide::DEFENDER);
|
||||
|
||||
//Fill BattleResult structure with exp info
|
||||
giveExp(*battleResult);
|
||||
|
||||
@ -231,11 +235,11 @@ void BattleResultProcessor::endBattle(int3 tile, const CGHeroInstance * heroAtta
|
||||
if(heroDefender)
|
||||
battleResult->exp[1] = heroDefender->calculateXp(battleResult->exp[1]);
|
||||
|
||||
auto battleQuery = std::dynamic_pointer_cast<CBattleQuery>(gameHandler->queries->topQuery(gameHandler->gameState()->curB->sides[0].color));
|
||||
auto battleQuery = std::dynamic_pointer_cast<CBattleQuery>(gameHandler->queries->topQuery(battle.sides[0].color));
|
||||
if (!battleQuery)
|
||||
{
|
||||
logGlobal->error("Cannot find battle query!");
|
||||
gameHandler->complain("Player " + boost::lexical_cast<std::string>(gameHandler->gameState()->curB->sides[0].color) + " has no battle query at the top!");
|
||||
gameHandler->complain("Player " + boost::lexical_cast<std::string>(battle.sides[0].color) + " has no battle query at the top!");
|
||||
return;
|
||||
}
|
||||
|
||||
@ -243,12 +247,14 @@ void BattleResultProcessor::endBattle(int3 tile, const CGHeroInstance * heroAtta
|
||||
|
||||
//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;
|
||||
finishingBattle = std::make_unique<FinishingBattleHelper>(battleQuery, queriedPlayers);
|
||||
|
||||
assert(finishingBattles.count(battle.battleID) == 0);
|
||||
finishingBattles[battle.battleID] = std::make_unique<FinishingBattleHelper>(battleQuery, queriedPlayers);
|
||||
|
||||
// in battles against neutrals, 1st player can ask to replay battle manually
|
||||
if (!gameHandler->gameState()->curB->sides[1].color.isValidPlayer())
|
||||
if (!battle.sides[1].color.isValidPlayer())
|
||||
{
|
||||
auto battleDialogQuery = std::make_shared<CBattleDialogQuery>(gameHandler, gameHandler->gameState()->curB);
|
||||
auto battleDialogQuery = std::make_shared<CBattleDialogQuery>(gameHandler, &battle);
|
||||
battleResult->queryID = battleDialogQuery->queryID;
|
||||
gameHandler->queries->addQuery(battleDialogQuery);
|
||||
}
|
||||
@ -263,25 +269,27 @@ void BattleResultProcessor::endBattle(int3 tile, const CGHeroInstance * heroAtta
|
||||
otherBattleQuery->result = battleQuery->result;
|
||||
}
|
||||
|
||||
gameHandler->turnTimerHandler.onBattleEnd();
|
||||
gameHandler->sendAndApply(battleResult.get()); //after this point casualties objects are destroyed
|
||||
gameHandler->turnTimerHandler.onBattleEnd(battle.battleID);
|
||||
|
||||
if (battleResult->queryID == QueryID::NONE)
|
||||
endBattleConfirm(gameHandler->gameState()->curB);
|
||||
endBattleConfirm(battle);
|
||||
}
|
||||
|
||||
void BattleResultProcessor::endBattleConfirm(const BattleInfo * battleInfo)
|
||||
void BattleResultProcessor::endBattleConfirm(const BattleInfo & battle)
|
||||
{
|
||||
auto battleQuery = std::dynamic_pointer_cast<CBattleQuery>(gameHandler->queries->topQuery(battleInfo->sides.at(0).color));
|
||||
auto battleQuery = std::dynamic_pointer_cast<CBattleQuery>(gameHandler->queries->topQuery(battle.sides.at(0).color));
|
||||
if(!battleQuery)
|
||||
{
|
||||
logGlobal->trace("No battle query, battle end was confirmed by another player");
|
||||
return;
|
||||
}
|
||||
|
||||
const EBattleResult result = battleResult.get()->result;
|
||||
auto * battleResult = battleResults.at(battle.battleID).get();
|
||||
auto * finishingBattle = finishingBattles.at(battle.battleID).get();
|
||||
|
||||
CasualtiesAfterBattle cab1(battleInfo->sides.at(0), battleInfo), cab2(battleInfo->sides.at(1), battleInfo); //calculate casualties before deleting battle
|
||||
const EBattleResult result = battleResult->result;
|
||||
|
||||
CasualtiesAfterBattle cab1(battle.sides.at(0), &battle), cab2(battle.sides.at(1), &battle); //calculate casualties before deleting battle
|
||||
ChangeSpells cs; //for Eagle Eye
|
||||
|
||||
if(!finishingBattle->isDraw() && finishingBattle->winnerHero)
|
||||
@ -289,7 +297,7 @@ void BattleResultProcessor::endBattleConfirm(const BattleInfo * battleInfo)
|
||||
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 : battleInfo->sides.at(!battleResult->winner).usedSpellsHistory)
|
||||
for(auto & spellId : battle.sides.at(!battleResult->winner).usedSpellsHistory)
|
||||
{
|
||||
auto spell = spellId.toSpell(VLC->spells());
|
||||
if(spell && spell->getLevel() <= eagleEyeLevel && !finishingBattle->winnerHero->spellbookContainsSpell(spell->getId()) && gameHandler->getRandomGenerator().nextInt(99) < eagleEyeChance)
|
||||
@ -357,7 +365,7 @@ void BattleResultProcessor::endBattleConfirm(const BattleInfo * battleInfo)
|
||||
}
|
||||
}
|
||||
}
|
||||
for (auto armySlot : battleInfo->sides.at(!battleResult->winner).armyObject->stacks)
|
||||
for (auto armySlot : battle.sides.at(!battleResult->winner).armyObject->stacks)
|
||||
{
|
||||
auto artifactsWorn = armySlot.second->artifactsWorn;
|
||||
for (auto artSlot : artifactsWorn)
|
||||
@ -458,10 +466,10 @@ void BattleResultProcessor::endBattleConfirm(const BattleInfo * battleInfo)
|
||||
gameHandler->changePrimSkill(finishingBattle->winnerHero, PrimarySkill::EXPERIENCE, battleResult->exp[finishingBattle->winnerSide]);
|
||||
|
||||
BattleResultAccepted raccepted;
|
||||
raccepted.heroResult[0].army = const_cast<CArmedInstance*>(battleInfo->sides.at(0).armyObject);
|
||||
raccepted.heroResult[1].army = const_cast<CArmedInstance*>(battleInfo->sides.at(1).armyObject);
|
||||
raccepted.heroResult[0].hero = const_cast<CGHeroInstance*>(battleInfo->sides.at(0).hero);
|
||||
raccepted.heroResult[1].hero = const_cast<CGHeroInstance*>(battleInfo->sides.at(1).hero);
|
||||
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].exp = battleResult->exp[0];
|
||||
raccepted.heroResult[1].exp = battleResult->exp[1];
|
||||
raccepted.winnerSide = finishingBattle->winnerSide;
|
||||
@ -471,13 +479,16 @@ void BattleResultProcessor::endBattleConfirm(const BattleInfo * battleInfo)
|
||||
//--> continuation (battleAfterLevelUp) occurs after level-up gameHandler->queries are handled or on removing query
|
||||
}
|
||||
|
||||
void BattleResultProcessor::battleAfterLevelUp(const BattleResult &result)
|
||||
void BattleResultProcessor::battleAfterLevelUp(const BattleInfo & battle, const BattleResult & result)
|
||||
{
|
||||
LOG_TRACE(logGlobal);
|
||||
|
||||
if(!finishingBattle)
|
||||
assert(finishingBattles.count(battle.battleID) != 0);
|
||||
if(finishingBattles.count(battle.battleID) == 0)
|
||||
return;
|
||||
|
||||
auto & finishingBattle = finishingBattles[battle.battleID];
|
||||
|
||||
finishingBattle->remainingBattleQueriesCount--;
|
||||
logGlobal->trace("Decremented gameHandler->queries count to %d", finishingBattle->remainingBattleQueriesCount);
|
||||
|
||||
@ -490,7 +501,7 @@ void BattleResultProcessor::battleAfterLevelUp(const BattleResult &result)
|
||||
// Still, it looks like a hole.
|
||||
|
||||
// Necromancy if applicable.
|
||||
const CStackBasicDescriptor raisedStack = finishingBattle->winnerHero ? finishingBattle->winnerHero->calculateNecromancy(*battleResult) : CStackBasicDescriptor();
|
||||
const CStackBasicDescriptor raisedStack = finishingBattle->winnerHero ? finishingBattle->winnerHero->calculateNecromancy(result) : CStackBasicDescriptor();
|
||||
// Give raised units to winner and show dialog, if any were raised,
|
||||
// units will be given after casualties are taken
|
||||
const SlotID necroSlot = raisedStack.type ? finishingBattle->winnerHero->getSlotFor(raisedStack.type) : SlotID();
|
||||
@ -528,25 +539,23 @@ void BattleResultProcessor::battleAfterLevelUp(const BattleResult &result)
|
||||
gameHandler->heroPool->onHeroEscaped(finishingBattle->victor, finishingBattle->winnerHero);
|
||||
}
|
||||
|
||||
finishingBattle.reset();
|
||||
battleResult.reset();
|
||||
finishingBattles.erase(battle.battleID);
|
||||
battleResults.erase(battle.battleID);
|
||||
}
|
||||
|
||||
void BattleResultProcessor::setBattleResult(EBattleResult resultType, int victoriusSide)
|
||||
void BattleResultProcessor::setBattleResult(const BattleInfo & battle, EBattleResult resultType, int victoriusSide)
|
||||
{
|
||||
battleResult = std::make_unique<BattleResult>();
|
||||
assert(battleResults.count(battle.battleID) == 0);
|
||||
|
||||
battleResults[battle.battleID] = std::make_unique<BattleResult>();
|
||||
|
||||
auto & battleResult = battleResults[battle.battleID];
|
||||
battleResult->result = resultType;
|
||||
battleResult->winner = victoriusSide; //surrendering side loses
|
||||
gameHandler->gameState()->curB->calculateCasualties(battleResult->casualties);
|
||||
battle.calculateCasualties(battleResult->casualties);
|
||||
}
|
||||
|
||||
void BattleResultProcessor::setupBattle()
|
||||
bool BattleResultProcessor::battleIsEnding(const BattleInfo & battle) const
|
||||
{
|
||||
finishingBattle.reset();
|
||||
battleResult.reset();
|
||||
}
|
||||
|
||||
bool BattleResultProcessor::battleIsEnding() const
|
||||
{
|
||||
return battleResult != nullptr;
|
||||
return battleResults.count(battle.battleID) != 0;
|
||||
}
|
||||
|
@ -64,18 +64,17 @@ class BattleResultProcessor : boost::noncopyable
|
||||
// BattleProcessor * owner;
|
||||
CGameHandler * gameHandler;
|
||||
|
||||
std::unique_ptr<BattleResult> battleResult;
|
||||
std::unique_ptr<FinishingBattleHelper> finishingBattle;
|
||||
std::map<BattleID, std::unique_ptr<BattleResult>> battleResults;
|
||||
std::map<BattleID, std::unique_ptr<FinishingBattleHelper>> finishingBattles;
|
||||
|
||||
public:
|
||||
explicit BattleResultProcessor(BattleProcessor * owner);
|
||||
void setGameHandler(CGameHandler * newGameHandler);
|
||||
|
||||
bool battleIsEnding() const;
|
||||
bool battleIsEnding(const BattleInfo & battle) const;
|
||||
|
||||
void setupBattle();
|
||||
void setBattleResult(EBattleResult resultType, int victoriusSide);
|
||||
void endBattle(int3 tile, const CGHeroInstance * hero1, const CGHeroInstance * hero2); //ends battle
|
||||
void endBattleConfirm(const BattleInfo * battleInfo);
|
||||
void battleAfterLevelUp(const BattleResult & result);
|
||||
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);
|
||||
};
|
||||
|
@ -49,7 +49,7 @@ bool CBattleQuery::blocksPack(const CPack * pack) const
|
||||
void CBattleQuery::onRemoval(PlayerColor color)
|
||||
{
|
||||
if(result)
|
||||
gh->battles->battleAfterLevelUp(*result);
|
||||
gh->battles->battleAfterLevelUp(bi->battleID, *result);
|
||||
}
|
||||
|
||||
CBattleDialogQuery::CBattleDialogQuery(CGameHandler * owner, const BattleInfo * Bi):
|
||||
@ -70,6 +70,6 @@ void CBattleDialogQuery::onRemoval(PlayerColor color)
|
||||
}
|
||||
else
|
||||
{
|
||||
gh->battles->endBattleConfirm(bi);
|
||||
gh->battles->endBattleConfirm(bi->battleID);
|
||||
}
|
||||
}
|
||||
|
@ -204,9 +204,9 @@ public:
|
||||
|
||||
BattleStart bs;
|
||||
bs.info = battle;
|
||||
ASSERT_EQ(gameState->curB, nullptr);
|
||||
ASSERT_EQ(gameState->currentBattles.size(), 0);
|
||||
gameCallback->sendAndApply(&bs);
|
||||
ASSERT_EQ(gameState->curB, battle);
|
||||
ASSERT_EQ(gameState->currentBattles.size(), 1);
|
||||
}
|
||||
|
||||
std::shared_ptr<CGameState> gameState;
|
||||
@ -247,11 +247,11 @@ TEST_F(CGameStateTest, issue2765)
|
||||
|
||||
{
|
||||
battle::UnitInfo info;
|
||||
info.id = gameState->curB->battleNextUnitId();
|
||||
info.id = gameState->currentBattles.front()->battleNextUnitId();
|
||||
info.count = 1;
|
||||
info.type = CreatureID(69);
|
||||
info.side = BattleSide::ATTACKER;
|
||||
info.position = gameState->curB->getAvaliableHex(info.type, info.side);
|
||||
info.position = gameState->currentBattles.front()->getAvaliableHex(info.type, info.side);
|
||||
info.summoned = false;
|
||||
|
||||
BattleUnitsChanged pack;
|
||||
@ -263,7 +263,7 @@ TEST_F(CGameStateTest, issue2765)
|
||||
const CStack * att = nullptr;
|
||||
const CStack * def = nullptr;
|
||||
|
||||
for(const CStack * s : gameState->curB->stacks)
|
||||
for(const CStack * s : gameState->currentBattles.front()->stacks)
|
||||
{
|
||||
if(s->unitType()->getId() == CreatureID::BALLISTA && s->unitSide() == BattleSide::DEFENDER)
|
||||
def = s;
|
||||
@ -292,7 +292,7 @@ TEST_F(CGameStateTest, issue2765)
|
||||
spells::AbilityCaster caster(att, 3);
|
||||
|
||||
//here tested ballista, but this applied to all war machines
|
||||
spells::BattleCast cast(gameState->curB, &caster, spells::Mode::PASSIVE, age);
|
||||
spells::BattleCast cast(gameState->currentBattles.front().get(), &caster, spells::Mode::PASSIVE, age);
|
||||
|
||||
spells::Target target;
|
||||
target.emplace_back(def);
|
||||
@ -339,7 +339,7 @@ TEST_F(CGameStateTest, battleResurrection)
|
||||
|
||||
startTestBattle(attacker, defender);
|
||||
|
||||
uint32_t unitId = gameState->curB->battleNextUnitId();
|
||||
uint32_t unitId = gameState->currentBattles.front()->battleNextUnitId();
|
||||
|
||||
{
|
||||
battle::UnitInfo info;
|
||||
@ -347,7 +347,7 @@ TEST_F(CGameStateTest, battleResurrection)
|
||||
info.count = 10;
|
||||
info.type = CreatureID(13);
|
||||
info.side = BattleSide::ATTACKER;
|
||||
info.position = gameState->curB->getAvaliableHex(info.type, info.side);
|
||||
info.position = gameState->currentBattles.front()->getAvaliableHex(info.type, info.side);
|
||||
info.summoned = false;
|
||||
|
||||
BattleUnitsChanged pack;
|
||||
@ -358,11 +358,11 @@ TEST_F(CGameStateTest, battleResurrection)
|
||||
|
||||
{
|
||||
battle::UnitInfo info;
|
||||
info.id = gameState->curB->battleNextUnitId();
|
||||
info.id = gameState->currentBattles.front()->battleNextUnitId();
|
||||
info.count = 10;
|
||||
info.type = CreatureID(13);
|
||||
info.side = BattleSide::DEFENDER;
|
||||
info.position = gameState->curB->getAvaliableHex(info.type, info.side);
|
||||
info.position = gameState->currentBattles.front()->getAvaliableHex(info.type, info.side);
|
||||
info.summoned = false;
|
||||
|
||||
BattleUnitsChanged pack;
|
||||
@ -371,7 +371,7 @@ TEST_F(CGameStateTest, battleResurrection)
|
||||
gameCallback->sendAndApply(&pack);
|
||||
}
|
||||
|
||||
CStack * unit = gameState->curB->getStack(unitId);
|
||||
CStack * unit = gameState->currentBattles.front()->getStack(unitId);
|
||||
|
||||
ASSERT_NE(unit, nullptr);
|
||||
|
||||
@ -390,7 +390,7 @@ TEST_F(CGameStateTest, battleResurrection)
|
||||
const CSpell * spell = SpellID(SpellID::RESURRECTION).toSpell();
|
||||
ASSERT_NE(spell, nullptr);
|
||||
|
||||
spells::BattleCast cast(gameState->curB, attacker, spells::Mode::HERO, spell);
|
||||
spells::BattleCast cast(gameState->currentBattles.front().get(), attacker, spells::Mode::HERO, spell);
|
||||
|
||||
spells::Target target;
|
||||
target.emplace_back(unit);
|
||||
|
Loading…
Reference in New Issue
Block a user