From 3de891b4b4df47565c1a9c1ae45c54240e9e8f8b Mon Sep 17 00:00:00 2001 From: FeniksFire Date: Fri, 17 Mar 2017 16:48:44 +0100 Subject: [PATCH] Moving/dividing classes from BattleState to separate files. --- AI/BattleAI/AttackPossibility.h | 2 +- AI/BattleAI/EnemyInfo.h | 3 +- AI/BattleAI/StackWithBonuses.cpp | 3 +- AI/BattleAI/ThreatMap.h | 1 - AI/StupidAI/StupidAI.cpp | 2 +- CCallback.cpp | 1 - client/CMakeLists.txt | 2 +- client/CPlayerInterface.cpp | 2 +- client/Client.cpp | 2 +- client/NetPacksClient.cpp | 3 +- client/battle/CBattleAnimations.cpp | 2 +- client/battle/CBattleInterface.cpp | 2 +- client/battle/CBattleInterfaceClasses.cpp | 2 +- client/windows/CCreatureWindow.cpp | 2 +- client/windows/CSpellWindow.cpp | 2 +- client/windows/GUIClasses.cpp | 2 +- lib/BattleAction.cpp | 3 +- lib/{BattleState.cpp => BattleInfo.cpp} | 560 +--------------------- lib/BattleInfo.h | 106 ++++ lib/BattleState.h | 310 ------------ lib/CBattleCallback.cpp | 3 +- lib/CGameInfoCallback.cpp | 2 +- lib/CGameInterface.cpp | 2 +- lib/CGameState.cpp | 2 +- lib/CMakeLists.txt | 5 +- lib/CStack.cpp | 539 +++++++++++++++++++++ lib/CStack.h | 156 ++++++ lib/HeroBonus.cpp | 2 +- lib/NetPacksLib.cpp | 3 +- lib/SideInBattle.cpp | 31 ++ lib/SideInBattle.h | 35 ++ lib/SiegeInfo.cpp | 35 ++ lib/SiegeInfo.h | 28 ++ lib/mapObjects/CGHeroInstance.cpp | 2 +- lib/registerTypes/RegisterTypes.cpp | 1 - lib/registerTypes/TypesClientPacks1.cpp | 1 - lib/registerTypes/TypesClientPacks2.cpp | 3 +- lib/registerTypes/TypesMapObjects1.cpp | 1 - lib/registerTypes/TypesMapObjects2.cpp | 3 +- lib/registerTypes/TypesMapObjects3.cpp | 1 - lib/registerTypes/TypesPregamePacks.cpp | 1 - lib/registerTypes/TypesServerPacks.cpp | 1 - lib/spells/BattleSpellMechanics.cpp | 3 +- lib/spells/CDefaultSpellMechanics.cpp | 3 +- lib/spells/CSpellHandler.cpp | 3 +- lib/spells/CreatureSpellMechanics.cpp | 3 +- lib/spells/ISpellMechanics.cpp | 4 +- server/CGameHandler.cpp | 3 +- server/CQuery.cpp | 2 +- server/NetPacksServer.cpp | 3 +- 50 files changed, 985 insertions(+), 908 deletions(-) rename lib/{BattleState.cpp => BattleInfo.cpp} (59%) create mode 100644 lib/BattleInfo.h delete mode 100644 lib/BattleState.h create mode 100644 lib/CStack.cpp create mode 100644 lib/CStack.h create mode 100644 lib/SideInBattle.cpp create mode 100644 lib/SideInBattle.h create mode 100644 lib/SiegeInfo.cpp create mode 100644 lib/SiegeInfo.h diff --git a/AI/BattleAI/AttackPossibility.h b/AI/BattleAI/AttackPossibility.h index f370e87cc..ac1fb321c 100644 --- a/AI/BattleAI/AttackPossibility.h +++ b/AI/BattleAI/AttackPossibility.h @@ -8,7 +8,7 @@ * */ #pragma once -#include "../../lib/BattleState.h" +#include "../../lib/CStack.h" #include "../../CCallback.h" #include "common.h" diff --git a/AI/BattleAI/EnemyInfo.h b/AI/BattleAI/EnemyInfo.h index 52212f050..66e2bddbe 100644 --- a/AI/BattleAI/EnemyInfo.h +++ b/AI/BattleAI/EnemyInfo.h @@ -8,7 +8,8 @@ * */ #pragma once -#include "../../lib/BattleState.h" + +#include "../../lib/BattleHex.h" class CStack; diff --git a/AI/BattleAI/StackWithBonuses.cpp b/AI/BattleAI/StackWithBonuses.cpp index ad706eeab..1e4fdb617 100644 --- a/AI/BattleAI/StackWithBonuses.cpp +++ b/AI/BattleAI/StackWithBonuses.cpp @@ -9,7 +9,8 @@ */ #include "StdInc.h" #include "StackWithBonuses.h" -#include "../../lib/BattleState.h" +#include "../../lib/CStack.h" + const TBonusListPtr StackWithBonuses::getAllBonuses(const CSelector &selector, const CSelector &limit, const CBonusSystemNode *root /*= nullptr*/, const std::string &cachingStr /*= ""*/) const diff --git a/AI/BattleAI/ThreatMap.h b/AI/BattleAI/ThreatMap.h index bd5df698c..2d800a72c 100644 --- a/AI/BattleAI/ThreatMap.h +++ b/AI/BattleAI/ThreatMap.h @@ -11,7 +11,6 @@ #pragma once #include "common.h" -#include "../../lib/BattleState.h" #include "CCallback.h" /* class ThreatMap diff --git a/AI/StupidAI/StupidAI.cpp b/AI/StupidAI/StupidAI.cpp index f9171f625..9d5389671 100644 --- a/AI/StupidAI/StupidAI.cpp +++ b/AI/StupidAI/StupidAI.cpp @@ -1,7 +1,7 @@ #include "StdInc.h" #include "../../lib/AI_Base.h" #include "StupidAI.h" -#include "../../lib/BattleState.h" +#include "../../lib/CStack.h" #include "../../CCallback.h" #include "../../lib/CCreatureHandler.h" diff --git a/CCallback.cpp b/CCallback.cpp index 7f1257db7..762d230e4 100644 --- a/CCallback.cpp +++ b/CCallback.cpp @@ -4,7 +4,6 @@ #include "lib/CCreatureHandler.h" #include "client/CGameInfo.h" #include "lib/CGameState.h" -#include "lib/BattleState.h" #include "client/CPlayerInterface.h" #include "client/Client.h" #include "lib/mapping/CMap.h" diff --git a/client/CMakeLists.txt b/client/CMakeLists.txt index 45989848e..33dc6342c 100644 --- a/client/CMakeLists.txt +++ b/client/CMakeLists.txt @@ -62,7 +62,7 @@ set(client_SRCS set(client_HEADERS gui/SDL_Pixels.h - gui/SDL_Compat.h + gui/SDL_Compat.h ) if(APPLE) diff --git a/client/CPlayerInterface.cpp b/client/CPlayerInterface.cpp index 7b809ec40..a3805eec7 100644 --- a/client/CPlayerInterface.cpp +++ b/client/CPlayerInterface.cpp @@ -28,7 +28,7 @@ #include "../lib/spells/CSpellHandler.h" #include "../lib/CTownHandler.h" #include "../lib/mapObjects/CObjectClassesHandler.h" // For displaying correct UI when interacting with objects -#include "../lib/BattleState.h" +#include "../lib/CStack.h" #include "../lib/JsonNode.h" #include "CMusicHandler.h" #include "../lib/CondSh.h" diff --git a/client/Client.cpp b/client/Client.cpp index 4a6536fd8..6ef710661 100644 --- a/client/Client.cpp +++ b/client/Client.cpp @@ -11,7 +11,7 @@ #include "../lib/CGameState.h" #include "CPlayerInterface.h" #include "../lib/StartInfo.h" -#include "../lib/BattleState.h" +#include "../lib/BattleInfo.h" #include "../lib/CModHandler.h" #include "../lib/CArtHandler.h" #include "../lib/CGeneralTextHandler.h" diff --git a/client/NetPacksClient.cpp b/client/NetPacksClient.cpp index 766901b9f..fac4a627d 100644 --- a/client/NetPacksClient.cpp +++ b/client/NetPacksClient.cpp @@ -24,7 +24,8 @@ #include "battle/CBattleInterface.h" #include "../lib/mapping/CCampaignHandler.h" #include "../lib/CGameState.h" -#include "../lib/BattleState.h" +#include "../lib/CStack.h" +#include "../lib/BattleInfo.h" #include "../lib/GameConstants.h" #include "../lib/CPlayerState.h" #include "gui/CGuiHandler.h" diff --git a/client/battle/CBattleAnimations.cpp b/client/battle/CBattleAnimations.cpp index 53e0a48d0..16b79b1ff 100644 --- a/client/battle/CBattleAnimations.cpp +++ b/client/battle/CBattleAnimations.cpp @@ -17,7 +17,7 @@ #include "../gui/SDL_Extensions.h" #include "../../CCallback.h" -#include "../../lib/BattleState.h" +#include "../../lib/CStack.h" #include "../../lib/CTownHandler.h" #include "../../lib/mapObjects/CGTownInstance.h" #include "../../lib/spells/CSpellHandler.h" diff --git a/client/battle/CBattleInterface.cpp b/client/battle/CBattleInterface.cpp index cc4e319ae..de5fce4c6 100644 --- a/client/battle/CBattleInterface.cpp +++ b/client/battle/CBattleInterface.cpp @@ -22,7 +22,7 @@ #include "../windows/CSpellWindow.h" #include "../../CCallback.h" -#include "../../lib/BattleState.h" +#include "../../lib/CStack.h" #include "../../lib/CConfigHandler.h" #include "../../lib/CGeneralTextHandler.h" #include "../../lib/CHeroHandler.h" diff --git a/client/battle/CBattleInterfaceClasses.cpp b/client/battle/CBattleInterfaceClasses.cpp index c99cfd769..58c83ac5e 100644 --- a/client/battle/CBattleInterfaceClasses.cpp +++ b/client/battle/CBattleInterfaceClasses.cpp @@ -20,7 +20,7 @@ #include "../windows/CSpellWindow.h" #include "../../CCallback.h" -#include "../../lib/BattleState.h" +#include "../../lib/CStack.h" #include "../../lib/CConfigHandler.h" #include "../../lib/CCreatureHandler.h" #include "../../lib/CGameState.h" diff --git a/client/windows/CCreatureWindow.cpp b/client/windows/CCreatureWindow.cpp index ba3abe492..fa1c4cac7 100644 --- a/client/windows/CCreatureWindow.cpp +++ b/client/windows/CCreatureWindow.cpp @@ -12,7 +12,7 @@ #include "../gui/CGuiHandler.h" #include "../../CCallback.h" -#include "../../lib/BattleState.h" +#include "../../lib/CStack.h" #include "../../lib/CBonusTypeHandler.h" #include "../../lib/CGeneralTextHandler.h" #include "../../lib/CModHandler.h" diff --git a/client/windows/CSpellWindow.cpp b/client/windows/CSpellWindow.cpp index c73eb27d2..944c93f03 100644 --- a/client/windows/CSpellWindow.cpp +++ b/client/windows/CSpellWindow.cpp @@ -25,7 +25,7 @@ #include "../../CCallback.h" -#include "../../lib/BattleState.h" +#include "../../lib/CStack.h" #include "../../lib/CConfigHandler.h" #include "../../lib/CGeneralTextHandler.h" #include "../../lib/CHeroHandler.h" diff --git a/client/windows/GUIClasses.cpp b/client/windows/GUIClasses.cpp index 2dc221d33..815ada1ab 100644 --- a/client/windows/GUIClasses.cpp +++ b/client/windows/GUIClasses.cpp @@ -30,7 +30,7 @@ #include "../../CCallback.h" -#include "../lib/BattleState.h" +#include "../lib/mapObjects/CGHeroInstance.h" #include "../lib/CArtHandler.h" #include "../lib/CBuildingHandler.h" #include "../lib/CConfigHandler.h" diff --git a/lib/BattleAction.cpp b/lib/BattleAction.cpp index 1107fc07a..4495a6e76 100644 --- a/lib/BattleAction.cpp +++ b/lib/BattleAction.cpp @@ -1,7 +1,8 @@ #include "StdInc.h" #include "BattleAction.h" -#include "BattleState.h" +#include "CStack.h" + /* * BattleAction.cpp, part of VCMI engine diff --git a/lib/BattleState.cpp b/lib/BattleInfo.cpp similarity index 59% rename from lib/BattleState.cpp rename to lib/BattleInfo.cpp index 1d345ff56..f377c096a 100644 --- a/lib/BattleState.cpp +++ b/lib/BattleInfo.cpp @@ -1,5 +1,5 @@ /* - * BattleState.cpp, part of VCMI engine + * BattleInfo.cpp, part of VCMI engine * * Authors: listed in file AUTHORS in main folder * @@ -7,31 +7,14 @@ * Full text of license available in license.txt file, in main folder * */ - #include "StdInc.h" -#include "BattleState.h" - -#include -#include "VCMI_Lib.h" -#include "mapObjects/CObjectHandler.h" +#include "BattleInfo.h" +#include "CStack.h" #include "CHeroHandler.h" -#include "spells/CSpellHandler.h" -#include "CTownHandler.h" #include "NetPacks.h" -#include "JsonNode.h" #include "filesystem/Filesystem.h" -#include "CRandomGenerator.h" #include "mapObjects/CGTownInstance.h" -SiegeInfo::SiegeInfo() -{ - for (int i = 0; i < wallState.size(); ++i) - { - wallState[i] = EWallState::NONE; - } - gateState = EGateState::NONE; -} - const CStack * BattleInfo::getNextStack() const { std::vector hlp; @@ -743,523 +726,6 @@ CGHeroInstance * BattleInfo::battleGetFightingHero(ui8 side) const return const_cast(CBattleInfoEssentials::battleGetFightingHero(side)); } -CStack::CStack(const CStackInstance *Base, PlayerColor O, int I, bool AO, SlotID S) - : base(Base), ID(I), owner(O), slot(S), attackerOwned(AO), - counterAttacksPerformed(0),counterAttacksTotalCache(0), cloneID(-1), - firstHPleft(-1), position(), shots(0), casts(0), resurrected(0) -{ - assert(base); - type = base->type; - count = baseAmount = base->count; - setNodeType(STACK_BATTLE); -} -CStack::CStack() -{ - init(); - setNodeType(STACK_BATTLE); -} -CStack::CStack(const CStackBasicDescriptor *stack, PlayerColor O, int I, bool AO, SlotID S) - : base(nullptr), ID(I), owner(O), slot(S), attackerOwned(AO), - counterAttacksPerformed(0), counterAttacksTotalCache(0), cloneID(-1), - firstHPleft(-1), position(), shots(0), casts(0), resurrected(0) -{ - type = stack->type; - count = baseAmount = stack->count; - setNodeType(STACK_BATTLE); -} - -void CStack::init() -{ - base = nullptr; - type = nullptr; - ID = -1; - count = baseAmount = -1; - firstHPleft = -1; - owner = PlayerColor::NEUTRAL; - slot = SlotID(255); - attackerOwned = false; - position = BattleHex(); - counterAttacksPerformed = 0; - counterAttacksTotalCache = 0; - cloneID = -1; - - shots = 0; - casts = 0; - resurrected = 0; -} - -void CStack::postInit() -{ - assert(type); - assert(getParentNodes().size()); - - firstHPleft = MaxHealth(); - shots = getCreature()->valOfBonuses(Bonus::SHOTS); - counterAttacksPerformed = 0; - counterAttacksTotalCache = 0; - casts = valOfBonuses(Bonus::CASTS); - resurrected = 0; - cloneID = -1; -} - -ui32 CStack::level() const -{ - if (base) - return base->getLevel(); //creatture or commander - else - return std::max(1, (int)getCreature()->level); //war machine, clone etc -} - -si32 CStack::magicResistance() const -{ - si32 magicResistance; - if (base) //TODO: make war machines receive aura of magic resistance - { - magicResistance = base->magicResistance(); - int auraBonus = 0; - for (const CStack * stack : base->armyObj->battle-> batteAdjacentCreatures(this)) - { - if (stack->owner == owner) - { - vstd::amax(auraBonus, stack->valOfBonuses(Bonus::SPELL_RESISTANCE_AURA)); //max value - } - } - magicResistance += auraBonus; - vstd::amin (magicResistance, 100); - } - else - magicResistance = type->magicResistance(); - return magicResistance; -} - -void CStack::stackEffectToFeature(std::vector & sf, const Bonus & sse) -{ - const CSpell * sp = SpellID(sse.sid).toSpell(); - - std::vector tmp; - sp->getEffects(tmp, sse.val); - - for(Bonus& b : tmp) - { - if(b.turnsRemain == 0) - b.turnsRemain = sse.turnsRemain; - sf.push_back(b); - } -} - -bool CStack::willMove(int turn /*= 0*/) const -{ - return ( turn ? true : !vstd::contains(state, EBattleStackState::DEFENDING) ) - && !moved(turn) - && canMove(turn); -} - -bool CStack::canMove( int turn /*= 0*/ ) const -{ - return alive() - && !hasBonus(Selector::type(Bonus::NOT_ACTIVE).And(Selector::turns(turn))); //eg. Ammo Cart or blinded creature -} - -bool CStack::moved( int turn /*= 0*/ ) const -{ - if(!turn) - return vstd::contains(state, EBattleStackState::MOVED); - else - return false; -} - -bool CStack::waited(int turn /*= 0*/) const -{ - if(!turn) - return vstd::contains(state, EBattleStackState::WAITING); - else - return false; -} - -bool CStack::doubleWide() const -{ - return getCreature()->doubleWide; -} - -BattleHex CStack::occupiedHex() const -{ - return occupiedHex(position); -} - -BattleHex CStack::occupiedHex(BattleHex assumedPos) const -{ - if (doubleWide()) - { - if (attackerOwned) - return assumedPos - 1; - else - return assumedPos + 1; - } - else - { - return BattleHex::INVALID; - } -} - -std::vector CStack::getHexes() const -{ - return getHexes(position); -} - -std::vector CStack::getHexes(BattleHex assumedPos) const -{ - return getHexes(assumedPos, doubleWide(), attackerOwned); -} - -std::vector CStack::getHexes(BattleHex assumedPos, bool twoHex, bool AttackerOwned) -{ - std::vector hexes; - hexes.push_back(assumedPos); - - if (twoHex) - { - if (AttackerOwned) - hexes.push_back(assumedPos - 1); - else - hexes.push_back(assumedPos + 1); - } - - return hexes; -} - -bool CStack::coversPos(BattleHex pos) const -{ - return vstd::contains(getHexes(), pos); -} - -std::vector CStack::getSurroundingHexes(BattleHex attackerPos) const -{ - BattleHex hex = (attackerPos != BattleHex::INVALID) ? attackerPos : position; //use hypothetical position - std::vector hexes; - if (doubleWide()) - { - const int WN = GameConstants::BFIELD_WIDTH; - if(attackerOwned) - { //position is equal to front hex - BattleHex::checkAndPush(hex - ( (hex/WN)%2 ? WN+2 : WN+1 ), hexes); - BattleHex::checkAndPush(hex - ( (hex/WN)%2 ? WN+1 : WN ), hexes); - BattleHex::checkAndPush(hex - ( (hex/WN)%2 ? WN : WN-1 ), hexes); - BattleHex::checkAndPush(hex - 2, hexes); - BattleHex::checkAndPush(hex + 1, hexes); - BattleHex::checkAndPush(hex + ( (hex/WN)%2 ? WN-2 : WN-1 ), hexes); - BattleHex::checkAndPush(hex + ( (hex/WN)%2 ? WN-1 : WN ), hexes); - BattleHex::checkAndPush(hex + ( (hex/WN)%2 ? WN : WN+1 ), hexes); - } - else - { - BattleHex::checkAndPush(hex - ( (hex/WN)%2 ? WN+1 : WN ), hexes); - BattleHex::checkAndPush(hex - ( (hex/WN)%2 ? WN : WN-1 ), hexes); - BattleHex::checkAndPush(hex - ( (hex/WN)%2 ? WN-1 : WN-2 ), hexes); - BattleHex::checkAndPush(hex + 2, hexes); - BattleHex::checkAndPush(hex - 1, hexes); - BattleHex::checkAndPush(hex + ( (hex/WN)%2 ? WN-1 : WN ), hexes); - BattleHex::checkAndPush(hex + ( (hex/WN)%2 ? WN : WN+1 ), hexes); - BattleHex::checkAndPush(hex + ( (hex/WN)%2 ? WN+1 : WN+2 ), hexes); - } - return hexes; - } - else - { - return hex.neighbouringTiles(); - } -} - -std::vector CStack::activeSpells() const -{ - std::vector ret; - - std::stringstream cachingStr; - cachingStr << "!type_" << Bonus::NONE << "source_" << Bonus::SPELL_EFFECT; - CSelector selector = Selector::sourceType(Bonus::SPELL_EFFECT) - .And(CSelector([](const Bonus *b)->bool - { - return b->type != Bonus::NONE; - })); - - TBonusListPtr spellEffects = getBonuses(selector, Selector::all, cachingStr.str()); - for(const std::shared_ptr it : *spellEffects) - { - if (!vstd::contains(ret, it->sid)) //do not duplicate spells with multiple effects - ret.push_back(it->sid); - } - - return ret; -} - -CStack::~CStack() -{ - detachFromAll(); -} - -const CGHeroInstance * CStack::getMyHero() const -{ - if(base) - return dynamic_cast(base->armyObj); - else //we are attached directly? - for(const CBonusSystemNode *n : getParentNodes()) - if(n->getNodeType() == HERO) - return dynamic_cast(n); - - return nullptr; -} - -ui32 CStack::totalHealth() const -{ - return ((count > 0) ? MaxHealth() * (count-1) : 0) + firstHPleft;//do not hide possible invalid firstHPleft for dead stack -} - -std::string CStack::nodeName() const -{ - std::ostringstream oss; - oss << "Battle stack [" << ID << "]: " << count << " creatures of "; - if(type) - oss << type->namePl; - else - oss << "[UNDEFINED TYPE]"; - - oss << " from slot " << slot; - if(base && base->armyObj) - oss << " of armyobj=" << base->armyObj->id.getNum(); - return oss.str(); -} - -std::pair CStack::countKilledByAttack(int damageReceived) const -{ - int newRemainingHP = 0; - int killedCount = damageReceived / MaxHealth(); - unsigned damageFirst = damageReceived % MaxHealth(); - - if (damageReceived && vstd::contains(state, EBattleStackState::CLONED)) // block ability should not kill clone (0 damage) - { - killedCount = count; - } - else - { - if( firstHPleft <= damageFirst ) - { - killedCount++; - newRemainingHP = firstHPleft + MaxHealth() - damageFirst; - } - else - { - newRemainingHP = firstHPleft - damageFirst; - } - } - - if(killedCount == count) - newRemainingHP = 0; - - return std::make_pair(killedCount, newRemainingHP); -} - -void CStack::prepareAttacked(BattleStackAttacked &bsa, CRandomGenerator & rand, boost::optional customCount /*= boost::none*/) const -{ - auto afterAttack = countKilledByAttack(bsa.damageAmount); - - bsa.killedAmount = afterAttack.first; - bsa.newHP = afterAttack.second; - - - if(bsa.damageAmount && vstd::contains(state, EBattleStackState::CLONED)) // block ability should not kill clone (0 damage) - { - bsa.flags |= BattleStackAttacked::CLONE_KILLED; - return; // no rebirth I believe - } - - const int countToUse = customCount ? *customCount : count; - - if(countToUse <= bsa.killedAmount) //stack killed - { - bsa.newAmount = 0; - bsa.flags |= BattleStackAttacked::KILLED; - bsa.killedAmount = countToUse; //we cannot kill more creatures than we have - - int resurrectFactor = valOfBonuses(Bonus::REBIRTH); - if(resurrectFactor > 0 && casts) //there must be casts left - { - int resurrectedStackCount = base->count * resurrectFactor / 100; - - // last stack has proportional chance to rebirth - auto diff = base->count * resurrectFactor / 100.0 - resurrectedStackCount; - if (diff > rand.nextDouble(0, 0.99)) - { - resurrectedStackCount += 1; - } - - if(hasBonusOfType(Bonus::REBIRTH, 1)) - { - // resurrect at least one Sacred Phoenix - vstd::amax(resurrectedStackCount, 1); - } - - if(resurrectedStackCount > 0) - { - bsa.flags |= BattleStackAttacked::REBIRTH; - bsa.newAmount = resurrectedStackCount; //risky? - bsa.newHP = MaxHealth(); //resore full health - } - } - } - else - { - bsa.newAmount = countToUse - bsa.killedAmount; - } -} - -bool CStack::isMeleeAttackPossible(const CStack * attacker, const CStack * defender, BattleHex attackerPos /*= BattleHex::INVALID*/, BattleHex defenderPos /*= BattleHex::INVALID*/) -{ - if (!attackerPos.isValid()) - { - attackerPos = attacker->position; - } - if (!defenderPos.isValid()) - { - defenderPos = defender->position; - } - - return - (BattleHex::mutualPosition(attackerPos, defenderPos) >= 0) //front <=> front - || (attacker->doubleWide() //back <=> front - && BattleHex::mutualPosition(attackerPos + (attacker->attackerOwned ? -1 : 1), defenderPos) >= 0) - || (defender->doubleWide() //front <=> back - && BattleHex::mutualPosition(attackerPos, defenderPos + (defender->attackerOwned ? -1 : 1)) >= 0) - || (defender->doubleWide() && attacker->doubleWide()//back <=> back - && BattleHex::mutualPosition(attackerPos + (attacker->attackerOwned ? -1 : 1), defenderPos + (defender->attackerOwned ? -1 : 1)) >= 0); - -} - -bool CStack::ableToRetaliate() const //FIXME: crash after clone is killed -{ - return alive() - && (counterAttacksPerformed < counterAttacksTotal() || hasBonusOfType(Bonus::UNLIMITED_RETALIATIONS)) - && !hasBonusOfType(Bonus::SIEGE_WEAPON) - && !hasBonusOfType(Bonus::HYPNOTIZED) - && !hasBonusOfType(Bonus::NO_RETALIATION); -} - -ui8 CStack::counterAttacksTotal() const -{ - //after dispell bonus should remain during current round - ui8 val = 1 + valOfBonuses(Bonus::ADDITIONAL_RETALIATION); - vstd::amax(counterAttacksTotalCache, val); - return counterAttacksTotalCache; -} - -si8 CStack::counterAttacksRemaining() const -{ - return counterAttacksTotal() - counterAttacksPerformed; -} - -std::string CStack::getName() const -{ - return (count > 1) ? type->namePl : type->nameSing; //War machines can't use base -} - -bool CStack::isValidTarget(bool allowDead/* = false*/) const -{ - return (alive() || (allowDead && isDead())) && position.isValid() && !isTurret(); -} - -bool CStack::isDead() const -{ - return !alive() && !isGhost(); -} - -bool CStack::isGhost() const -{ - return vstd::contains(state,EBattleStackState::GHOST); -} - -bool CStack::isTurret() const -{ - return type->idNumber == CreatureID::ARROW_TOWERS; -} - -bool CStack::canBeHealed() const -{ - return firstHPleft < MaxHealth() - && isValidTarget() - && !hasBonusOfType(Bonus::SIEGE_WEAPON); -} - -void CStack::makeGhost() -{ - state.erase(EBattleStackState::ALIVE); - state.insert(EBattleStackState::GHOST_PENDING); -} - -ui32 CStack::calculateHealedHealthPoints(ui32 toHeal, const bool resurrect) const -{ - if(!resurrect && !alive()) - { - logGlobal->warnStream() <<"Attempt to heal corpse detected."; - return 0; - } - - return std::min(toHeal, MaxHealth() - firstHPleft + (resurrect ? (baseAmount - count) * MaxHealth() : 0)); -} - -ui8 CStack::getSpellSchoolLevel(const CSpell * spell, int * outSelectedSchool) const -{ - int skill = valOfBonuses(Selector::typeSubtype(Bonus::SPELLCASTER, spell->id)); - - vstd::abetween(skill, 0, 3); - - return skill; -} - -ui32 CStack::getSpellBonus(const CSpell * spell, ui32 base, const CStack * affectedStack) const -{ - //stacks does not have sorcery-like bonuses (yet?) - return base; -} - -int CStack::getEffectLevel(const CSpell * spell) const -{ - return getSpellSchoolLevel(spell); -} - -int CStack::getEffectPower(const CSpell * spell) const -{ - return valOfBonuses(Bonus::CREATURE_SPELL_POWER) * count / 100; -} - -int CStack::getEnchantPower(const CSpell * spell) const -{ - int res = valOfBonuses(Bonus::CREATURE_ENCHANT_POWER); - if(res<=0) - res = 3;//default for creatures - return res; -} - -int CStack::getEffectValue(const CSpell * spell) const -{ - return valOfBonuses(Bonus::SPECIFIC_SPELL_POWER, spell->id.toEnum()) * count; -} - -const PlayerColor CStack::getOwner() const -{ - return owner; -} - -void CStack::getCasterName(MetaString & text) const -{ - //always plural name in case of spell cast. - text.addReplacement(MetaString::CRE_PL_NAMES, type->idNumber.num); -} - -void CStack::getCastDescription(const CSpell * spell, const std::vector & attacked, MetaString & text) const -{ - text.addTxt(MetaString::GENERAL_TXT, 565);//The %s casts %s - //todo: use text 566 for single creature - getCasterName(text); - text.addReplacement(MetaString::SPELL_NAME, spell->id.toEnum()); -} bool CMP_stack::operator()( const CStack* a, const CStack* b ) { @@ -1297,23 +763,3 @@ CMP_stack::CMP_stack( int Phase /*= 1*/, int Turn ) phase = Phase; turn = Turn; } - - -SideInBattle::SideInBattle() -{ - color = PlayerColor::CANNOT_DETERMINE; - hero = nullptr; - armyObject = nullptr; - castSpellsCount = 0; - enchanterCounter = 0; -} - -void SideInBattle::init(const CGHeroInstance *Hero, const CArmedInstance *Army) -{ - hero = Hero; - armyObject = Army; - color = armyObject->getOwner(); - - if(color == PlayerColor::UNFLAGGABLE) - color = PlayerColor::NEUTRAL; -} diff --git a/lib/BattleInfo.h b/lib/BattleInfo.h new file mode 100644 index 000000000..7acbf1b96 --- /dev/null +++ b/lib/BattleInfo.h @@ -0,0 +1,106 @@ +/* + * BattleInfo.h, part of VCMI engine + * + * Authors: listed in file AUTHORS in main folder + * + * License: GNU General Public License v2.0 or later + * Full text of license available in license.txt file, in main folder + * + */ +#pragma once +#include "SiegeInfo.h" +#include "SideInBattle.h" +#include "HeroBonus.h" +#include "CBattleCallback.h" +#include "int3.h" + +class CStack; +class CStackInstance; +class CStackBasicDescriptor; + +struct DLL_LINKAGE BattleInfo : public CBonusSystemNode, public CBattleInfoCallback +{ + std::array sides; //sides[0] - attacker, sides[1] - defender + si32 round, activeStack, selectedStack; + const CGTownInstance * town; //used during town siege, nullptr if this is not a siege (note that fortless town IS also a siege) + int3 tile; //for background and bonuses + std::vector stacks; + std::vector > obstacles; + SiegeInfo si; + + BFieldType battlefieldType; //like !!BA:B + ETerrainType terrainType; //used for some stack nativity checks (not the bonus limiters though that have their own copy) + + ui8 tacticsSide; //which side is requested to play tactics phase + ui8 tacticDistance; //how many hexes we can go forward (1 = only hexes adjacent to margin line) + + template void serialize(Handler &h, const int version) + { + h & sides; + h & round & activeStack & selectedStack & town & tile & stacks & obstacles + & si & battlefieldType & terrainType; + h & tacticsSide & tacticDistance; + h & static_cast(*this); + } + + ////////////////////////////////////////////////////////////////////////// + BattleInfo(); + ~BattleInfo(){}; + + ////////////////////////////////////////////////////////////////////////// + CStack * getStack(int stackID, bool onlyAlive = true); + using CBattleInfoEssentials::battleGetArmyObject; + CArmedInstance * battleGetArmyObject(ui8 side) const; + using CBattleInfoEssentials::battleGetFightingHero; + CGHeroInstance * battleGetFightingHero(ui8 side) const; + + const CStack * getNextStack() const; //which stack will have turn after current one + //void getStackQueue(std::vector &out, int howMany, int turn = 0, int lastMoved = -1) const; //returns stack in order of their movement action + + //void getAccessibilityMap(bool *accessibility, bool twoHex, bool attackerOwned, bool addOccupiable, std::set & occupyable, bool flying, const CStack* stackToOmmit = nullptr) const; //send pointer to at least 187 allocated bytes + //static bool isAccessible(BattleHex hex, bool * accessibility, bool twoHex, bool attackerOwned, bool flying, bool lastPos); //helper for makeBFS + int getAvaliableHex(CreatureID creID, bool attackerOwned, int initialPos = -1) const; //find place for summon / clone effects + //void makeBFS(BattleHex start, bool*accessibility, BattleHex *predecessor, int *dists, bool twoHex, bool attackerOwned, bool flying, bool fillPredecessors) const; //*accessibility must be prepared bool[187] array; last two pointers must point to the at least 187-elements int arrays - there is written result + std::pair< std::vector, int > getPath(BattleHex start, BattleHex dest, const CStack *stack); //returned value: pair; length may be different than number of elements in path since flying vreatures jump between distant hexes + //std::vector getAccessibility(const CStack * stack, bool addOccupiable, std::vector * attackable = nullptr, bool forPassingBy = false) const; //returns vector of accessible tiles (taking into account the creature range) + + //bool isObstacleVisibleForSide(const CObstacleInstance &obstacle, ui8 side) const; + std::shared_ptr getObstacleOnTile(BattleHex tile) const; + std::set getStoppers(bool whichSidePerspective) const; + + ui32 calculateDmg(const CStack * attacker, const CStack * defender, bool shooting, ui8 charge, bool lucky, bool unlucky, bool deathBlow, bool ballistaDoubleDmg, CRandomGenerator & rand); //charge - number of hexes travelled before attack (for champion's jousting) + void calculateCasualties(std::map *casualties) const; //casualties are array of maps size 2 (attacker, defeneder), maps are (crid => amount) + + //void getPotentiallyAttackableHexes(AttackableTiles &at, const CStack* attacker, BattleHex destinationTile, BattleHex attackerPos); //hexes around target that could be attacked in melee + //std::set getAttackedCreatures(const CStack* attacker, BattleHex destinationTile, BattleHex attackerPos = BattleHex::INVALID); //calculates range of multi-hex attacks + //std::set getAttackedHexes(const CStack* attacker, BattleHex destinationTile, BattleHex attackerPos = BattleHex::INVALID); //calculates range of multi-hex attacks + + CStack * generateNewStack(const CStackInstance &base, bool attackerOwned, SlotID slot, BattleHex position) const; //helper for CGameHandler::setupBattle and spells addign new stacks to the battlefield + CStack * generateNewStack(const CStackBasicDescriptor &base, bool attackerOwned, SlotID slot, BattleHex position) const; //helper for CGameHandler::setupBattle and spells addign new stacks to the battlefield + int getIdForNewStack() const; //suggest a currently unused ID that'd suitable for generating a new stack + + const CGHeroInstance * getHero(PlayerColor player) const; //returns fighting hero that belongs to given player + + void localInit(); + + void localInitStack(CStack * s); + static BattleInfo * setupBattle( int3 tile, ETerrainType terrain, BFieldType battlefieldType, const CArmedInstance *armies[2], const CGHeroInstance * heroes[2], bool creatureBank, const CGTownInstance *town ); + //bool hasNativeStack(ui8 side) const; + + PlayerColor theOtherPlayer(PlayerColor player) const; + ui8 whatSide(PlayerColor player) const; + + static BattlefieldBI::BattlefieldBI battlefieldTypeToBI(BFieldType bfieldType); //converts above to ERM BI format + static int battlefieldTypeToTerrain(int bfieldType); //converts above to ERM BI format +}; + + +class DLL_LINKAGE CMP_stack +{ + int phase; //rules of which phase will be used + int turn; +public: + + bool operator ()(const CStack* a, const CStack* b); + CMP_stack(int Phase = 1, int Turn = 0); +}; diff --git a/lib/BattleState.h b/lib/BattleState.h deleted file mode 100644 index 69f96a8eb..000000000 --- a/lib/BattleState.h +++ /dev/null @@ -1,310 +0,0 @@ -/* - * BattleState.h, part of VCMI engine - * - * Authors: listed in file AUTHORS in main folder - * - * License: GNU General Public License v2.0 or later - * Full text of license available in license.txt file, in main folder - * - */ - -#pragma once - -#include "BattleHex.h" -#include "mapObjects/CArmedInstance.h" // for army serialization -#include "mapObjects/CGHeroInstance.h" // for commander serialization -#include "CCreatureHandler.h" -#include "ConstTransitivePtr.h" -#include "GameConstants.h" -#include "CBattleCallback.h" -#include "int3.h" -#include "spells/Magic.h" - -class CGHeroInstance; -class CStack; -class CArmedInstance; -class CGTownInstance; -class CStackInstance; -struct BattleStackAttacked; -class CRandomGenerator; - - -//only for use in BattleInfo -struct DLL_LINKAGE SiegeInfo -{ - std::array wallState; - EGateState gateState; - - SiegeInfo(); - - // return EWallState decreased by value of damage points - static EWallState::EWallState applyDamage(EWallState::EWallState state, unsigned int value) - { - if (value == 0) - return state; - - switch (applyDamage(state, value - 1)) - { - case EWallState::INTACT : return EWallState::DAMAGED; - case EWallState::DAMAGED : return EWallState::DESTROYED; - case EWallState::DESTROYED : return EWallState::DESTROYED; - default: return EWallState::NONE; - } - } - - template void serialize(Handler &h, const int version) - { - h & wallState & gateState; - } -}; - -struct DLL_LINKAGE SideInBattle -{ - PlayerColor color; - const CGHeroInstance *hero; //may be NULL if army is not commanded by hero - const CArmedInstance *armyObject; //adv. map object with army that participates in battle; may be same as hero - - ui8 castSpellsCount; //how many spells each side has cast this turn - std::vector usedSpellsHistory; //every time hero casts spell, it's inserted here -> eagle eye skill - si16 enchanterCounter; //tends to pass through 0, so sign is needed - - SideInBattle(); - void init(const CGHeroInstance *Hero, const CArmedInstance *Army); - - - template void serialize(Handler &h, const int version) - { - h & color & hero & armyObject; - h & castSpellsCount & usedSpellsHistory & enchanterCounter; - } -}; - -struct DLL_LINKAGE BattleInfo : public CBonusSystemNode, public CBattleInfoCallback -{ - std::array sides; //sides[0] - attacker, sides[1] - defender - si32 round, activeStack, selectedStack; - const CGTownInstance * town; //used during town siege, nullptr if this is not a siege (note that fortless town IS also a siege) - int3 tile; //for background and bonuses - std::vector stacks; - std::vector > obstacles; - SiegeInfo si; - - BFieldType battlefieldType; //like !!BA:B - ETerrainType terrainType; //used for some stack nativity checks (not the bonus limiters though that have their own copy) - - ui8 tacticsSide; //which side is requested to play tactics phase - ui8 tacticDistance; //how many hexes we can go forward (1 = only hexes adjacent to margin line) - - template void serialize(Handler &h, const int version) - { - h & sides; - h & round & activeStack & selectedStack & town & tile & stacks & obstacles - & si & battlefieldType & terrainType; - h & tacticsSide & tacticDistance; - h & static_cast(*this); - } - - ////////////////////////////////////////////////////////////////////////// - BattleInfo(); - ~BattleInfo(){}; - - ////////////////////////////////////////////////////////////////////////// - CStack * getStack(int stackID, bool onlyAlive = true); - using CBattleInfoEssentials::battleGetArmyObject; - CArmedInstance * battleGetArmyObject(ui8 side) const; - using CBattleInfoEssentials::battleGetFightingHero; - CGHeroInstance * battleGetFightingHero(ui8 side) const; - - const CStack * getNextStack() const; //which stack will have turn after current one - //void getStackQueue(std::vector &out, int howMany, int turn = 0, int lastMoved = -1) const; //returns stack in order of their movement action - - //void getAccessibilityMap(bool *accessibility, bool twoHex, bool attackerOwned, bool addOccupiable, std::set & occupyable, bool flying, const CStack* stackToOmmit = nullptr) const; //send pointer to at least 187 allocated bytes - //static bool isAccessible(BattleHex hex, bool * accessibility, bool twoHex, bool attackerOwned, bool flying, bool lastPos); //helper for makeBFS - int getAvaliableHex(CreatureID creID, bool attackerOwned, int initialPos = -1) const; //find place for summon / clone effects - //void makeBFS(BattleHex start, bool*accessibility, BattleHex *predecessor, int *dists, bool twoHex, bool attackerOwned, bool flying, bool fillPredecessors) const; //*accessibility must be prepared bool[187] array; last two pointers must point to the at least 187-elements int arrays - there is written result - std::pair< std::vector, int > getPath(BattleHex start, BattleHex dest, const CStack *stack); //returned value: pair; length may be different than number of elements in path since flying vreatures jump between distant hexes - //std::vector getAccessibility(const CStack * stack, bool addOccupiable, std::vector * attackable = nullptr, bool forPassingBy = false) const; //returns vector of accessible tiles (taking into account the creature range) - - //bool isObstacleVisibleForSide(const CObstacleInstance &obstacle, ui8 side) const; - std::shared_ptr getObstacleOnTile(BattleHex tile) const; - std::set getStoppers(bool whichSidePerspective) const; - - ui32 calculateDmg(const CStack * attacker, const CStack * defender, bool shooting, ui8 charge, bool lucky, bool unlucky, bool deathBlow, bool ballistaDoubleDmg, CRandomGenerator & rand); //charge - number of hexes travelled before attack (for champion's jousting) - void calculateCasualties(std::map *casualties) const; //casualties are array of maps size 2 (attacker, defeneder), maps are (crid => amount) - - //void getPotentiallyAttackableHexes(AttackableTiles &at, const CStack* attacker, BattleHex destinationTile, BattleHex attackerPos); //hexes around target that could be attacked in melee - //std::set getAttackedCreatures(const CStack* attacker, BattleHex destinationTile, BattleHex attackerPos = BattleHex::INVALID); //calculates range of multi-hex attacks - //std::set getAttackedHexes(const CStack* attacker, BattleHex destinationTile, BattleHex attackerPos = BattleHex::INVALID); //calculates range of multi-hex attacks - - CStack * generateNewStack(const CStackInstance &base, bool attackerOwned, SlotID slot, BattleHex position) const; //helper for CGameHandler::setupBattle and spells addign new stacks to the battlefield - CStack * generateNewStack(const CStackBasicDescriptor &base, bool attackerOwned, SlotID slot, BattleHex position) const; //helper for CGameHandler::setupBattle and spells addign new stacks to the battlefield - int getIdForNewStack() const; //suggest a currently unused ID that'd suitable for generating a new stack - - const CGHeroInstance * getHero(PlayerColor player) const; //returns fighting hero that belongs to given player - - void localInit(); - - void localInitStack(CStack * s); - static BattleInfo * setupBattle( int3 tile, ETerrainType terrain, BFieldType battlefieldType, const CArmedInstance *armies[2], const CGHeroInstance * heroes[2], bool creatureBank, const CGTownInstance *town ); - //bool hasNativeStack(ui8 side) const; - - PlayerColor theOtherPlayer(PlayerColor player) const; - ui8 whatSide(PlayerColor player) const; - - static BattlefieldBI::BattlefieldBI battlefieldTypeToBI(BFieldType bfieldType); //converts above to ERM BI format - static int battlefieldTypeToTerrain(int bfieldType); //converts above to ERM BI format -}; - -class DLL_LINKAGE CStack : public CBonusSystemNode, public CStackBasicDescriptor, public ISpellCaster -{ -public: - const CStackInstance *base; //garrison slot from which stack originates (nullptr for war machines, summoned cres, etc) - - ui32 ID; //unique ID of stack - ui32 baseAmount; - ui32 firstHPleft; //HP of first creature in stack - PlayerColor owner; //owner - player colour (255 for neutrals) - SlotID slot; //slot - position in garrison (may be 255 for neutrals/called creatures) - bool attackerOwned; //if true, this stack is owned by attakcer (this one from left hand side of battle) - BattleHex position; //position on battlefield; -2 - keep, -3 - lower tower, -4 - upper tower - ///how many times this stack has been counterattacked this round - ui8 counterAttacksPerformed; - ///cached total count of counterattacks; should be cleared each round;do not serialize - mutable ui8 counterAttacksTotalCache; - si16 shots; //how many shots left - ui8 casts; //how many casts left - TQuantity resurrected; // these units will be taken back after battle is over - ///id of alive clone of this stack clone if any - si32 cloneID; - std::set state; - //overrides - const CCreature* getCreature() const {return type;} - - CStack(const CStackInstance *base, PlayerColor O, int I, bool AO, SlotID S); //c-tor - CStack(const CStackBasicDescriptor *stack, PlayerColor O, int I, bool AO, SlotID S = SlotID(255)); //c-tor - CStack(); //c-tor - ~CStack(); - std::string nodeName() const override; - - void init(); //set initial (invalid) values - void postInit(); //used to finish initialization when inheriting creature parameters is working - std::string getName() const; //plural or singular - bool willMove(int turn = 0) const; //if stack has remaining move this turn - bool ableToRetaliate() const; //if stack can retaliate after attacked - ///how many times this stack can counterattack in one round - ui8 counterAttacksTotal() const; - ///how many times this stack can counterattack in one round more - si8 counterAttacksRemaining() const; - bool moved(int turn = 0) const; //if stack was already moved this turn - bool waited(int turn = 0) const; - bool canMove(int turn = 0) const; //if stack can move - bool canBeHealed() const; //for first aid tent - only harmed stacks that are not war machines - ///returns actual heal value based on internal state - ui32 calculateHealedHealthPoints(ui32 toHeal, const bool resurrect) const; - ui32 level() const; - si32 magicResistance() const override; //include aura of resistance - static void stackEffectToFeature(std::vector & sf, const Bonus & sse); - std::vector activeSpells() const; //returns vector of active spell IDs sorted by time of cast - const CGHeroInstance *getMyHero() const; //if stack belongs to hero (directly or was by him summoned) returns hero, nullptr otherwise - ui32 totalHealth() const; // total health for all creatures in stack; - - static bool isMeleeAttackPossible(const CStack * attacker, const CStack * defender, BattleHex attackerPos = BattleHex::INVALID, BattleHex defenderPos = BattleHex::INVALID); - - bool doubleWide() const; - BattleHex occupiedHex() const; //returns number of occupied hex (not the position) if stack is double wide; otherwise -1 - BattleHex occupiedHex(BattleHex assumedPos) const; //returns number of occupied hex (not the position) if stack is double wide and would stand on assumedPos; otherwise -1 - std::vector getHexes() const; //up to two occupied hexes, starting from front - std::vector getHexes(BattleHex assumedPos) const; //up to two occupied hexes, starting from front - static std::vector getHexes(BattleHex assumedPos, bool twoHex, bool AttackerOwned); //up to two occupied hexes, starting from front - bool coversPos(BattleHex position) const; //checks also if unit is double-wide - std::vector getSurroundingHexes(BattleHex attackerPos = BattleHex::INVALID) const; // get six or 8 surrounding hexes depending on creature size - - std::pair countKilledByAttack(int damageReceived) const; //returns pair - void prepareAttacked(BattleStackAttacked &bsa, CRandomGenerator & rand, boost::optional customCount = boost::none) const; //requires bsa.damageAmout filled - - ///ISpellCaster - ui8 getSpellSchoolLevel(const CSpell * spell, int *outSelectedSchool = nullptr) const override; - ui32 getSpellBonus(const CSpell * spell, ui32 base, const CStack * affectedStack) const override; - - ///default spell school level for effect calculation - int getEffectLevel(const CSpell * spell) const override; - - ///default spell-power for damage/heal calculation - int getEffectPower(const CSpell * spell) const override; - - ///default spell-power for timed effects duration - int getEnchantPower(const CSpell * spell) const override; - - ///damage/heal override(ignores spell configuration, effect level and effect power) - int getEffectValue(const CSpell * spell) const override; - - const PlayerColor getOwner() const override; - - void getCasterName(MetaString & text) const override; - - void getCastDescription(const CSpell * spell, const std::vector & attacked, MetaString & text) const override; - - ///stack will be ghost in next battle state update - void makeGhost(); - - template void serialize(Handler &h, const int version) - { - assert(isIndependentNode()); - h & static_cast(*this); - h & static_cast(*this); - h & ID & baseAmount & firstHPleft & owner & slot & attackerOwned & position & state & counterAttacksPerformed - & shots & casts & count & resurrected; - - const CArmedInstance *army = (base ? base->armyObj : nullptr); - SlotID extSlot = (base ? base->armyObj->findStack(base) : SlotID()); - - if(h.saving) - { - h & army & extSlot; - } - else - { - h & army & extSlot; - if(extSlot == SlotID::COMMANDER_SLOT_PLACEHOLDER) - { - auto hero = dynamic_cast(army); - assert (hero); - base = hero->commander; - } - else if(slot == SlotID::SUMMONED_SLOT_PLACEHOLDER || slot == SlotID::ARROW_TOWERS_SLOT || slot == SlotID::WAR_MACHINES_SLOT) - { - //no external slot possible, so no base stack - base = nullptr; - } - else if(!army || extSlot == SlotID() || !army->hasStackAtSlot(extSlot)) - { - base = nullptr; - logGlobal->warnStream() << type->nameSing << " doesn't have a base stack!"; - } - else - { - base = &army->getStack(extSlot); - } - } - - } - bool alive() const //determines if stack is alive - { - return vstd::contains(state,EBattleStackState::ALIVE); - } - - bool isDead() const; - bool isGhost() const; //determines if stack was removed - bool isValidTarget(bool allowDead = false) const; //non-turret non-ghost stacks (can be attacked or be object of magic effect) - bool isTurret() const; -}; - -class DLL_LINKAGE CMP_stack -{ - int phase; //rules of which phase will be used - int turn; -public: - - bool operator ()(const CStack* a, const CStack* b); - CMP_stack(int Phase = 1, int Turn = 0); -}; diff --git a/lib/CBattleCallback.cpp b/lib/CBattleCallback.cpp index 6206055d5..0c1e56a3d 100644 --- a/lib/CBattleCallback.cpp +++ b/lib/CBattleCallback.cpp @@ -1,6 +1,7 @@ #include "StdInc.h" #include "CBattleCallback.h" -#include "BattleState.h" +#include "CStack.h" +#include "BattleInfo.h" #include "CGameState.h" #include "NetPacks.h" #include "spells/CSpellHandler.h" diff --git a/lib/CGameInfoCallback.cpp b/lib/CGameInfoCallback.cpp index a5b9a3dc3..24785cfd5 100644 --- a/lib/CGameInfoCallback.cpp +++ b/lib/CGameInfoCallback.cpp @@ -15,7 +15,7 @@ #include "CGeneralTextHandler.h" #include "mapObjects/CObjectHandler.h" // for CGObjectInstance #include "StartInfo.h" // for StartInfo -#include "BattleState.h" // for BattleInfo +#include "BattleInfo.h" // for BattleInfo #include "NetPacks.h" // for InfoWindow #include "CModHandler.h" #include "spells/CSpellHandler.h" diff --git a/lib/CGameInterface.cpp b/lib/CGameInterface.cpp index 977c548b1..6caf1a913 100644 --- a/lib/CGameInterface.cpp +++ b/lib/CGameInterface.cpp @@ -1,7 +1,7 @@ #include "StdInc.h" #include "CGameInterface.h" -#include "BattleState.h" +#include "CStack.h" #include "VCMIDirs.h" #ifdef VCMI_WINDOWS diff --git a/lib/CGameState.cpp b/lib/CGameState.cpp index 51c535cc2..af18f26cd 100644 --- a/lib/CGameState.cpp +++ b/lib/CGameState.cpp @@ -17,7 +17,7 @@ #include "NetPacks.h" #include "registerTypes/RegisterTypes.h" #include "mapping/CMapInfo.h" -#include "BattleState.h" +#include "BattleInfo.h" #include "JsonNode.h" #include "filesystem/Filesystem.h" #include "GameConstants.h" diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt index 435546199..aa62bf328 100644 --- a/lib/CMakeLists.txt +++ b/lib/CMakeLists.txt @@ -78,7 +78,10 @@ set(lib_SRCS BattleAction.cpp BattleHex.cpp - BattleState.cpp + BattleInfo.cpp + SiegeInfo.cpp + SideInBattle.cpp + CStack.cpp CArtHandler.cpp CBattleCallback.cpp CBonusTypeHandler.cpp diff --git a/lib/CStack.cpp b/lib/CStack.cpp new file mode 100644 index 000000000..4eef55bb1 --- /dev/null +++ b/lib/CStack.cpp @@ -0,0 +1,539 @@ +/* + * CStack.cpp, part of VCMI engine + * + * Authors: listed in file AUTHORS in main folder + * + * License: GNU General Public License v2.0 or later + * Full text of license available in license.txt file, in main folder + * + */ +#include "StdInc.h" +#include "CStack.h" +#include "BattleInfo.h" +#include "spells/CSpellHandler.h" +#include "CRandomGenerator.h" +#include "NetPacks.h" + + +CStack::CStack(const CStackInstance *Base, PlayerColor O, int I, bool AO, SlotID S) + : base(Base), ID(I), owner(O), slot(S), attackerOwned(AO), + counterAttacksPerformed(0),counterAttacksTotalCache(0), cloneID(-1), + firstHPleft(-1), position(), shots(0), casts(0), resurrected(0) +{ + assert(base); + type = base->type; + count = baseAmount = base->count; + setNodeType(STACK_BATTLE); +} +CStack::CStack() +{ + init(); + setNodeType(STACK_BATTLE); +} +CStack::CStack(const CStackBasicDescriptor *stack, PlayerColor O, int I, bool AO, SlotID S) + : base(nullptr), ID(I), owner(O), slot(S), attackerOwned(AO), + counterAttacksPerformed(0), counterAttacksTotalCache(0), cloneID(-1), + firstHPleft(-1), position(), shots(0), casts(0), resurrected(0) +{ + type = stack->type; + count = baseAmount = stack->count; + setNodeType(STACK_BATTLE); +} + +void CStack::init() +{ + base = nullptr; + type = nullptr; + ID = -1; + count = baseAmount = -1; + firstHPleft = -1; + owner = PlayerColor::NEUTRAL; + slot = SlotID(255); + attackerOwned = false; + position = BattleHex(); + counterAttacksPerformed = 0; + counterAttacksTotalCache = 0; + cloneID = -1; + + shots = 0; + casts = 0; + resurrected = 0; +} + +void CStack::postInit() +{ + assert(type); + assert(getParentNodes().size()); + + firstHPleft = MaxHealth(); + shots = getCreature()->valOfBonuses(Bonus::SHOTS); + counterAttacksPerformed = 0; + counterAttacksTotalCache = 0; + casts = valOfBonuses(Bonus::CASTS); + resurrected = 0; + cloneID = -1; +} + +ui32 CStack::level() const +{ + if (base) + return base->getLevel(); //creatture or commander + else + return std::max(1, (int)getCreature()->level); //war machine, clone etc +} + +si32 CStack::magicResistance() const +{ + si32 magicResistance; + if (base) //TODO: make war machines receive aura of magic resistance + { + magicResistance = base->magicResistance(); + int auraBonus = 0; + for (const CStack * stack : base->armyObj->battle-> batteAdjacentCreatures(this)) + { + if (stack->owner == owner) + { + vstd::amax(auraBonus, stack->valOfBonuses(Bonus::SPELL_RESISTANCE_AURA)); //max value + } + } + magicResistance += auraBonus; + vstd::amin (magicResistance, 100); + } + else + magicResistance = type->magicResistance(); + return magicResistance; +} + +void CStack::stackEffectToFeature(std::vector & sf, const Bonus & sse) +{ + const CSpell * sp = SpellID(sse.sid).toSpell(); + + std::vector tmp; + sp->getEffects(tmp, sse.val); + + for(Bonus& b : tmp) + { + if(b.turnsRemain == 0) + b.turnsRemain = sse.turnsRemain; + sf.push_back(b); + } +} + +bool CStack::willMove(int turn /*= 0*/) const +{ + return ( turn ? true : !vstd::contains(state, EBattleStackState::DEFENDING) ) + && !moved(turn) + && canMove(turn); +} + +bool CStack::canMove( int turn /*= 0*/ ) const +{ + return alive() + && !hasBonus(Selector::type(Bonus::NOT_ACTIVE).And(Selector::turns(turn))); //eg. Ammo Cart or blinded creature +} + +bool CStack::moved( int turn /*= 0*/ ) const +{ + if(!turn) + return vstd::contains(state, EBattleStackState::MOVED); + else + return false; +} + +bool CStack::waited(int turn /*= 0*/) const +{ + if(!turn) + return vstd::contains(state, EBattleStackState::WAITING); + else + return false; +} + +bool CStack::doubleWide() const +{ + return getCreature()->doubleWide; +} + +BattleHex CStack::occupiedHex() const +{ + return occupiedHex(position); +} + +BattleHex CStack::occupiedHex(BattleHex assumedPos) const +{ + if (doubleWide()) + { + if (attackerOwned) + return assumedPos - 1; + else + return assumedPos + 1; + } + else + { + return BattleHex::INVALID; + } +} + +std::vector CStack::getHexes() const +{ + return getHexes(position); +} + +std::vector CStack::getHexes(BattleHex assumedPos) const +{ + return getHexes(assumedPos, doubleWide(), attackerOwned); +} + +std::vector CStack::getHexes(BattleHex assumedPos, bool twoHex, bool AttackerOwned) +{ + std::vector hexes; + hexes.push_back(assumedPos); + + if (twoHex) + { + if (AttackerOwned) + hexes.push_back(assumedPos - 1); + else + hexes.push_back(assumedPos + 1); + } + + return hexes; +} + +bool CStack::coversPos(BattleHex pos) const +{ + return vstd::contains(getHexes(), pos); +} + +std::vector CStack::getSurroundingHexes(BattleHex attackerPos) const +{ + BattleHex hex = (attackerPos != BattleHex::INVALID) ? attackerPos : position; //use hypothetical position + std::vector hexes; + if (doubleWide()) + { + const int WN = GameConstants::BFIELD_WIDTH; + if(attackerOwned) + { //position is equal to front hex + BattleHex::checkAndPush(hex - ( (hex/WN)%2 ? WN+2 : WN+1 ), hexes); + BattleHex::checkAndPush(hex - ( (hex/WN)%2 ? WN+1 : WN ), hexes); + BattleHex::checkAndPush(hex - ( (hex/WN)%2 ? WN : WN-1 ), hexes); + BattleHex::checkAndPush(hex - 2, hexes); + BattleHex::checkAndPush(hex + 1, hexes); + BattleHex::checkAndPush(hex + ( (hex/WN)%2 ? WN-2 : WN-1 ), hexes); + BattleHex::checkAndPush(hex + ( (hex/WN)%2 ? WN-1 : WN ), hexes); + BattleHex::checkAndPush(hex + ( (hex/WN)%2 ? WN : WN+1 ), hexes); + } + else + { + BattleHex::checkAndPush(hex - ( (hex/WN)%2 ? WN+1 : WN ), hexes); + BattleHex::checkAndPush(hex - ( (hex/WN)%2 ? WN : WN-1 ), hexes); + BattleHex::checkAndPush(hex - ( (hex/WN)%2 ? WN-1 : WN-2 ), hexes); + BattleHex::checkAndPush(hex + 2, hexes); + BattleHex::checkAndPush(hex - 1, hexes); + BattleHex::checkAndPush(hex + ( (hex/WN)%2 ? WN-1 : WN ), hexes); + BattleHex::checkAndPush(hex + ( (hex/WN)%2 ? WN : WN+1 ), hexes); + BattleHex::checkAndPush(hex + ( (hex/WN)%2 ? WN+1 : WN+2 ), hexes); + } + return hexes; + } + else + { + return hex.neighbouringTiles(); + } +} + +std::vector CStack::activeSpells() const +{ + std::vector ret; + + std::stringstream cachingStr; + cachingStr << "!type_" << Bonus::NONE << "source_" << Bonus::SPELL_EFFECT; + CSelector selector = Selector::sourceType(Bonus::SPELL_EFFECT) + .And(CSelector([](const Bonus *b)->bool + { + return b->type != Bonus::NONE; + })); + + TBonusListPtr spellEffects = getBonuses(selector, Selector::all, cachingStr.str()); + for(const std::shared_ptr it : *spellEffects) + { + if (!vstd::contains(ret, it->sid)) //do not duplicate spells with multiple effects + ret.push_back(it->sid); + } + + return ret; +} + +CStack::~CStack() +{ + detachFromAll(); +} + +const CGHeroInstance * CStack::getMyHero() const +{ + if(base) + return dynamic_cast(base->armyObj); + else //we are attached directly? + for(const CBonusSystemNode *n : getParentNodes()) + if(n->getNodeType() == HERO) + return dynamic_cast(n); + + return nullptr; +} + +ui32 CStack::totalHealth() const +{ + return ((count > 0) ? MaxHealth() * (count-1) : 0) + firstHPleft;//do not hide possible invalid firstHPleft for dead stack +} + +std::string CStack::nodeName() const +{ + std::ostringstream oss; + oss << "Battle stack [" << ID << "]: " << count << " creatures of "; + if(type) + oss << type->namePl; + else + oss << "[UNDEFINED TYPE]"; + + oss << " from slot " << slot; + if(base && base->armyObj) + oss << " of armyobj=" << base->armyObj->id.getNum(); + return oss.str(); +} + +std::pair CStack::countKilledByAttack(int damageReceived) const +{ + int newRemainingHP = 0; + int killedCount = damageReceived / MaxHealth(); + unsigned damageFirst = damageReceived % MaxHealth(); + + if (damageReceived && vstd::contains(state, EBattleStackState::CLONED)) // block ability should not kill clone (0 damage) + { + killedCount = count; + } + else + { + if( firstHPleft <= damageFirst ) + { + killedCount++; + newRemainingHP = firstHPleft + MaxHealth() - damageFirst; + } + else + { + newRemainingHP = firstHPleft - damageFirst; + } + } + + if(killedCount == count) + newRemainingHP = 0; + + return std::make_pair(killedCount, newRemainingHP); +} + +void CStack::prepareAttacked(BattleStackAttacked &bsa, CRandomGenerator & rand, boost::optional customCount /*= boost::none*/) const +{ + auto afterAttack = countKilledByAttack(bsa.damageAmount); + + bsa.killedAmount = afterAttack.first; + bsa.newHP = afterAttack.second; + + + if(bsa.damageAmount && vstd::contains(state, EBattleStackState::CLONED)) // block ability should not kill clone (0 damage) + { + bsa.flags |= BattleStackAttacked::CLONE_KILLED; + return; // no rebirth I believe + } + + const int countToUse = customCount ? *customCount : count; + + if(countToUse <= bsa.killedAmount) //stack killed + { + bsa.newAmount = 0; + bsa.flags |= BattleStackAttacked::KILLED; + bsa.killedAmount = countToUse; //we cannot kill more creatures than we have + + int resurrectFactor = valOfBonuses(Bonus::REBIRTH); + if(resurrectFactor > 0 && casts) //there must be casts left + { + int resurrectedStackCount = base->count * resurrectFactor / 100; + + // last stack has proportional chance to rebirth + auto diff = base->count * resurrectFactor / 100.0 - resurrectedStackCount; + if (diff > rand.nextDouble(0, 0.99)) + { + resurrectedStackCount += 1; + } + + if(hasBonusOfType(Bonus::REBIRTH, 1)) + { + // resurrect at least one Sacred Phoenix + vstd::amax(resurrectedStackCount, 1); + } + + if(resurrectedStackCount > 0) + { + bsa.flags |= BattleStackAttacked::REBIRTH; + bsa.newAmount = resurrectedStackCount; //risky? + bsa.newHP = MaxHealth(); //resore full health + } + } + } + else + { + bsa.newAmount = countToUse - bsa.killedAmount; + } +} + +bool CStack::isMeleeAttackPossible(const CStack * attacker, const CStack * defender, BattleHex attackerPos /*= BattleHex::INVALID*/, BattleHex defenderPos /*= BattleHex::INVALID*/) +{ + if (!attackerPos.isValid()) + { + attackerPos = attacker->position; + } + if (!defenderPos.isValid()) + { + defenderPos = defender->position; + } + + return + (BattleHex::mutualPosition(attackerPos, defenderPos) >= 0) //front <=> front + || (attacker->doubleWide() //back <=> front + && BattleHex::mutualPosition(attackerPos + (attacker->attackerOwned ? -1 : 1), defenderPos) >= 0) + || (defender->doubleWide() //front <=> back + && BattleHex::mutualPosition(attackerPos, defenderPos + (defender->attackerOwned ? -1 : 1)) >= 0) + || (defender->doubleWide() && attacker->doubleWide()//back <=> back + && BattleHex::mutualPosition(attackerPos + (attacker->attackerOwned ? -1 : 1), defenderPos + (defender->attackerOwned ? -1 : 1)) >= 0); + +} + +bool CStack::ableToRetaliate() const //FIXME: crash after clone is killed +{ + return alive() + && (counterAttacksPerformed < counterAttacksTotal() || hasBonusOfType(Bonus::UNLIMITED_RETALIATIONS)) + && !hasBonusOfType(Bonus::SIEGE_WEAPON) + && !hasBonusOfType(Bonus::HYPNOTIZED) + && !hasBonusOfType(Bonus::NO_RETALIATION); +} + +ui8 CStack::counterAttacksTotal() const +{ + //after dispell bonus should remain during current round + ui8 val = 1 + valOfBonuses(Bonus::ADDITIONAL_RETALIATION); + vstd::amax(counterAttacksTotalCache, val); + return counterAttacksTotalCache; +} + +si8 CStack::counterAttacksRemaining() const +{ + return counterAttacksTotal() - counterAttacksPerformed; +} + +std::string CStack::getName() const +{ + return (count > 1) ? type->namePl : type->nameSing; //War machines can't use base +} + +bool CStack::isValidTarget(bool allowDead/* = false*/) const +{ + return (alive() || (allowDead && isDead())) && position.isValid() && !isTurret(); +} + +bool CStack::isDead() const +{ + return !alive() && !isGhost(); +} + +bool CStack::isGhost() const +{ + return vstd::contains(state,EBattleStackState::GHOST); +} + +bool CStack::isTurret() const +{ + return type->idNumber == CreatureID::ARROW_TOWERS; +} + +bool CStack::canBeHealed() const +{ + return firstHPleft < MaxHealth() + && isValidTarget() + && !hasBonusOfType(Bonus::SIEGE_WEAPON); +} + +void CStack::makeGhost() +{ + state.erase(EBattleStackState::ALIVE); + state.insert(EBattleStackState::GHOST_PENDING); +} + +bool CStack::alive() const //determines if stack is alive +{ + return vstd::contains(state,EBattleStackState::ALIVE); +} + +ui32 CStack::calculateHealedHealthPoints(ui32 toHeal, const bool resurrect) const +{ + if(!resurrect && !alive()) + { + logGlobal->warnStream() <<"Attempt to heal corpse detected."; + return 0; + } + + return std::min(toHeal, MaxHealth() - firstHPleft + (resurrect ? (baseAmount - count) * MaxHealth() : 0)); +} + +ui8 CStack::getSpellSchoolLevel(const CSpell * spell, int * outSelectedSchool) const +{ + int skill = valOfBonuses(Selector::typeSubtype(Bonus::SPELLCASTER, spell->id)); + + vstd::abetween(skill, 0, 3); + + return skill; +} + +ui32 CStack::getSpellBonus(const CSpell * spell, ui32 base, const CStack * affectedStack) const +{ + //stacks does not have sorcery-like bonuses (yet?) + return base; +} + +int CStack::getEffectLevel(const CSpell * spell) const +{ + return getSpellSchoolLevel(spell); +} + +int CStack::getEffectPower(const CSpell * spell) const +{ + return valOfBonuses(Bonus::CREATURE_SPELL_POWER) * count / 100; +} + +int CStack::getEnchantPower(const CSpell * spell) const +{ + int res = valOfBonuses(Bonus::CREATURE_ENCHANT_POWER); + if(res<=0) + res = 3;//default for creatures + return res; +} + +int CStack::getEffectValue(const CSpell * spell) const +{ + return valOfBonuses(Bonus::SPECIFIC_SPELL_POWER, spell->id.toEnum()) * count; +} + +const PlayerColor CStack::getOwner() const +{ + return owner; +} + +void CStack::getCasterName(MetaString & text) const +{ + //always plural name in case of spell cast. + text.addReplacement(MetaString::CRE_PL_NAMES, type->idNumber.num); +} + +void CStack::getCastDescription(const CSpell * spell, const std::vector & attacked, MetaString & text) const +{ + text.addTxt(MetaString::GENERAL_TXT, 565);//The %s casts %s + //todo: use text 566 for single creature + getCasterName(text); + text.addReplacement(MetaString::SPELL_NAME, spell->id.toEnum()); +} diff --git a/lib/CStack.h b/lib/CStack.h new file mode 100644 index 000000000..75cc150b0 --- /dev/null +++ b/lib/CStack.h @@ -0,0 +1,156 @@ +/* + * CStack.h, part of VCMI engine + * + * Authors: listed in file AUTHORS in main folder + * + * License: GNU General Public License v2.0 or later + * Full text of license available in license.txt file, in main folder + * + */ +#pragma once +#include "BattleHex.h" +#include "CCreatureHandler.h" +#include "mapObjects/CGHeroInstance.h" // for commander serialization + +struct BattleStackAttacked; + +class DLL_LINKAGE CStack : public CBonusSystemNode, public CStackBasicDescriptor, public ISpellCaster +{ +public: + const CStackInstance *base; //garrison slot from which stack originates (nullptr for war machines, summoned cres, etc) + + ui32 ID; //unique ID of stack + ui32 baseAmount; + ui32 firstHPleft; //HP of first creature in stack + PlayerColor owner; //owner - player colour (255 for neutrals) + SlotID slot; //slot - position in garrison (may be 255 for neutrals/called creatures) + bool attackerOwned; //if true, this stack is owned by attakcer (this one from left hand side of battle) + BattleHex position; //position on battlefield; -2 - keep, -3 - lower tower, -4 - upper tower + ///how many times this stack has been counterattacked this round + ui8 counterAttacksPerformed; + ///cached total count of counterattacks; should be cleared each round;do not serialize + mutable ui8 counterAttacksTotalCache; + si16 shots; //how many shots left + ui8 casts; //how many casts left + TQuantity resurrected; // these units will be taken back after battle is over + ///id of alive clone of this stack clone if any + si32 cloneID; + std::set state; + //overrides + const CCreature* getCreature() const {return type;} + + CStack(const CStackInstance *base, PlayerColor O, int I, bool AO, SlotID S); //c-tor + CStack(const CStackBasicDescriptor *stack, PlayerColor O, int I, bool AO, SlotID S = SlotID(255)); //c-tor + CStack(); //c-tor + ~CStack(); + std::string nodeName() const override; + + void init(); //set initial (invalid) values + void postInit(); //used to finish initialization when inheriting creature parameters is working + std::string getName() const; //plural or singular + bool willMove(int turn = 0) const; //if stack has remaining move this turn + bool ableToRetaliate() const; //if stack can retaliate after attacked + ///how many times this stack can counterattack in one round + ui8 counterAttacksTotal() const; + ///how many times this stack can counterattack in one round more + si8 counterAttacksRemaining() const; + bool moved(int turn = 0) const; //if stack was already moved this turn + bool waited(int turn = 0) const; + bool canMove(int turn = 0) const; //if stack can move + bool canBeHealed() const; //for first aid tent - only harmed stacks that are not war machines + ///returns actual heal value based on internal state + ui32 calculateHealedHealthPoints(ui32 toHeal, const bool resurrect) const; + ui32 level() const; + si32 magicResistance() const override; //include aura of resistance + static void stackEffectToFeature(std::vector & sf, const Bonus & sse); + std::vector activeSpells() const; //returns vector of active spell IDs sorted by time of cast + const CGHeroInstance *getMyHero() const; //if stack belongs to hero (directly or was by him summoned) returns hero, nullptr otherwise + ui32 totalHealth() const; // total health for all creatures in stack; + + static bool isMeleeAttackPossible(const CStack * attacker, const CStack * defender, BattleHex attackerPos = BattleHex::INVALID, BattleHex defenderPos = BattleHex::INVALID); + + bool doubleWide() const; + BattleHex occupiedHex() const; //returns number of occupied hex (not the position) if stack is double wide; otherwise -1 + BattleHex occupiedHex(BattleHex assumedPos) const; //returns number of occupied hex (not the position) if stack is double wide and would stand on assumedPos; otherwise -1 + std::vector getHexes() const; //up to two occupied hexes, starting from front + std::vector getHexes(BattleHex assumedPos) const; //up to two occupied hexes, starting from front + static std::vector getHexes(BattleHex assumedPos, bool twoHex, bool AttackerOwned); //up to two occupied hexes, starting from front + bool coversPos(BattleHex position) const; //checks also if unit is double-wide + std::vector getSurroundingHexes(BattleHex attackerPos = BattleHex::INVALID) const; // get six or 8 surrounding hexes depending on creature size + + std::pair countKilledByAttack(int damageReceived) const; //returns pair + void prepareAttacked(BattleStackAttacked &bsa, CRandomGenerator & rand, boost::optional customCount = boost::none) const; //requires bsa.damageAmout filled + + ///ISpellCaster + ui8 getSpellSchoolLevel(const CSpell * spell, int *outSelectedSchool = nullptr) const override; + ui32 getSpellBonus(const CSpell * spell, ui32 base, const CStack * affectedStack) const override; + + ///default spell school level for effect calculation + int getEffectLevel(const CSpell * spell) const override; + + ///default spell-power for damage/heal calculation + int getEffectPower(const CSpell * spell) const override; + + ///default spell-power for timed effects duration + int getEnchantPower(const CSpell * spell) const override; + + ///damage/heal override(ignores spell configuration, effect level and effect power) + int getEffectValue(const CSpell * spell) const override; + + const PlayerColor getOwner() const override; + + void getCasterName(MetaString & text) const override; + + void getCastDescription(const CSpell * spell, const std::vector & attacked, MetaString & text) const override; + + ///stack will be ghost in next battle state update + void makeGhost(); + + template void serialize(Handler &h, const int version) + { + assert(isIndependentNode()); + h & static_cast(*this); + h & static_cast(*this); + h & ID & baseAmount & firstHPleft & owner & slot & attackerOwned & position & state & counterAttacksPerformed + & shots & casts & count & resurrected; + + const CArmedInstance *army = (base ? base->armyObj : nullptr); + SlotID extSlot = (base ? base->armyObj->findStack(base) : SlotID()); + + if(h.saving) + { + h & army & extSlot; + } + else + { + h & army & extSlot; + if(extSlot == SlotID::COMMANDER_SLOT_PLACEHOLDER) + { + auto hero = dynamic_cast(army); + assert (hero); + base = hero->commander; + } + else if(slot == SlotID::SUMMONED_SLOT_PLACEHOLDER || slot == SlotID::ARROW_TOWERS_SLOT || slot == SlotID::WAR_MACHINES_SLOT) + { + //no external slot possible, so no base stack + base = nullptr; + } + else if(!army || extSlot == SlotID() || !army->hasStackAtSlot(extSlot)) + { + base = nullptr; + logGlobal->warnStream() << type->nameSing << " doesn't have a base stack!"; + } + else + { + base = &army->getStack(extSlot); + } + } + + } + bool alive() const; + + bool isDead() const; + bool isGhost() const; //determines if stack was removed + bool isValidTarget(bool allowDead = false) const; //non-turret non-ghost stacks (can be attacked or be object of magic effect) + bool isTurret() const; +}; diff --git a/lib/HeroBonus.cpp b/lib/HeroBonus.cpp index d29ed67a5..6625228cf 100644 --- a/lib/HeroBonus.cpp +++ b/lib/HeroBonus.cpp @@ -17,7 +17,7 @@ #include "CCreatureSet.h" #include "CHeroHandler.h" #include "CGeneralTextHandler.h" -#include "BattleState.h" +#include "CStack.h" #include "CArtHandler.h" #define FOREACH_PARENT(pname) TNodes lparents; getParents(lparents); for(CBonusSystemNode *pname : lparents) diff --git a/lib/NetPacksLib.cpp b/lib/NetPacksLib.cpp index bfada7adb..938562fff 100644 --- a/lib/NetPacksLib.cpp +++ b/lib/NetPacksLib.cpp @@ -12,7 +12,8 @@ #include "spells/CSpellHandler.h" #include "CCreatureHandler.h" #include "CGameState.h" -#include "BattleState.h" +#include "CStack.h" +#include "BattleInfo.h" #include "CTownHandler.h" #include "mapping/CMapInfo.h" #include "StartInfo.h" diff --git a/lib/SideInBattle.cpp b/lib/SideInBattle.cpp new file mode 100644 index 000000000..a0db160f5 --- /dev/null +++ b/lib/SideInBattle.cpp @@ -0,0 +1,31 @@ +/* + * SideInBattle.cpp, part of VCMI engine + * + * Authors: listed in file AUTHORS in main folder + * + * License: GNU General Public License v2.0 or later + * Full text of license available in license.txt file, in main folder + * + */ +#include "StdInc.h" +#include "SideInBattle.h" +#include "mapObjects/CArmedInstance.h" + +SideInBattle::SideInBattle() +{ + color = PlayerColor::CANNOT_DETERMINE; + hero = nullptr; + armyObject = nullptr; + castSpellsCount = 0; + enchanterCounter = 0; +} + +void SideInBattle::init(const CGHeroInstance *Hero, const CArmedInstance *Army) +{ + hero = Hero; + armyObject = Army; + color = armyObject->getOwner(); + + if(color == PlayerColor::UNFLAGGABLE) + color = PlayerColor::NEUTRAL; +} diff --git a/lib/SideInBattle.h b/lib/SideInBattle.h new file mode 100644 index 000000000..e2514e844 --- /dev/null +++ b/lib/SideInBattle.h @@ -0,0 +1,35 @@ +/* + * SideInBattle.h, part of VCMI engine + * + * Authors: listed in file AUTHORS in main folder + * + * License: GNU General Public License v2.0 or later + * Full text of license available in license.txt file, in main folder + * + */ +#pragma once +#include "GameConstants.h" + +class CGHeroInstance; +class CArmedInstance; + +struct DLL_LINKAGE SideInBattle +{ + PlayerColor color; + const CGHeroInstance *hero; //may be NULL if army is not commanded by hero + const CArmedInstance *armyObject; //adv. map object with army that participates in battle; may be same as hero + + ui8 castSpellsCount; //how many spells each side has cast this turn + std::vector usedSpellsHistory; //every time hero casts spell, it's inserted here -> eagle eye skill + si16 enchanterCounter; //tends to pass through 0, so sign is needed + + SideInBattle(); + void init(const CGHeroInstance *Hero, const CArmedInstance *Army); + + + template void serialize(Handler &h, const int version) + { + h & color & hero & armyObject; + h & castSpellsCount & usedSpellsHistory & enchanterCounter; + } +}; diff --git a/lib/SiegeInfo.cpp b/lib/SiegeInfo.cpp new file mode 100644 index 000000000..1d5bf7338 --- /dev/null +++ b/lib/SiegeInfo.cpp @@ -0,0 +1,35 @@ +/* + * SiegeInfo.cpp, part of VCMI engine + * + * Authors: listed in file AUTHORS in main folder + * + * License: GNU General Public License v2.0 or later + * Full text of license available in license.txt file, in main folder + * + */ +#include "StdInc.h" +#include "SiegeInfo.h" + + +SiegeInfo::SiegeInfo() +{ + for (int i = 0; i < wallState.size(); ++i) + { + wallState[i] = EWallState::NONE; + } + gateState = EGateState::NONE; +} + +EWallState::EWallState SiegeInfo::applyDamage(EWallState::EWallState state, unsigned int value) +{ + if (value == 0) + return state; + + switch (applyDamage(state, value - 1)) + { + case EWallState::INTACT : return EWallState::DAMAGED; + case EWallState::DAMAGED : return EWallState::DESTROYED; + case EWallState::DESTROYED : return EWallState::DESTROYED; + default: return EWallState::NONE; + } +} diff --git a/lib/SiegeInfo.h b/lib/SiegeInfo.h new file mode 100644 index 000000000..c0ceb1401 --- /dev/null +++ b/lib/SiegeInfo.h @@ -0,0 +1,28 @@ +/* + * SiegeInfo.h, part of VCMI engine + * + * Authors: listed in file AUTHORS in main folder + * + * License: GNU General Public License v2.0 or later + * Full text of license available in license.txt file, in main folder + * + */ +#pragma once +#include "GameConstants.h" + +//only for use in BattleInfo +struct DLL_LINKAGE SiegeInfo +{ + std::array wallState; + EGateState gateState; + + SiegeInfo(); + + // return EWallState decreased by value of damage points + static EWallState::EWallState applyDamage(EWallState::EWallState state, unsigned int value); + + template void serialize(Handler &h, const int version) + { + h & wallState & gateState; + } +}; diff --git a/lib/mapObjects/CGHeroInstance.cpp b/lib/mapObjects/CGHeroInstance.cpp index 60ff5cd2c..fcc131a1e 100644 --- a/lib/mapObjects/CGHeroInstance.cpp +++ b/lib/mapObjects/CGHeroInstance.cpp @@ -21,7 +21,7 @@ #include "../IGameCallback.h" #include "../CGameState.h" #include "../CCreatureHandler.h" -#include "../BattleState.h" +#include "../CStack.h" #include "../CTownHandler.h" #include "../mapping/CMap.h" #include "CGTownInstance.h" diff --git a/lib/registerTypes/RegisterTypes.cpp b/lib/registerTypes/RegisterTypes.cpp index a0f116094..95491ed1f 100644 --- a/lib/registerTypes/RegisterTypes.cpp +++ b/lib/registerTypes/RegisterTypes.cpp @@ -4,7 +4,6 @@ #include "../mapping/CMapInfo.h" #include "../StartInfo.h" -#include "../BattleState.h" #include "../CGameState.h" #include "../mapping/CMap.h" #include "../mapObjects/CObjectHandler.h" diff --git a/lib/registerTypes/TypesClientPacks1.cpp b/lib/registerTypes/TypesClientPacks1.cpp index 28986b6f8..7c756576f 100644 --- a/lib/registerTypes/TypesClientPacks1.cpp +++ b/lib/registerTypes/TypesClientPacks1.cpp @@ -3,7 +3,6 @@ #include "../mapping/CMapInfo.h" #include "../StartInfo.h" -#include "../BattleState.h" #include "../CGameState.h" #include "../mapping/CMap.h" #include "../CModHandler.h" diff --git a/lib/registerTypes/TypesClientPacks2.cpp b/lib/registerTypes/TypesClientPacks2.cpp index cf371cd13..1f91f0f80 100644 --- a/lib/registerTypes/TypesClientPacks2.cpp +++ b/lib/registerTypes/TypesClientPacks2.cpp @@ -3,7 +3,8 @@ #include "../mapping/CMapInfo.h" #include "../StartInfo.h" -#include "../BattleState.h" +#include "../CStack.h" +#include "../BattleInfo.h" #include "../CGameState.h" #include "../mapping/CMap.h" #include "../CModHandler.h" diff --git a/lib/registerTypes/TypesMapObjects1.cpp b/lib/registerTypes/TypesMapObjects1.cpp index d977a69e8..1dd0fb09f 100644 --- a/lib/registerTypes/TypesMapObjects1.cpp +++ b/lib/registerTypes/TypesMapObjects1.cpp @@ -3,7 +3,6 @@ #include "../mapping/CMapInfo.h" #include "../StartInfo.h" -#include "../BattleState.h" #include "../CGameState.h" #include "../mapping/CMap.h" #include "../CModHandler.h" diff --git a/lib/registerTypes/TypesMapObjects2.cpp b/lib/registerTypes/TypesMapObjects2.cpp index 67897b7a2..f06a180db 100644 --- a/lib/registerTypes/TypesMapObjects2.cpp +++ b/lib/registerTypes/TypesMapObjects2.cpp @@ -3,7 +3,8 @@ #include "../mapping/CMapInfo.h" #include "../StartInfo.h" -#include "../BattleState.h" +#include "../CStack.h" +#include "../BattleInfo.h" #include "../CGameState.h" #include "../mapping/CMap.h" #include "../CModHandler.h" diff --git a/lib/registerTypes/TypesMapObjects3.cpp b/lib/registerTypes/TypesMapObjects3.cpp index 3dcb08ba2..713e0c303 100644 --- a/lib/registerTypes/TypesMapObjects3.cpp +++ b/lib/registerTypes/TypesMapObjects3.cpp @@ -3,7 +3,6 @@ #include "../mapping/CMapInfo.h" #include "../StartInfo.h" -#include "../BattleState.h" #include "../CGameState.h" #include "../mapping/CMap.h" #include "../CModHandler.h" diff --git a/lib/registerTypes/TypesPregamePacks.cpp b/lib/registerTypes/TypesPregamePacks.cpp index 2fba9ac9e..22a64c813 100644 --- a/lib/registerTypes/TypesPregamePacks.cpp +++ b/lib/registerTypes/TypesPregamePacks.cpp @@ -3,7 +3,6 @@ #include "../mapping/CMapInfo.h" #include "../StartInfo.h" -#include "../BattleState.h" #include "../CGameState.h" #include "../mapping/CMap.h" #include "../CModHandler.h" diff --git a/lib/registerTypes/TypesServerPacks.cpp b/lib/registerTypes/TypesServerPacks.cpp index 291014c0b..7f19b9c30 100644 --- a/lib/registerTypes/TypesServerPacks.cpp +++ b/lib/registerTypes/TypesServerPacks.cpp @@ -3,7 +3,6 @@ #include "../mapping/CMapInfo.h" #include "../StartInfo.h" -#include "../BattleState.h" #include "../CGameState.h" #include "../mapping/CMap.h" #include "../CModHandler.h" diff --git a/lib/spells/BattleSpellMechanics.cpp b/lib/spells/BattleSpellMechanics.cpp index a3f197656..0ce425e6a 100644 --- a/lib/spells/BattleSpellMechanics.cpp +++ b/lib/spells/BattleSpellMechanics.cpp @@ -12,7 +12,8 @@ #include "BattleSpellMechanics.h" #include "../NetPacks.h" -#include "../BattleState.h" +#include "../CStack.h" +#include "../BattleInfo.h" #include "../mapObjects/CGHeroInstance.h" #include "../mapObjects/CGTownInstance.h" diff --git a/lib/spells/CDefaultSpellMechanics.cpp b/lib/spells/CDefaultSpellMechanics.cpp index 73848a6fb..df7322cc5 100644 --- a/lib/spells/CDefaultSpellMechanics.cpp +++ b/lib/spells/CDefaultSpellMechanics.cpp @@ -12,7 +12,8 @@ #include "CDefaultSpellMechanics.h" -#include "../BattleState.h" +#include "../CStack.h" +#include "../BattleInfo.h" #include "../CGeneralTextHandler.h" diff --git a/lib/spells/CSpellHandler.cpp b/lib/spells/CSpellHandler.cpp index 2dd96a2c5..1f8b0e249 100644 --- a/lib/spells/CSpellHandler.cpp +++ b/lib/spells/CSpellHandler.cpp @@ -22,7 +22,8 @@ #include "../CModHandler.h" #include "../StringConstants.h" -#include "../BattleState.h" +#include "../CStack.h" +#include "../BattleInfo.h" #include "../CBattleCallback.h" #include "../CGameState.h" //todo: remove diff --git a/lib/spells/CreatureSpellMechanics.cpp b/lib/spells/CreatureSpellMechanics.cpp index 2c8bd96d3..7ed5ace1e 100644 --- a/lib/spells/CreatureSpellMechanics.cpp +++ b/lib/spells/CreatureSpellMechanics.cpp @@ -13,7 +13,8 @@ #include "CreatureSpellMechanics.h" #include "../NetPacks.h" -#include "../BattleState.h" +#include "../CStack.h" +#include "../BattleInfo.h" ///AcidBreathDamageMechanics void AcidBreathDamageMechanics::applyBattleEffects(const SpellCastEnvironment * env, const BattleSpellCastParameters & parameters, SpellCastContext & ctx) const diff --git a/lib/spells/ISpellMechanics.cpp b/lib/spells/ISpellMechanics.cpp index 163cedf91..b5528cbe9 100644 --- a/lib/spells/ISpellMechanics.cpp +++ b/lib/spells/ISpellMechanics.cpp @@ -11,7 +11,9 @@ #include "StdInc.h" #include "ISpellMechanics.h" -#include "../BattleState.h" +#include "../CStack.h" +#include "../BattleInfo.h" + #include "../NetPacks.h" #include "CDefaultSpellMechanics.h" diff --git a/server/CGameHandler.cpp b/server/CGameHandler.cpp index c20b895f0..5d37819b7 100644 --- a/server/CGameHandler.cpp +++ b/server/CGameHandler.cpp @@ -15,7 +15,8 @@ #include "../lib/CTownHandler.h" #include "../lib/CCreatureHandler.h" #include "../lib/CGameState.h" -#include "../lib/BattleState.h" +#include "../lib/CStack.h" +#include "../lib/BattleInfo.h" #include "../lib/CondSh.h" #include "../lib/NetPacks.h" #include "../lib/VCMI_Lib.h" diff --git a/server/CQuery.cpp b/server/CQuery.cpp index 2801aef40..773283689 100644 --- a/server/CQuery.cpp +++ b/server/CQuery.cpp @@ -1,7 +1,7 @@ #include "StdInc.h" #include "CQuery.h" #include "CGameHandler.h" -#include "../lib/BattleState.h" +#include "../lib/BattleInfo.h" #include "../lib/mapObjects/MiscObjects.h" boost::mutex Queries::mx; diff --git a/server/NetPacksServer.cpp b/server/NetPacksServer.cpp index 8c56632a5..756f20486 100644 --- a/server/NetPacksServer.cpp +++ b/server/NetPacksServer.cpp @@ -5,7 +5,8 @@ #include "../lib/IGameCallback.h" #include "../lib/mapping/CMap.h" #include "../lib/CGameState.h" -#include "../lib/BattleState.h" +#include "../lib/CStack.h" +#include "../lib/BattleInfo.h" #include "../lib/BattleAction.h" #include "../lib/serializer/Connection.h"