mirror of
https://github.com/vcmi/vcmi.git
synced 2025-07-05 00:49:09 +02:00
Fixed battle replay
This commit is contained in:
@ -1530,6 +1530,19 @@ struct DLL_LINKAGE BattleSetActiveStack : public CPackForClient
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct DLL_LINKAGE BattleCancelled: public CPackForClient
|
||||||
|
{
|
||||||
|
void applyGs(CGameState * gs) const;
|
||||||
|
|
||||||
|
BattleID battleID = BattleID::NONE;
|
||||||
|
|
||||||
|
template <typename Handler> void serialize(Handler & h, const int version)
|
||||||
|
{
|
||||||
|
h & battleID;
|
||||||
|
assert(battleID != BattleID::NONE);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
struct DLL_LINKAGE BattleResultAccepted : public CPackForClient
|
struct DLL_LINKAGE BattleResultAccepted : public CPackForClient
|
||||||
{
|
{
|
||||||
void applyGs(CGameState * gs) const;
|
void applyGs(CGameState * gs) const;
|
||||||
|
@ -2119,7 +2119,7 @@ void BattleStart::applyGs(CGameState * gs) const
|
|||||||
info->battleID = gs->nextBattleID;
|
info->battleID = gs->nextBattleID;
|
||||||
info->localInit();
|
info->localInit();
|
||||||
|
|
||||||
vstd::next(gs->nextBattleID, 1);
|
gs->nextBattleID = vstd::next(gs->nextBattleID, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
void BattleNextRound::applyGs(CGameState * gs) const
|
void BattleNextRound::applyGs(CGameState * gs) const
|
||||||
@ -2177,6 +2177,17 @@ void BattleUpdateGateState::applyGs(CGameState * gs) const
|
|||||||
gs->getBattle(battleID)->si.gateState = state;
|
gs->getBattle(battleID)->si.gateState = state;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void BattleCancelled::applyGs(CGameState * gs) const
|
||||||
|
{
|
||||||
|
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 BattleResultAccepted::applyGs(CGameState * gs) const
|
void BattleResultAccepted::applyGs(CGameState * gs) const
|
||||||
{
|
{
|
||||||
// Remove any "until next battle" bonuses
|
// Remove any "until next battle" bonuses
|
||||||
|
@ -286,6 +286,7 @@ void registerTypesClientPacks2(Serializer &s)
|
|||||||
s.template registerType<CPackForClient, BattleSetActiveStack>();
|
s.template registerType<CPackForClient, BattleSetActiveStack>();
|
||||||
s.template registerType<CPackForClient, BattleResult>();
|
s.template registerType<CPackForClient, BattleResult>();
|
||||||
s.template registerType<CPackForClient, BattleResultAccepted>();
|
s.template registerType<CPackForClient, BattleResultAccepted>();
|
||||||
|
s.template registerType<CPackForClient, BattleCancelled>();
|
||||||
s.template registerType<CPackForClient, BattleLogMessage>();
|
s.template registerType<CPackForClient, BattleLogMessage>();
|
||||||
s.template registerType<CPackForClient, BattleStackMoved>();
|
s.template registerType<CPackForClient, BattleStackMoved>();
|
||||||
s.template registerType<CPackForClient, BattleAttack>();
|
s.template registerType<CPackForClient, BattleAttack>();
|
||||||
|
@ -137,6 +137,7 @@ void Catapult::applyTargeted(ServerCallback * server, const Mechanics * m, const
|
|||||||
attack.damageDealt = getRandomDamage(server);
|
attack.damageDealt = getRandomDamage(server);
|
||||||
|
|
||||||
CatapultAttack ca; //package for clients
|
CatapultAttack ca; //package for clients
|
||||||
|
ca.battleID = m->battle()->getBattle()->getBattleID();
|
||||||
ca.attacker = m->caster->getHeroCaster() ? -1 : m->caster->getCasterUnitId();
|
ca.attacker = m->caster->getHeroCaster() ? -1 : m->caster->getCasterUnitId();
|
||||||
ca.attackedParts.push_back(attack);
|
ca.attackedParts.push_back(attack);
|
||||||
server->apply(&ca);
|
server->apply(&ca);
|
||||||
|
@ -254,12 +254,15 @@ void TurnTimerHandler::onBattleLoop(const BattleID & battleID, int waitTime)
|
|||||||
std::lock_guard<std::recursive_mutex> guard(mx);
|
std::lock_guard<std::recursive_mutex> guard(mx);
|
||||||
const auto * gs = gameHandler.gameState();
|
const auto * gs = gameHandler.gameState();
|
||||||
const auto * si = gameHandler.getStartInfo();
|
const auto * si = gameHandler.getStartInfo();
|
||||||
if(!si || !gs || !si->turnTimerInfo.isBattleEnabled())
|
if(!si || !gs)
|
||||||
{
|
{
|
||||||
assert(0);
|
assert(0);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!si->turnTimerInfo.isBattleEnabled())
|
||||||
|
return;
|
||||||
|
|
||||||
ui8 side = 0;
|
ui8 side = 0;
|
||||||
const CStack * stack = nullptr;
|
const CStack * stack = nullptr;
|
||||||
bool isTactisPhase = gs->getBattle(battleID)->battleTacticDist() > 0;
|
bool isTactisPhase = gs->getBattle(battleID)->battleTacticDist() > 0;
|
||||||
@ -279,6 +282,7 @@ void TurnTimerHandler::onBattleLoop(const BattleID & battleID, int waitTime)
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
const auto * state = gameHandler.getPlayerState(player);
|
const auto * state = gameHandler.getPlayerState(player);
|
||||||
|
assert(state && state->status != EPlayerStatus::INGAME);
|
||||||
if(!state || state->status != EPlayerStatus::INGAME || !state->human)
|
if(!state || state->status != EPlayerStatus::INGAME || !state->human)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
@ -904,7 +904,7 @@ void BattleActionProcessor::makeAttack(const CBattleInfoCallback & battle, const
|
|||||||
bat.flags |= BattleAttack::DEATH_BLOW;
|
bat.flags |= BattleAttack::DEATH_BLOW;
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto * owner = battle.battleGetFightingHero(attacker->unitOwner());
|
const auto * owner = battle.battleGetFightingHero(attacker->unitSide());
|
||||||
if(owner)
|
if(owner)
|
||||||
{
|
{
|
||||||
int chance = owner->valOfBonuses(BonusType::BONUS_DAMAGE_CHANCE, attacker->creatureIndex());
|
int chance = owner->valOfBonuses(BonusType::BONUS_DAMAGE_CHANCE, attacker->creatureIndex());
|
||||||
|
@ -51,33 +51,23 @@ void BattleProcessor::engageIntoBattle(PlayerColor player)
|
|||||||
gameHandler->sendAndApply(&pb);
|
gameHandler->sendAndApply(&pb);
|
||||||
}
|
}
|
||||||
|
|
||||||
void BattleProcessor::startBattlePrimary(const CArmedInstance *army1, const CArmedInstance *army2, int3 tile,
|
void BattleProcessor::restartBattlePrimary(const BattleID & battleID, const CArmedInstance *army1, const CArmedInstance *army2, int3 tile,
|
||||||
const CGHeroInstance *hero1, const CGHeroInstance *hero2, bool creatureBank,
|
const CGHeroInstance *hero1, const CGHeroInstance *hero2, bool creatureBank,
|
||||||
const CGTownInstance *town) //use hero=nullptr for no hero
|
const CGTownInstance *town)
|
||||||
{
|
{
|
||||||
assert(gameHandler->gameState()->getBattle(army1->getOwner()) == nullptr);
|
auto battle = gameHandler->gameState()->getBattle(battleID);
|
||||||
assert(gameHandler->gameState()->getBattle(army2->getOwner()) == nullptr);
|
|
||||||
|
|
||||||
engageIntoBattle(army1->tempOwner);
|
|
||||||
engageIntoBattle(army2->tempOwner);
|
|
||||||
|
|
||||||
static const CArmedInstance *armies[2];
|
|
||||||
armies[0] = army1;
|
|
||||||
armies[1] = army2;
|
|
||||||
static const CGHeroInstance*heroes[2];
|
|
||||||
heroes[0] = hero1;
|
|
||||||
heroes[1] = hero2;
|
|
||||||
|
|
||||||
auto battleID = setupBattle(tile, armies, heroes, creatureBank, town); //initializes stacks, places creatures on battlefield, blocks and informs player interfaces
|
|
||||||
|
|
||||||
const auto * battle = gameHandler->gameState()->getBattle(battleID);
|
|
||||||
assert(battle);
|
|
||||||
|
|
||||||
auto lastBattleQuery = std::dynamic_pointer_cast<CBattleQuery>(gameHandler->queries->topQuery(battle->sides[0].color));
|
auto lastBattleQuery = std::dynamic_pointer_cast<CBattleQuery>(gameHandler->queries->topQuery(battle->sides[0].color));
|
||||||
|
|
||||||
|
assert(lastBattleQuery);
|
||||||
|
|
||||||
//existing battle query for retying auto-combat
|
//existing battle query for retying auto-combat
|
||||||
if(lastBattleQuery)
|
if(lastBattleQuery)
|
||||||
{
|
{
|
||||||
|
const CGHeroInstance*heroes[2];
|
||||||
|
heroes[0] = hero1;
|
||||||
|
heroes[1] = hero2;
|
||||||
|
|
||||||
for(int i : {0, 1})
|
for(int i : {0, 1})
|
||||||
{
|
{
|
||||||
if(heroes[i])
|
if(heroes[i])
|
||||||
@ -89,21 +79,58 @@ void BattleProcessor::startBattlePrimary(const CArmedInstance *army1, const CArm
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
lastBattleQuery->battleID = battle->getBattleID();
|
|
||||||
lastBattleQuery->result = std::nullopt;
|
lastBattleQuery->result = std::nullopt;
|
||||||
lastBattleQuery->belligerents[0] = battle->sides[0].armyObject;
|
|
||||||
lastBattleQuery->belligerents[1] = battle->sides[1].armyObject;
|
assert(lastBattleQuery->belligerents[0] == battle->sides[0].armyObject);
|
||||||
|
assert(lastBattleQuery->belligerents[1] == battle->sides[1].armyObject);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto nextBattleQuery = std::make_shared<CBattleQuery>(gameHandler, battle);
|
BattleCancelled bc;
|
||||||
|
bc.battleID = battleID;
|
||||||
|
gameHandler->sendAndApply(&bc);
|
||||||
|
|
||||||
|
startBattlePrimary(army1, army2, tile, hero1, hero2, creatureBank, town);
|
||||||
|
}
|
||||||
|
|
||||||
|
void BattleProcessor::startBattlePrimary(const CArmedInstance *army1, const CArmedInstance *army2, int3 tile,
|
||||||
|
const CGHeroInstance *hero1, const CGHeroInstance *hero2, bool creatureBank,
|
||||||
|
const CGTownInstance *town)
|
||||||
|
{
|
||||||
|
assert(gameHandler->gameState()->getBattle(army1->getOwner()) == nullptr);
|
||||||
|
assert(gameHandler->gameState()->getBattle(army2->getOwner()) == nullptr);
|
||||||
|
|
||||||
|
engageIntoBattle(army1->tempOwner);
|
||||||
|
engageIntoBattle(army2->tempOwner);
|
||||||
|
|
||||||
|
const CArmedInstance *armies[2];
|
||||||
|
armies[0] = army1;
|
||||||
|
armies[1] = army2;
|
||||||
|
const CGHeroInstance*heroes[2];
|
||||||
|
heroes[0] = hero1;
|
||||||
|
heroes[1] = hero2;
|
||||||
|
|
||||||
|
auto battleID = setupBattle(tile, armies, heroes, creatureBank, town); //initializes stacks, places creatures on battlefield, blocks and informs player interfaces
|
||||||
|
|
||||||
|
const auto * battle = gameHandler->gameState()->getBattle(battleID);
|
||||||
|
assert(battle);
|
||||||
|
|
||||||
|
auto lastBattleQuery = std::dynamic_pointer_cast<CBattleQuery>(gameHandler->queries->topQuery(battle->sides[0].color));
|
||||||
|
|
||||||
|
if (lastBattleQuery)
|
||||||
|
{
|
||||||
|
lastBattleQuery->battleID = battleID;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
auto newBattleQuery = std::make_shared<CBattleQuery>(gameHandler, battle);
|
||||||
|
|
||||||
|
// store initial mana to reset if battle has been restarted
|
||||||
for(int i : {0, 1})
|
for(int i : {0, 1})
|
||||||
{
|
|
||||||
if(heroes[i])
|
if(heroes[i])
|
||||||
{
|
newBattleQuery->initialHeroMana[i] = heroes[i]->mana;
|
||||||
nextBattleQuery->initialHeroMana[i] = heroes[i]->mana;
|
|
||||||
|
gameHandler->queries->addQuery(newBattleQuery);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
gameHandler->queries->addQuery(nextBattleQuery);
|
|
||||||
|
|
||||||
flowProcessor->onBattleStarted(*battle);
|
flowProcessor->onBattleStarted(*battle);
|
||||||
}
|
}
|
||||||
|
@ -63,6 +63,8 @@ public:
|
|||||||
void startBattleI(const CArmedInstance *army1, const CArmedInstance *army2, int3 tile, bool creatureBank = false);
|
void startBattleI(const CArmedInstance *army1, const CArmedInstance *army2, int3 tile, bool creatureBank = false);
|
||||||
/// Starts battle between two armies (which can also be heroes) at position of 2nd object
|
/// Starts battle between two armies (which can also be heroes) at position of 2nd object
|
||||||
void startBattleI(const CArmedInstance *army1, const CArmedInstance *army2, bool creatureBank = false);
|
void startBattleI(const CArmedInstance *army1, const CArmedInstance *army2, bool creatureBank = false);
|
||||||
|
/// Restart ongoing battle and end previous battle
|
||||||
|
void restartBattlePrimary(const BattleID & battleID, const CArmedInstance *army1, const CArmedInstance *army2, int3 tile, const CGHeroInstance *hero1, const CGHeroInstance *hero2, bool creatureBank = false, const CGTownInstance *town = nullptr);
|
||||||
|
|
||||||
/// Processing of incoming battle action netpack
|
/// Processing of incoming battle action netpack
|
||||||
bool makePlayerBattleAction(const BattleID & battleID, PlayerColor player, const BattleAction & ba);
|
bool makePlayerBattleAction(const BattleID & battleID, PlayerColor player, const BattleAction & ba);
|
||||||
|
@ -278,11 +278,12 @@ void BattleResultProcessor::endBattle(const CBattleInfoCallback & battle)
|
|||||||
for(auto q : gameHandler->queries->allQueries())
|
for(auto q : gameHandler->queries->allQueries())
|
||||||
{
|
{
|
||||||
auto otherBattleQuery = std::dynamic_pointer_cast<CBattleQuery>(q);
|
auto otherBattleQuery = std::dynamic_pointer_cast<CBattleQuery>(q);
|
||||||
if(otherBattleQuery)
|
if(otherBattleQuery && otherBattleQuery->battleID == battle.getBattle()->getBattleID())
|
||||||
otherBattleQuery->result = battleQuery->result;
|
otherBattleQuery->result = battleQuery->result;
|
||||||
}
|
}
|
||||||
|
|
||||||
gameHandler->turnTimerHandler.onBattleEnd(battle.getBattle()->getBattleID());
|
gameHandler->turnTimerHandler.onBattleEnd(battle.getBattle()->getBattleID());
|
||||||
|
gameHandler->sendAndApply(battleResult);
|
||||||
|
|
||||||
if (battleResult->queryID == QueryID::NONE)
|
if (battleResult->queryID == QueryID::NONE)
|
||||||
endBattleConfirm(battle);
|
endBattleConfirm(battle);
|
||||||
|
@ -10,6 +10,7 @@
|
|||||||
#include "StdInc.h"
|
#include "StdInc.h"
|
||||||
#include "BattleQueries.h"
|
#include "BattleQueries.h"
|
||||||
#include "MapQueries.h"
|
#include "MapQueries.h"
|
||||||
|
#include "QueriesProcessor.h"
|
||||||
|
|
||||||
#include "../CGameHandler.h"
|
#include "../CGameHandler.h"
|
||||||
#include "../battles/BattleProcessor.h"
|
#include "../battles/BattleProcessor.h"
|
||||||
@ -18,6 +19,8 @@
|
|||||||
|
|
||||||
void CBattleQuery::notifyObjectAboutRemoval(const CObjectVisitQuery & objectVisit) const
|
void CBattleQuery::notifyObjectAboutRemoval(const CObjectVisitQuery & objectVisit) const
|
||||||
{
|
{
|
||||||
|
assert(result);
|
||||||
|
|
||||||
if(result)
|
if(result)
|
||||||
objectVisit.visitedObject->battleFinished(objectVisit.visitingHero, *result);
|
objectVisit.visitedObject->battleFinished(objectVisit.visitingHero, *result);
|
||||||
}
|
}
|
||||||
@ -47,10 +50,21 @@ bool CBattleQuery::blocksPack(const CPack * pack) const
|
|||||||
|
|
||||||
void CBattleQuery::onRemoval(PlayerColor color)
|
void CBattleQuery::onRemoval(PlayerColor color)
|
||||||
{
|
{
|
||||||
|
assert(result);
|
||||||
|
|
||||||
if(result)
|
if(result)
|
||||||
gh->battles->battleAfterLevelUp(battleID, *result);
|
gh->battles->battleAfterLevelUp(battleID, *result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CBattleQuery::onExposure(QueryPtr topQuery)
|
||||||
|
{
|
||||||
|
// this method may be called in two cases:
|
||||||
|
// 1) when requesting battle replay (but before replay starts -> no valid result)
|
||||||
|
// 2) when aswering on levelup queries after accepting battle result -> valid result
|
||||||
|
if(result)
|
||||||
|
owner->popQuery(*this);
|
||||||
|
}
|
||||||
|
|
||||||
CBattleDialogQuery::CBattleDialogQuery(CGameHandler * owner, const IBattleInfo * bi):
|
CBattleDialogQuery::CBattleDialogQuery(CGameHandler * owner, const IBattleInfo * bi):
|
||||||
CDialogQuery(owner),
|
CDialogQuery(owner),
|
||||||
bi(bi)
|
bi(bi)
|
||||||
@ -64,7 +78,8 @@ void CBattleDialogQuery::onRemoval(PlayerColor color)
|
|||||||
assert(answer);
|
assert(answer);
|
||||||
if(*answer == 1)
|
if(*answer == 1)
|
||||||
{
|
{
|
||||||
gh->startBattlePrimary(
|
gh->battles->restartBattlePrimary(
|
||||||
|
bi->getBattleID(),
|
||||||
bi->getSideArmy(0),
|
bi->getSideArmy(0),
|
||||||
bi->getSideArmy(1),
|
bi->getSideArmy(1),
|
||||||
bi->getLocation(),
|
bi->getLocation(),
|
||||||
|
@ -31,6 +31,7 @@ public:
|
|||||||
virtual void notifyObjectAboutRemoval(const CObjectVisitQuery &objectVisit) const override;
|
virtual void notifyObjectAboutRemoval(const CObjectVisitQuery &objectVisit) const override;
|
||||||
virtual bool blocksPack(const CPack *pack) const override;
|
virtual bool blocksPack(const CPack *pack) const override;
|
||||||
virtual void onRemoval(PlayerColor color) override;
|
virtual void onRemoval(PlayerColor color) override;
|
||||||
|
virtual void onExposure(QueryPtr topQuery) override;
|
||||||
};
|
};
|
||||||
|
|
||||||
class CBattleDialogQuery : public CDialogQuery
|
class CBattleDialogQuery : public CDialogQuery
|
||||||
|
Reference in New Issue
Block a user