1
0
mirror of https://github.com/vcmi/vcmi.git synced 2025-05-23 22:40:07 +02:00

Fix inability of unit to cast spell after receiving morale

This commit is contained in:
Ivan Savenko 2025-04-28 19:34:36 +03:00
parent 5433b07e5f
commit 5550edeb9a
16 changed files with 62 additions and 26 deletions

@ -568,7 +568,7 @@ bool BattleEvaluator::attemptCastingSpell(const CStack * activeStack)
ourTurnSpan++;
}
state->nextTurn(unit->unitId());
state->nextTurn(unit->unitId(), BattleUnitTurnReason::TURN_QUEUE);
PotentialTargets potentialTargets(unit, damageCache, state);

@ -342,14 +342,14 @@ void HypotheticBattle::nextRound()
}
}
void HypotheticBattle::nextTurn(uint32_t unitId)
void HypotheticBattle::nextTurn(uint32_t unitId, BattleUnitTurnReason reason)
{
activeUnitId = unitId;
auto unit = getForUpdate(unitId);
unit->removeUnitBonus(Bonus::UntilGetsTurn);
unit->afterGetsTurn();
unit->afterGetsTurn(reason);
}
void HypotheticBattle::addUnit(uint32_t id, const JsonNode & data)

@ -137,7 +137,7 @@ public:
battle::Units getUnitsIf(const battle::UnitFilter & predicate) const override;
void nextRound() override;
void nextTurn(uint32_t unitId) override;
void nextTurn(uint32_t unitId, BattleUnitTurnReason reason) override;
void addUnit(uint32_t id, const JsonNode & data) override;
void setUnitState(uint32_t id, const JsonNode & data, int64_t healthDelta) override;

@ -769,7 +769,7 @@ void ApplyClientNetPackVisitor::visitBattleNextRound(BattleNextRound & pack)
void ApplyClientNetPackVisitor::visitBattleSetActiveStack(BattleSetActiveStack & pack)
{
if(!pack.askPlayerInterface)
if(pack.reason == BattleUnitTurnReason::AUTOMATIC_ACTION)
return;
const CStack *activated = gs.getBattle(pack.battleID)->battleGetStackByID(pack.stack);

@ -418,6 +418,7 @@ set(lib_MAIN_HEADERS
battle/BattleSide.h
battle/BattleStateInfoForRetreat.h
battle/BattleProxy.h
battle/BattleUnitTurnReason.h
battle/CBattleInfoCallback.h
battle/CBattleInfoEssentials.h
battle/CObstacleInstance.h

@ -666,7 +666,7 @@ void BattleInfo::nextRound()
obst->battleTurnPassed();
}
void BattleInfo::nextTurn(uint32_t unitId)
void BattleInfo::nextTurn(uint32_t unitId, BattleUnitTurnReason reason)
{
activeStack = unitId;
@ -675,7 +675,7 @@ void BattleInfo::nextTurn(uint32_t unitId)
//remove bonuses that last until when stack gets new turn
st->removeBonusesRecursive(Bonus::UntilGetsTurn);
st->afterGetsTurn();
st->afterGetsTurn(reason);
}
void BattleInfo::addUnit(uint32_t id, const JsonNode & data)

@ -128,7 +128,7 @@ public:
// IBattleState
void nextRound() override;
void nextTurn(uint32_t unitId) override;
void nextTurn(uint32_t unitId, BattleUnitTurnReason reason) override;
void addUnit(uint32_t id, const JsonNode & data) override;
void moveUnit(uint32_t id, const BattleHex & destination) override;

@ -0,0 +1,28 @@
/*
* BattleUnitTurnReason.h, part of VCMI engine
*
* Authors: listed in file AUTHORS in main folder
*
* License: GNU General Public License v2.0 or later
* Full text of license available in license.txt file, in main folder
*
*/
#pragma once
VCMI_LIB_NAMESPACE_BEGIN
enum class BattleUnitTurnReason : int8_t
{
/// Unit gained turn due to becoming first unit in turn queue
TURN_QUEUE,
/// Unit gained turn due to morale triggering
MORALE,
/// Unit (re)gained turn due to hero casting a spell while this unit is active
HERO_SPELLCAST,
/// Unit gained turn due to casting a spell while having ability to cast spells without spending turn
UNIT_SPELLCAST,
/// Unit gained turn for automatic action, player can not select action for this unit
AUTOMATIC_ACTION
};
VCMI_LIB_NAMESPACE_END

@ -920,11 +920,13 @@ void CUnitState::afterNewRound()
makeGhost();
}
void CUnitState::afterGetsTurn()
void CUnitState::afterGetsTurn(BattleUnitTurnReason reason)
{
//if moving second time this round it must be high morale bonus
if(movedThisRound)
if(reason == BattleUnitTurnReason::MORALE)
{
hadMorale = true;
castSpellThisTurn = false;
}
}
void CUnitState::makeGhost()

@ -10,6 +10,7 @@
#pragma once
#include "BattleUnitTurnReason.h"
#include "Unit.h"
#include "../bonuses/BonusCache.h"
@ -254,7 +255,7 @@ public:
void afterNewRound();
void afterGetsTurn();
void afterGetsTurn(BattleUnitTurnReason reason);
void makeGhost();

@ -10,6 +10,7 @@
#pragma once
#include "CBattleInfoEssentials.h"
#include "BattleUnitTurnReason.h"
VCMI_LIB_NAMESPACE_BEGIN
@ -80,7 +81,7 @@ class DLL_LINKAGE IBattleState : public IBattleInfo
{
public:
virtual void nextRound() = 0;
virtual void nextTurn(uint32_t unitId) = 0;
virtual void nextTurn(uint32_t unitId, BattleUnitTurnReason reason) = 0;
virtual void addUnit(uint32_t id, const JsonNode & data) = 0;
virtual void setUnitState(uint32_t id, const JsonNode & data, int64_t healthDelta) = 0;

@ -2007,7 +2007,7 @@ void BattleNextRound::applyGs(CGameState *gs)
void BattleSetActiveStack::applyGs(CGameState *gs)
{
gs->getBattle(battleID)->nextTurn(stack);
gs->getBattle(battleID)->nextTurn(stack, reason);
}
void BattleTriggerEffect::applyGs(CGameState *gs)

@ -12,9 +12,10 @@
#include "NetPacksBase.h"
#include "BattleChanges.h"
#include "PacksForClient.h"
#include "../battle/BattleHexArray.h"
#include "../battle/BattleAction.h"
#include "../battle/BattleInfo.h"
#include "../battle/BattleHexArray.h"
#include "../battle/BattleUnitTurnReason.h"
#include "../texts/MetaString.h"
class CClient;
@ -63,8 +64,8 @@ struct DLL_LINKAGE BattleSetActiveStack : public CPackForClient
void applyGs(CGameState * gs) override;
BattleID battleID = BattleID::NONE;
ui32 stack = 0;
ui8 askPlayerInterface = true;
uint32_t stack = 0;
BattleUnitTurnReason reason;
void visitTyped(ICPackVisitor & visitor) override;
@ -72,7 +73,7 @@ struct DLL_LINKAGE BattleSetActiveStack : public CPackForClient
{
h & battleID;
h & stack;
h & askPlayerInterface;
h & reason;
assert(battleID != BattleID::NONE);
}
};

@ -334,7 +334,7 @@ void BattleFlowProcessor::activateNextStack(const CBattleInfoCallback & battle)
if (!tryMakeAutomaticAction(battle, next))
{
if(next->alive()) {
setActiveStack(battle, next);
setActiveStack(battle, next, BattleUnitTurnReason::TURN_QUEUE);
break;
}
}
@ -576,7 +576,7 @@ void BattleFlowProcessor::onActionMade(const CBattleInfoCallback & battle, const
// NOTE: in case of random spellcaster, (e.g. Master Genie) spell has been selected by server and was not present in action received from player
if(actedStack->castSpellThisTurn && ba.spell.hasValue() && ba.spell.toSpell()->canCastWithoutSkip())
{
setActiveStack(battle, actedStack);
setActiveStack(battle, actedStack, BattleUnitTurnReason::UNIT_SPELLCAST);
return;
}
}
@ -589,7 +589,7 @@ void BattleFlowProcessor::onActionMade(const CBattleInfoCallback & battle, const
if (rollGoodMorale(battle, actedStack))
{
// Good morale - same stack makes 2nd turn
setActiveStack(battle, actedStack);
setActiveStack(battle, actedStack, BattleUnitTurnReason::MORALE);
return;
}
}
@ -599,7 +599,7 @@ void BattleFlowProcessor::onActionMade(const CBattleInfoCallback & battle, const
{
// 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(battle, activeStack);
setActiveStack(battle, activeStack, BattleUnitTurnReason::HERO_SPELLCAST);
return;
}
}
@ -622,7 +622,7 @@ bool BattleFlowProcessor::makeAutomaticAction(const CBattleInfoCallback & battle
BattleSetActiveStack bsa;
bsa.battleID = battle.getBattle()->getBattleID();
bsa.stack = stack->unitId();
bsa.askPlayerInterface = false;
bsa.reason = BattleUnitTurnReason::AUTOMATIC_ACTION;
gameHandler->sendAndApply(bsa);
bool ret = owner->makeAutomaticBattleAction(battle, ba);
@ -809,12 +809,13 @@ void BattleFlowProcessor::stackTurnTrigger(const CBattleInfoCallback & battle, c
}
}
void BattleFlowProcessor::setActiveStack(const CBattleInfoCallback & battle, const battle::Unit * stack)
void BattleFlowProcessor::setActiveStack(const CBattleInfoCallback & battle, const battle::Unit * stack, BattleUnitTurnReason reason)
{
assert(stack);
BattleSetActiveStack sas;
sas.battleID = battle.getBattle()->getBattleID();
sas.stack = stack->unitId();
sas.reason = reason;
gameHandler->sendAndApply(sas);
}

@ -10,6 +10,7 @@
#pragma once
#include "../lib/battle/BattleSide.h"
#include "../lib/battle/BattleUnitTurnReason.h"
VCMI_LIB_NAMESPACE_BEGIN
class CStack;
@ -48,7 +49,7 @@ class BattleFlowProcessor : boost::noncopyable
void stackEnchantedTrigger(const CBattleInfoCallback & battle, const CStack * stack);
void removeObstacle(const CBattleInfoCallback & battle, const CObstacleInstance & obstacle);
void stackTurnTrigger(const CBattleInfoCallback & battle, const CStack * stack);
void setActiveStack(const CBattleInfoCallback & battle, const battle::Unit * stack);
void setActiveStack(const CBattleInfoCallback & battle, const battle::Unit * stack, BattleUnitTurnReason reason);
void makeStackDoNothing(const CBattleInfoCallback & battle, const CStack * next);
bool makeAutomaticAction(const CBattleInfoCallback & battle, const CStack * stack, BattleAction & ba); //used when action is taken by stack without volition of player (eg. unguided catapult attack)

@ -42,7 +42,7 @@ public:
MOCK_CONST_METHOD1(getUsedSpells, std::vector<SpellID>(BattleSide));
MOCK_METHOD0(nextRound, void());
MOCK_METHOD1(nextTurn, void(uint32_t));
MOCK_METHOD2(nextTurn, void(uint32_t, BattleUnitTurnReason));
MOCK_METHOD2(addUnit, void(uint32_t, const JsonNode &));
MOCK_METHOD3(setUnitState, void(uint32_t, const JsonNode &, int64_t));
MOCK_METHOD2(moveUnit, void(uint32_t, const BattleHex &));