diff --git a/AI/BattleAI/EnemyInfo.h b/AI/BattleAI/EnemyInfo.h index 66e2bddbe..f973aa3b7 100644 --- a/AI/BattleAI/EnemyInfo.h +++ b/AI/BattleAI/EnemyInfo.h @@ -9,7 +9,7 @@ */ #pragma once -#include "../../lib/BattleHex.h" +#include "../../lib/battle/BattleHex.h" class CStack; diff --git a/AI/StupidAI/StupidAI.h b/AI/StupidAI/StupidAI.h index 439a1d3cf..1b9a0b9ee 100644 --- a/AI/StupidAI/StupidAI.h +++ b/AI/StupidAI/StupidAI.h @@ -1,6 +1,6 @@ #pragma once -#include "../../lib/BattleHex.h" +#include "../../lib/battle/BattleHex.h" class CStupidAI : public CBattleGameInterface { diff --git a/CCallback.cpp b/CCallback.cpp index f79649627..23d76be44 100644 --- a/CCallback.cpp +++ b/CCallback.cpp @@ -18,7 +18,7 @@ #include "lib/GameConstants.h" #include "lib/CPlayerState.h" #include "lib/UnlockGuard.h" -#include "lib/BattleInfo.h" +#include "lib/battle/BattleInfo.h" /* * CCallback.cpp, part of VCMI engine diff --git a/client/Client.cpp b/client/Client.cpp index ba0adfdaa..ebb23c26f 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/BattleInfo.h" +#include "../lib/battle/BattleInfo.h" #include "../lib/CModHandler.h" #include "../lib/CArtHandler.h" #include "../lib/CGeneralTextHandler.h" diff --git a/client/Client.h b/client/Client.h index 7ecb861e4..39523dc94 100644 --- a/client/Client.h +++ b/client/Client.h @@ -2,7 +2,7 @@ #include "../lib/IGameCallback.h" -#include "../lib/BattleAction.h" +#include "../lib/battle/BattleAction.h" #include "../lib/CStopWatch.h" #include "../lib/int3.h" diff --git a/client/NetPacksClient.cpp b/client/NetPacksClient.cpp index 99bba1032..d39aa7bb4 100644 --- a/client/NetPacksClient.cpp +++ b/client/NetPacksClient.cpp @@ -25,7 +25,7 @@ #include "../lib/mapping/CCampaignHandler.h" #include "../lib/CGameState.h" #include "../lib/CStack.h" -#include "../lib/BattleInfo.h" +#include "../lib/battle/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 e70b9264e..08d2bf88a 100644 --- a/client/battle/CBattleAnimations.cpp +++ b/client/battle/CBattleAnimations.cpp @@ -944,7 +944,7 @@ bool CSpellEffectAnimation::init() { const CStack* destStack = owner->getCurrentPlayerInterface()->cb->battleGetStackByPos(destTile, false); - Rect &tilePos = owner->bfield[destTile]->pos; + Rect & tilePos = owner->bfield[destTile]->pos; BattleEffect be; be.effectID = ID; be.anim = CDefHandler::giveDef(customAnim); diff --git a/client/battle/CBattleAnimations.h b/client/battle/CBattleAnimations.h index 53d12fe47..424610c3b 100644 --- a/client/battle/CBattleAnimations.h +++ b/client/battle/CBattleAnimations.h @@ -1,6 +1,6 @@ #pragma once -#include "../../lib/BattleHex.h" +#include "../../lib/battle/BattleHex.h" #include "../widgets/Images.h" class CBattleInterface; diff --git a/client/battle/CBattleInterfaceClasses.h b/client/battle/CBattleInterfaceClasses.h index 2a1c0a1ec..2fc64631d 100644 --- a/client/battle/CBattleInterfaceClasses.h +++ b/client/battle/CBattleInterfaceClasses.h @@ -1,7 +1,7 @@ #pragma once #include "../gui/CIntObject.h" -#include "../../lib/BattleHex.h" +#include "../../lib/battle/BattleHex.h" #include "../windows/CWindowObject.h" struct SDL_Surface; diff --git a/lib/CBattleCallback.h b/lib/CBattleCallback.h deleted file mode 100644 index 7ae560afe..000000000 --- a/lib/CBattleCallback.h +++ /dev/null @@ -1,341 +0,0 @@ -#pragma once -#include "BattleHex.h" - -/* - * CBattleCallback.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 - * - */ - -class CGameState; -class CGTownInstance; -class CGHeroInstance; -class CStack; -class ISpellCaster; -class CSpell; -struct BattleInfo; -struct CObstacleInstance; -class IBonusBearer; -struct InfoAboutHero; -class CArmedInstance; -class CRandomGenerator; - -namespace boost -{class shared_mutex;} - -namespace BattleSide -{ - enum {ATTACKER = 0, DEFENDER = 1}; -} - -typedef std::vector TStacks; -typedef std::function TStackFilter; - -class CBattleInfoEssentials; - -//Basic class for various callbacks (interfaces called by players to get info about game and so forth) -class DLL_LINKAGE CCallbackBase -{ - const BattleInfo *battle; //battle to which the player is engaged, nullptr if none or not applicable - - const BattleInfo * getBattle() const - { - return battle; - } - -protected: - CGameState *gs; - boost::optional player; // not set gives access to all information, otherwise callback provides only information "visible" for player - - CCallbackBase(CGameState *GS, boost::optional Player) - : battle(nullptr), gs(GS), player(Player) - {} - CCallbackBase() - : battle(nullptr), gs(nullptr) - {} - - void setBattle(const BattleInfo *B); - bool duringBattle() const; - -public: - boost::optional getPlayerID() const; - - friend class CBattleInfoEssentials; -}; - - -struct DLL_LINKAGE AttackableTiles -{ - std::set hostileCreaturePositions; - std::set friendlyCreaturePositions; //for Dragon Breath - template void serialize(Handler &h, const int version) - { - h & hostileCreaturePositions & friendlyCreaturePositions; - } -}; - -//Accessibility is property of hex in battle. It doesn't depend on stack, side's perspective and so on. -namespace EAccessibility -{ - enum EAccessibility - { - ACCESSIBLE, - ALIVE_STACK, - OBSTACLE, - DESTRUCTIBLE_WALL, - GATE, //sieges -> gate opens only for defender stacks - UNAVAILABLE, //indestructible wall parts, special battlefields (like boat-to-boat) - SIDE_COLUMN //used for first and last columns of hexes that are unavailable but wat machines can stand there - }; -} - -typedef std::array TAccessibilityArray; - -struct DLL_LINKAGE AccessibilityInfo : TAccessibilityArray -{ - bool occupiable(const CStack *stack, BattleHex tile) const; - bool accessible(BattleHex tile, const CStack *stack) const; //checks for both tiles if stack is double wide - bool accessible(BattleHex tile, bool doubleWide, bool attackerOwned) const; //checks for both tiles if stack is double wide -}; - -namespace BattlePerspective -{ - enum BattlePerspective - { - INVALID = -2, - ALL_KNOWING = -1, - LEFT_SIDE, - RIGHT_SIDE - }; -} - -// Reachability info is result of BFS calculation. It's dependent on stack (it's owner, whether it's flying), -// startPosition and perpective. -struct DLL_LINKAGE ReachabilityInfo -{ - typedef std::array TDistances; - typedef std::array TPredecessors; - - enum { INFINITE_DIST = 1000000 }; - - struct DLL_LINKAGE Parameters - { - const CStack *stack; //stack for which calculation is mage => not required (kept for debugging mostly), following variables are enough - - bool attackerOwned; - bool doubleWide; - bool flying; - std::vector knownAccessible; //hexes that will be treated as accessible, even if they're occupied by stack (by default - tiles occupied by stack we do reachability for, so it doesn't block itself) - - BattleHex startPosition; //assumed position of stack - BattlePerspective::BattlePerspective perspective; //some obstacles (eg. quicksands) may be invisible for some side - - Parameters(); - Parameters(const CStack *Stack); - }; - - Parameters params; - AccessibilityInfo accessibility; - TDistances distances; - TPredecessors predecessors; - - ReachabilityInfo() - { - distances.fill(INFINITE_DIST); - predecessors.fill(BattleHex::INVALID); - } - - bool isReachable(BattleHex hex) const - { - return distances[hex] < INFINITE_DIST; - } -}; - -class DLL_LINKAGE CBattleInfoEssentials : public virtual CCallbackBase -{ -protected: - bool battleDoWeKnowAbout(ui8 side) const; - const IBonusBearer * getBattleNode() const; -public: - enum EStackOwnership - { - ONLY_MINE, ONLY_ENEMY, MINE_AND_ENEMY - }; - - BattlePerspective::BattlePerspective battleGetMySide() const; - - ETerrainType battleTerrainType() const; - BFieldType battleGetBattlefieldType() const; - std::vector > battleGetAllObstacles(boost::optional perspective = boost::none) const; //returns all obstacles on the battlefield - - /** @brief Main method for getting battle stacks - * - * @param predicate Functor that shall return true for desired stack - * @return filtered stacks - * - */ - TStacks battleGetStacksIf(TStackFilter predicate) const; - - bool battleHasNativeStack(ui8 side) const; - int battleGetMoatDmg() const; //what dmg unit will suffer if ending turn in the moat - const CGTownInstance * battleGetDefendedTown() const; //returns defended town if current battle is a siege, nullptr instead - const CStack *battleActiveStack() const; - si8 battleTacticDist() const; //returns tactic distance in current tactics phase; 0 if not in tactics phase - si8 battleGetTacticsSide() const; //returns which side is in tactics phase, undefined if none (?) - bool battleCanFlee(PlayerColor player) const; - bool battleCanSurrender(PlayerColor player) const; - si8 playerToSide(PlayerColor player) const; - bool playerHasAccessToHeroInfo(PlayerColor player, const CGHeroInstance * h) const; - ui8 battleGetSiegeLevel() const; //returns 0 when there is no siege, 1 if fort, 2 is citadel, 3 is castle - bool battleHasHero(ui8 side) const; - int battleCastSpells(ui8 side) const; //how many spells has given side cast - const CGHeroInstance * battleGetFightingHero(ui8 side) const; //depracated for players callback, easy to get wrong - const CArmedInstance * battleGetArmyObject(ui8 side) const; - InfoAboutHero battleGetHeroInfo(ui8 side) const; - - // for determining state of a part of the wall; format: parameter [0] - keep, [1] - bottom tower, [2] - bottom wall, - // [3] - below gate, [4] - over gate, [5] - upper wall, [6] - uppert tower, [7] - gate; returned value: 1 - intact, 2 - damaged, 3 - destroyed; 0 - no battle - si8 battleGetWallState(int partOfWall) const; - EGateState battleGetGateState() const; - - //helpers - ///returns all stacks, alive or dead or undead or mechanical :) - TStacks battleGetAllStacks(bool includeTurrets = false) const; - - ///returns all alive stacks excluding turrets - TStacks battleAliveStacks() const; - ///returns all alive stacks from particular side excluding turrets - TStacks battleAliveStacks(ui8 side) const; - const CStack * battleGetStackByID(int ID, bool onlyAlive = true) const; //returns stack info by given ID - bool battleIsObstacleVisibleForSide(const CObstacleInstance & coi, BattlePerspective::BattlePerspective side) const; - - ///returns player that controls given stack; mind control included - PlayerColor battleGetOwner(const CStack * stack) const; - - ///returns hero that controls given stack; nullptr if none; mind control included - const CGHeroInstance * battleGetOwnerHero(const CStack * stack) const; - - ///check that stacks are controlled by same|other player(s) depending on positiveness - ///mind control included - bool battleMatchOwner(const CStack * attacker, const CStack * defender, const boost::logic::tribool positivness = false) const; -}; - -struct DLL_LINKAGE BattleAttackInfo -{ - const IBonusBearer *attackerBonuses, *defenderBonuses; - const CStack *attacker, *defender; - BattleHex attackerPosition, defenderPosition; - - int attackerCount, defenderCount; - bool shooting; - int chargedFields; - - bool luckyHit; - bool unluckyHit; - bool deathBlow; - bool ballistaDoubleDamage; - - BattleAttackInfo(const CStack *Attacker, const CStack *Defender, bool Shooting = false); - BattleAttackInfo reverse() const; -}; - -class DLL_LINKAGE CBattleInfoCallback : public virtual CBattleInfoEssentials -{ -public: - enum ERandomSpell - { - RANDOM_GENIE, RANDOM_AIMED - }; - - //battle - boost::optional battleIsFinished() const; //return none if battle is ongoing; otherwise the victorious side (0/1) or 2 if it is a draw - - std::shared_ptr battleGetObstacleOnPos(BattleHex tile, bool onlyBlocking = true) const; //blocking obstacles makes tile inaccessible, others cause special effects (like Land Mines, Moat, Quicksands) - const CStack * battleGetStackByPos(BattleHex pos, bool onlyAlive = true) const; //returns stack info by given pos - void battleGetStackQueue(std::vector &out, const int howMany, const int turn = 0, int lastMoved = -1) const; - void battleGetStackCountOutsideHexes(bool *ac) const; // returns hexes which when in front of a stack cause us to move the amount box back - - - std::vector battleGetAvailableHexes(const CStack * stack, bool addOccupiable, std::vector * attackable = nullptr) const; //returns hexes reachable by creature with id ID (valid movement destinations), DOES contain stack current position - - int battleGetSurrenderCost(PlayerColor Player) const; //returns cost of surrendering battle, -1 if surrendering is not possible - ReachabilityInfo::TDistances battleGetDistances(const CStack * stack, BattleHex hex = BattleHex::INVALID, BattleHex * predecessors = nullptr) const; //returns vector of distances to [dest hex number] - std::set battleGetAttackedHexes(const CStack* attacker, BattleHex destinationTile, BattleHex attackerPos = BattleHex::INVALID) const; - - bool battleCanAttack(const CStack * stack, const CStack * target, BattleHex dest) const; //determines if stack with given ID can attack target at the selected destination - bool battleCanShoot(const CStack * stack, BattleHex dest) const; //determines if stack with given ID shoot at the selected destination - bool battleIsStackBlocked(const CStack * stack) const; //returns true if there is neighboring enemy stack - std::set batteAdjacentCreatures (const CStack * stack) const; - - TDmgRange calculateDmgRange(const BattleAttackInfo &info) const; //charge - number of hexes travelled before attack (for champion's jousting); returns pair - TDmgRange calculateDmgRange(const CStack* attacker, const CStack* defender, TQuantity attackerCount, bool shooting, ui8 charge, bool lucky, bool unlucky, bool deathBlow, bool ballistaDoubleDmg) const; //charge - number of hexes travelled before attack (for champion's jousting); returns pair - TDmgRange calculateDmgRange(const CStack* attacker, const CStack* defender, bool shooting, ui8 charge, bool lucky, bool unlucky, bool deathBlow, bool ballistaDoubleDmg) const; //charge - number of hexes travelled before attack (for champion's jousting); returns pair - - //hextowallpart //int battleGetWallUnderHex(BattleHex hex) const; //returns part of destructible wall / gate / keep under given hex or -1 if not found - std::pair battleEstimateDamage(CRandomGenerator & rand, const BattleAttackInfo &bai, std::pair * retaliationDmg = nullptr) const; //estimates damage dealt by attacker to defender; it may be not precise especially when stack has randomly working bonuses; returns pair - std::pair battleEstimateDamage(CRandomGenerator & rand, const CStack * attacker, const CStack * defender, std::pair * retaliationDmg = nullptr) const; //estimates damage dealt by attacker to defender; it may be not precise especially when stack has randomly working bonuses; returns pair - si8 battleHasDistancePenalty( const CStack * stack, BattleHex destHex ) const; - si8 battleHasDistancePenalty(const IBonusBearer *bonusBearer, BattleHex shooterPosition, BattleHex destHex ) const; - si8 battleHasWallPenalty(const CStack * stack, BattleHex destHex) const; //checks if given stack has wall penalty - si8 battleHasWallPenalty(const IBonusBearer *bonusBearer, BattleHex shooterPosition, BattleHex destHex) const; //checks if given stack has wall penalty - - BattleHex wallPartToBattleHex(EWallPart::EWallPart part) const; - EWallPart::EWallPart battleHexToWallPart(BattleHex hex) const; //returns part of destructible wall / gate / keep under given hex or -1 if not found - bool isWallPartPotentiallyAttackable(EWallPart::EWallPart wallPart) const; // returns true if the wall part is potentially attackable (independent of wall state), false if not - std::vector getAttackableBattleHexes() const; - - //*** MAGIC - si8 battleMaxSpellLevel(ui8 side) const; //calculates minimum spell level possible to be cast on battlefield - takes into account artifacts of both heroes; if no effects are set, 0 is returned - ui32 battleGetSpellCost(const CSpell * sp, const CGHeroInstance * caster) const; //returns cost of given spell - ESpellCastProblem::ESpellCastProblem battleCanCastSpell(const ISpellCaster * caster, ECastingMode::ECastingMode mode) const; //returns true if there are no general issues preventing from casting a spell - - SpellID battleGetRandomStackSpell(CRandomGenerator & rand, const CStack * stack, ERandomSpell mode) const; - SpellID getRandomBeneficialSpell(CRandomGenerator & rand, const CStack * subject) const; - SpellID getRandomCastedSpell(CRandomGenerator & rand, const CStack * caster) const; //called at the beginning of turn for Faerie Dragon - - const CStack * getStackIf(std::function pred) const; - - si8 battleHasShootingPenalty(const CStack * stack, BattleHex destHex) - { - return battleHasDistancePenalty(stack, destHex) || battleHasWallPenalty(stack, destHex); - } - si8 battleCanTeleportTo(const CStack * stack, BattleHex destHex, int telportLevel) const; //checks if teleportation of given stack to given position can take place - - - //convenience methods using the ones above - bool isInTacticRange( BattleHex dest ) const; - si8 battleGetTacticDist() const; //returns tactic distance for calling player or 0 if this player is not in tactic phase (for ALL_KNOWING actual distance for tactic side) - - AttackableTiles getPotentiallyAttackableHexes(const CStack* attacker, BattleHex destinationTile, BattleHex attackerPos) const; //TODO: apply rotation to two-hex attacker - std::set getAttackedCreatures(const CStack* attacker, BattleHex destinationTile, BattleHex attackerPos = BattleHex::INVALID) const; //calculates range of multi-hex attacks - bool isToReverse(BattleHex hexFrom, BattleHex hexTo, bool curDir /*if true, creature is in attacker's direction*/, bool toDoubleWide, bool toDir) const; //determines if creature should be reversed (it stands on hexFrom and should 'see' hexTo) - bool isToReverseHlp(BattleHex hexFrom, BattleHex hexTo, bool curDir) const; //helper for isToReverse - - ReachabilityInfo getReachability(const CStack *stack) const; - ReachabilityInfo getReachability(const ReachabilityInfo::Parameters ¶ms) const; - AccessibilityInfo getAccesibility() const; - AccessibilityInfo getAccesibility(const CStack *stack) const; //Hexes ocupied by stack will be marked as accessible. - AccessibilityInfo getAccesibility(const std::vector &accessibleHexes) const; //given hexes will be marked as accessible - std::pair getNearestStack(const CStack * closest, boost::logic::tribool attackerOwned) const; -protected: - ReachabilityInfo getFlyingReachability(const ReachabilityInfo::Parameters ¶ms) const; - ReachabilityInfo makeBFS(const AccessibilityInfo &accessibility, const ReachabilityInfo::Parameters ¶ms) const; - ReachabilityInfo makeBFS(const CStack *stack) const; //uses default parameters -> stack position and owner's perspective - std::set getStoppers(BattlePerspective::BattlePerspective whichSidePerspective) const; //get hexes with stopping obstacles (quicksands) -}; - -class DLL_LINKAGE CPlayerBattleCallback : public CBattleInfoCallback -{ -public: - bool battleCanFlee() const; //returns true if caller can flee from the battle - TStacks battleGetStacks(EStackOwnership whose = MINE_AND_ENEMY, bool onlyAlive = true) const; //returns stacks on battlefield - - int battleGetSurrenderCost() const; //returns cost of surrendering battle, -1 if surrendering is not possible - - const CGHeroInstance * battleGetMyHero() const; - InfoAboutHero battleGetEnemyHero() const; -}; diff --git a/lib/CGameInfoCallback.cpp b/lib/CGameInfoCallback.cpp index 28fd0b988..a31685412 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 "BattleInfo.h" // for BattleInfo +#include "battle/BattleInfo.h" // for BattleInfo #include "NetPacks.h" // for InfoWindow #include "CModHandler.h" #include "spells/CSpellHandler.h" diff --git a/lib/CGameInfoCallback.h b/lib/CGameInfoCallback.h index 4d2903657..16fa777be 100644 --- a/lib/CGameInfoCallback.h +++ b/lib/CGameInfoCallback.h @@ -1,7 +1,7 @@ #pragma once #include "ResourceSet.h" // for Res::ERes -#include "CBattleCallback.h" //for CCallbackBase +#include "battle/CPlayerBattleCallback.h" /* * CGameInfoCallback.h, part of VCMI engine diff --git a/lib/CGameInterface.h b/lib/CGameInterface.h index 08e293a63..86f60d373 100644 --- a/lib/CGameInterface.h +++ b/lib/CGameInterface.h @@ -1,7 +1,7 @@ #pragma once -#include "BattleAction.h" +#include "battle/BattleAction.h" #include "IGameEventsReceiver.h" #include "CGameStateFwd.h" diff --git a/lib/CGameState.cpp b/lib/CGameState.cpp index a1b8a6179..a0d4405d4 100644 --- a/lib/CGameState.cpp +++ b/lib/CGameState.cpp @@ -16,7 +16,7 @@ #include "StartInfo.h" #include "NetPacks.h" #include "registerTypes/RegisterTypes.h" -#include "BattleInfo.h" +#include "battle/BattleInfo.h" #include "JsonNode.h" #include "filesystem/Filesystem.h" #include "GameConstants.h" diff --git a/lib/CHeroHandler.cpp b/lib/CHeroHandler.cpp index 1489dc349..3dc8ff2db 100644 --- a/lib/CHeroHandler.cpp +++ b/lib/CHeroHandler.cpp @@ -6,7 +6,7 @@ #include "VCMI_Lib.h" #include "JsonNode.h" #include "StringConstants.h" -#include "BattleHex.h" +#include "battle/BattleHex.h" #include "CCreatureHandler.h" #include "CModHandler.h" #include "CTownHandler.h" diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt index ad0ee0b98..6ca76f88d 100644 --- a/lib/CMakeLists.txt +++ b/lib/CMakeLists.txt @@ -76,14 +76,20 @@ set(lib_SRCS spells/CDefaultSpellMechanics.cpp spells/ViewSpellInt.cpp - BattleAction.cpp - BattleHex.cpp - BattleInfo.cpp - SiegeInfo.cpp - SideInBattle.cpp - CStack.cpp + battle/BattleAction.cpp + battle/BattleHex.cpp + battle/BattleInfo.cpp + battle/AccessibilityInfo.cpp + battle/BattleAttackInfo.cpp + battle/CBattleInfoCallback.cpp + battle/CBattleInfoEssentials.cpp + battle/CCallbackBase.cpp + battle/CPlayerBattleCallback.cpp + battle/ReachabilityInfo.cpp + SiegeInfo.cpp + SideInBattle.cpp + CStack.cpp CArtHandler.cpp - CBattleCallback.cpp CBonusTypeHandler.cpp CBuildingHandler.cpp CConfigHandler.cpp @@ -94,7 +100,7 @@ set(lib_SRCS CGeneralTextHandler.cpp CHeroHandler.cpp CModHandler.cpp - CObstacleInstance.cpp + battle/CObstacleInstance.cpp CRandomGenerator.cpp CThreadHelper.cpp diff --git a/lib/CStack.cpp b/lib/CStack.cpp index 3736dddfc..bfc613e47 100644 --- a/lib/CStack.cpp +++ b/lib/CStack.cpp @@ -9,7 +9,7 @@ */ #include "StdInc.h" #include "CStack.h" -#include "BattleInfo.h" +#include "battle/BattleInfo.h" #include "spells/CSpellHandler.h" #include "CRandomGenerator.h" #include "NetPacks.h" diff --git a/lib/CStack.h b/lib/CStack.h index 86f1bde8f..2edc9c341 100644 --- a/lib/CStack.h +++ b/lib/CStack.h @@ -8,7 +8,7 @@ * */ #pragma once -#include "BattleHex.h" +#include "battle/BattleHex.h" #include "CCreatureHandler.h" #include "mapObjects/CGHeroInstance.h" // for commander serialization diff --git a/lib/CTownHandler.h b/lib/CTownHandler.h index c08be54c9..f823d6328 100644 --- a/lib/CTownHandler.h +++ b/lib/CTownHandler.h @@ -6,7 +6,7 @@ #include "GameConstants.h" #include "IHandlerBase.h" #include "LogicalExpression.h" -#include "BattleHex.h" +#include "battle/BattleHex.h" /* * CTownHandler.h, part of VCMI engine diff --git a/lib/IGameEventsReceiver.h b/lib/IGameEventsReceiver.h index 472599a72..cfa5e619f 100644 --- a/lib/IGameEventsReceiver.h +++ b/lib/IGameEventsReceiver.h @@ -10,7 +10,7 @@ #pragma once -#include "BattleHex.h" +#include "battle/BattleHex.h" #include "int3.h" class CGTownInstance; diff --git a/lib/NetPacks.h b/lib/NetPacks.h index 700e43d7b..8f162047c 100644 --- a/lib/NetPacks.h +++ b/lib/NetPacks.h @@ -2,14 +2,14 @@ #include "NetPacksBase.h" -#include "BattleAction.h" +#include "battle/BattleAction.h" #include "mapObjects/CGHeroInstance.h" #include "ConstTransitivePtr.h" #include "int3.h" #include "ResourceSet.h" #include "CGameStateFwd.h" #include "mapping/CMapDefines.h" -#include "CObstacleInstance.h" +#include "battle/CObstacleInstance.h" #include "spells/ViewSpellInt.h" diff --git a/lib/NetPacksLib.cpp b/lib/NetPacksLib.cpp index b49f91bae..7ba96e5a5 100644 --- a/lib/NetPacksLib.cpp +++ b/lib/NetPacksLib.cpp @@ -13,7 +13,7 @@ #include "CCreatureHandler.h" #include "CGameState.h" #include "CStack.h" -#include "BattleInfo.h" +#include "battle/BattleInfo.h" #include "CTownHandler.h" #include "mapping/CMapInfo.h" #include "StartInfo.h" diff --git a/lib/VCMI_lib.cbp b/lib/VCMI_lib.cbp index 0d7369d8f..f0c645d65 100644 --- a/lib/VCMI_lib.cbp +++ b/lib/VCMI_lib.cbp @@ -125,16 +125,28 @@ - - - - - - + + + + + + + + + + + + + + + + + + + + - - @@ -161,8 +173,8 @@ - - + + diff --git a/lib/VCMI_lib.vcxproj b/lib/VCMI_lib.vcxproj index 3afdf7fea..fe4756448 100644 --- a/lib/VCMI_lib.vcxproj +++ b/lib/VCMI_lib.vcxproj @@ -168,9 +168,16 @@ - - - + + + + + + + + + + @@ -183,7 +190,7 @@ - + @@ -262,7 +269,6 @@ - @@ -290,9 +296,16 @@ - - - + + + + + + + + + + @@ -306,7 +319,7 @@ - + @@ -376,7 +389,6 @@ - @@ -425,4 +437,4 @@ - \ No newline at end of file + diff --git a/lib/VCMI_lib.vcxproj.filters b/lib/VCMI_lib.vcxproj.filters index 803970053..3262ffbc5 100644 --- a/lib/VCMI_lib.vcxproj.filters +++ b/lib/VCMI_lib.vcxproj.filters @@ -31,7 +31,7 @@ - + @@ -49,14 +49,20 @@ - + - + - + + + + + + + @@ -263,7 +269,7 @@ - + @@ -317,10 +323,10 @@ Header Files - + Header Files - + Header Files @@ -344,7 +350,7 @@ Header Files - + Header Files @@ -380,7 +386,25 @@ Header Files - + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + Header Files @@ -656,7 +680,7 @@ Header Files - + Header Files diff --git a/lib/battle/AccessibilityInfo.cpp b/lib/battle/AccessibilityInfo.cpp new file mode 100644 index 000000000..b45ed9ca8 --- /dev/null +++ b/lib/battle/AccessibilityInfo.cpp @@ -0,0 +1,51 @@ +/* + * AccessibilityInfo.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 "AccessibilityInfo.h" +#include "CStack.h" + +bool AccessibilityInfo::accessible(BattleHex tile, const CStack * stack) const +{ + return accessible(tile, stack->doubleWide(), stack->attackerOwned); +} + +bool AccessibilityInfo::accessible(BattleHex tile, bool doubleWide, bool attackerOwned) const +{ + // All hexes that stack would cover if standing on tile have to be accessible. + for(auto hex : CStack::getHexes(tile, doubleWide, attackerOwned)) + { + // If the hex is out of range then the tile isn't accessible + if(!hex.isValid()) + return false; + // If we're no defender which step on gate and the hex isn't accessible, then the tile + // isn't accessible + else if(at(hex) != EAccessibility::ACCESSIBLE && + !(at(hex) == EAccessibility::GATE && !attackerOwned)) + { + return false; + } + } + return true; +} + +bool AccessibilityInfo::occupiable(const CStack * stack, BattleHex tile) const +{ + //obviously, we can occupy tile by standing on it + if(accessible(tile, stack)) + return true; + if(stack->doubleWide()) + { + //Check the tile next to -> if stack stands there, it'll also occupy considered hex + const BattleHex anotherTile = tile + (stack->attackerOwned ? BattleHex::RIGHT : BattleHex::LEFT); + if(accessible(anotherTile, stack)) + return true; + } + return false; +} diff --git a/lib/battle/AccessibilityInfo.h b/lib/battle/AccessibilityInfo.h new file mode 100644 index 000000000..32f8b14d3 --- /dev/null +++ b/lib/battle/AccessibilityInfo.h @@ -0,0 +1,35 @@ +/* + * AccessibilityInfo.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" + +class CStack; + +//Accessibility is property of hex in battle. It doesn't depend on stack, side's perspective and so on. +enum class EAccessibility +{ + ACCESSIBLE, + ALIVE_STACK, + OBSTACLE, + DESTRUCTIBLE_WALL, + GATE, //sieges -> gate opens only for defender stacks + UNAVAILABLE, //indestructible wall parts, special battlefields (like boat-to-boat) + SIDE_COLUMN //used for first and last columns of hexes that are unavailable but wat machines can stand there +}; + + +typedef std::array TAccessibilityArray; + +struct DLL_LINKAGE AccessibilityInfo : TAccessibilityArray +{ + bool occupiable(const CStack * stack, BattleHex tile) const; + bool accessible(BattleHex tile, const CStack * stack) const; //checks for both tiles if stack is double wide + bool accessible(BattleHex tile, bool doubleWide, bool attackerOwned) const; //checks for both tiles if stack is double wide +}; diff --git a/lib/BattleAction.cpp b/lib/battle/BattleAction.cpp similarity index 76% rename from lib/BattleAction.cpp rename to lib/battle/BattleAction.cpp index 4495a6e76..d08517401 100644 --- a/lib/BattleAction.cpp +++ b/lib/battle/BattleAction.cpp @@ -1,9 +1,3 @@ -#include "StdInc.h" -#include "BattleAction.h" - -#include "CStack.h" - - /* * BattleAction.cpp, part of VCMI engine * @@ -14,6 +8,10 @@ * */ +#include "StdInc.h" +#include "BattleAction.h" +#include "CStack.h" + using namespace Battle; BattleAction::BattleAction(): @@ -26,7 +24,7 @@ BattleAction::BattleAction(): { } -BattleAction BattleAction::makeHeal(const CStack *healer, const CStack *healed) +BattleAction BattleAction::makeHeal(const CStack * healer, const CStack * healed) { BattleAction ba; ba.side = !healer->attackerOwned; @@ -36,7 +34,7 @@ BattleAction BattleAction::makeHeal(const CStack *healer, const CStack *healed) return ba; } -BattleAction BattleAction::makeDefend(const CStack *stack) +BattleAction BattleAction::makeDefend(const CStack * stack) { BattleAction ba; ba.side = !stack->attackerOwned; @@ -46,7 +44,7 @@ BattleAction BattleAction::makeDefend(const CStack *stack) } -BattleAction BattleAction::makeMeleeAttack(const CStack *stack, const CStack * attacked, BattleHex attackFrom /*= BattleHex::INVALID*/) +BattleAction BattleAction::makeMeleeAttack(const CStack * stack, const CStack * attacked, BattleHex attackFrom /*= BattleHex::INVALID*/) { BattleAction ba; ba.side = !stack->attackerOwned; @@ -57,7 +55,7 @@ BattleAction BattleAction::makeMeleeAttack(const CStack *stack, const CStack * a return ba; } -BattleAction BattleAction::makeWait(const CStack *stack) +BattleAction BattleAction::makeWait(const CStack * stack) { BattleAction ba; ba.side = !stack->attackerOwned; @@ -66,7 +64,7 @@ BattleAction BattleAction::makeWait(const CStack *stack) return ba; } -BattleAction BattleAction::makeShotAttack(const CStack *shooter, const CStack *target) +BattleAction BattleAction::makeShotAttack(const CStack * shooter, const CStack * target) { BattleAction ba; ba.side = !shooter->attackerOwned; @@ -76,7 +74,7 @@ BattleAction BattleAction::makeShotAttack(const CStack *shooter, const CStack *t return ba; } -BattleAction BattleAction::makeMove(const CStack *stack, BattleHex dest) +BattleAction BattleAction::makeMove(const CStack * stack, BattleHex dest) { BattleAction ba; ba.side = !stack->attackerOwned; diff --git a/lib/BattleAction.h b/lib/battle/BattleAction.h similarity index 65% rename from lib/BattleAction.h rename to lib/battle/BattleAction.h index 255b8c7b4..e2ae825ef 100644 --- a/lib/BattleAction.h +++ b/lib/battle/BattleAction.h @@ -1,8 +1,3 @@ -#pragma once - - -#include "BattleHex.h" - /* * BattleAction.h, part of VCMI engine * @@ -12,14 +7,16 @@ * Full text of license available in license.txt file, in main folder * */ +#pragma once +#include "BattleHex.h" -/// A struct which handles battle actions like defending, walking,... - represents a creature stack in a battle class CStack; +/// A struct which handles battle actions like defending, walking,... - represents a creature stack in a battle struct DLL_LINKAGE BattleAction { ui8 side; //who made this action: false - left, true - right player - ui32 stackNumber;//stack ID, -1 left hero, -2 right hero, + ui32 stackNumber; //stack ID, -1 left hero, -2 right hero, Battle::ActionType actionType; //use ActionType enum for values BattleHex destinationTile; si32 additionalInfo; // e.g. spell number if type is 1 || 10; tile to attack if type is 6 @@ -32,12 +29,12 @@ struct DLL_LINKAGE BattleAction BattleAction(); - static BattleAction makeHeal(const CStack *healer, const CStack *healed); - static BattleAction makeDefend(const CStack *stack); - static BattleAction makeWait(const CStack *stack); - static BattleAction makeMeleeAttack(const CStack *stack, const CStack * attacked, BattleHex attackFrom = BattleHex::INVALID); - static BattleAction makeShotAttack(const CStack *shooter, const CStack *target); - static BattleAction makeMove(const CStack *stack, BattleHex dest); + static BattleAction makeHeal(const CStack * healer, const CStack * healed); + static BattleAction makeDefend(const CStack * stack); + static BattleAction makeWait(const CStack * stack); + static BattleAction makeMeleeAttack(const CStack * stack, const CStack * attacked, BattleHex attackFrom = BattleHex::INVALID); + static BattleAction makeShotAttack(const CStack * shooter, const CStack * target); + static BattleAction makeMove(const CStack * stack, BattleHex dest); static BattleAction makeEndOFTacticPhase(ui8 side); }; diff --git a/lib/battle/BattleAttackInfo.cpp b/lib/battle/BattleAttackInfo.cpp new file mode 100644 index 000000000..05fd2c477 --- /dev/null +++ b/lib/battle/BattleAttackInfo.cpp @@ -0,0 +1,51 @@ +/* + * BattleAttackInfo.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 "BattleAttackInfo.h" +#include "CStack.h" + + +BattleAttackInfo::BattleAttackInfo(const CStack * Attacker, const CStack * Defender, bool Shooting) +{ + attacker = Attacker; + defender = Defender; + + attackerBonuses = Attacker; + defenderBonuses = Defender; + + attackerPosition = Attacker->position; + defenderPosition = Defender->position; + + attackerCount = Attacker->count; + defenderCount = Defender->count; + + shooting = Shooting; + chargedFields = 0; + + luckyHit = false; + unluckyHit = false; + deathBlow = false; + ballistaDoubleDamage = false; +} + +BattleAttackInfo BattleAttackInfo::reverse() const +{ + BattleAttackInfo ret = *this; + std::swap(ret.attacker, ret.defender); + std::swap(ret.attackerBonuses, ret.defenderBonuses); + std::swap(ret.attackerPosition, ret.defenderPosition); + std::swap(ret.attackerCount, ret.defenderCount); + + ret.shooting = false; + ret.chargedFields = 0; + ret.luckyHit = ret.ballistaDoubleDamage = ret.deathBlow = false; + + return ret; +} diff --git a/lib/battle/BattleAttackInfo.h b/lib/battle/BattleAttackInfo.h new file mode 100644 index 000000000..7ced108e1 --- /dev/null +++ b/lib/battle/BattleAttackInfo.h @@ -0,0 +1,33 @@ +/* + * BattleAttackInfo.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" + +class CStack; +class IBonusBearer; + +struct DLL_LINKAGE BattleAttackInfo +{ + const IBonusBearer *attackerBonuses, *defenderBonuses; + const CStack *attacker, *defender; + BattleHex attackerPosition, defenderPosition; + + int attackerCount, defenderCount; + bool shooting; + int chargedFields; + + bool luckyHit; + bool unluckyHit; + bool deathBlow; + bool ballistaDoubleDamage; + + BattleAttackInfo(const CStack * Attacker, const CStack * Defender, bool Shooting = false); + BattleAttackInfo reverse() const; +}; diff --git a/lib/BattleHex.cpp b/lib/battle/BattleHex.cpp similarity index 100% rename from lib/BattleHex.cpp rename to lib/battle/BattleHex.cpp diff --git a/lib/BattleHex.h b/lib/battle/BattleHex.h similarity index 95% rename from lib/BattleHex.h rename to lib/battle/BattleHex.h index a5a3a0be3..18ba8d599 100644 --- a/lib/BattleHex.h +++ b/lib/battle/BattleHex.h @@ -8,7 +8,7 @@ * */ #pragma once -#include "GameConstants.h" +#include "../GameConstants.h" // for battle stacks' positions struct DLL_LINKAGE BattleHex //TODO: decide if this should be changed to class for better code design diff --git a/lib/BattleInfo.cpp b/lib/battle/BattleInfo.cpp similarity index 92% rename from lib/BattleInfo.cpp rename to lib/battle/BattleInfo.cpp index 9f914a460..518491b96 100644 --- a/lib/BattleInfo.cpp +++ b/lib/battle/BattleInfo.cpp @@ -57,7 +57,7 @@ int BattleInfo::getAvaliableHex(CreatureID creID, bool attackerOwned, int initia return BattleHex::getClosestTile(attackerOwned, pos, occupyable); } -std::pair< std::vector, int > BattleInfo::getPath(BattleHex start, BattleHex dest, const CStack *stack) +std::pair< std::vector, int > BattleInfo::getPath(BattleHex start, BattleHex dest, const CStack * stack) { auto reachability = getReachability(stack); @@ -78,8 +78,8 @@ std::pair< std::vector, int > BattleInfo::getPath(BattleHex start, Ba return std::make_pair(path, reachability.distances[dest]); } -ui32 BattleInfo::calculateDmg( const CStack* attacker, const CStack* defender, - bool shooting, ui8 charge, bool lucky, bool unlucky, bool deathBlow, bool ballistaDoubleDmg, CRandomGenerator & rand ) +ui32 BattleInfo::calculateDmg(const CStack* attacker, const CStack* defender, + bool shooting, ui8 charge, bool lucky, bool unlucky, bool deathBlow, bool ballistaDoubleDmg, CRandomGenerator & rand) { TDmgRange range = calculateDmgRange(attacker, defender, shooting, charge, lucky, unlucky, deathBlow, ballistaDoubleDmg); @@ -96,7 +96,7 @@ ui32 BattleInfo::calculateDmg( const CStack* attacker, const CStack* defender, return range.first; } -void BattleInfo::calculateCasualties( std::map *casualties ) const +void BattleInfo::calculateCasualties(std::map * casualties) const { for(auto & elem : stacks)//setting casualties { @@ -112,12 +112,12 @@ CStack * BattleInfo::generateNewStack(const CStackInstance &base, bool attackerO { int stackID = getIdForNewStack(); PlayerColor owner = sides[attackerOwned ? 0 : 1].color; - assert((owner >= PlayerColor::PLAYER_LIMIT) || - (base.armyObj && base.armyObj->tempOwner == owner)); + assert((owner >= PlayerColor::PLAYER_LIMIT) || + (base.armyObj && base.armyObj->tempOwner == owner)); - auto ret = new CStack(&base, owner, stackID, attackerOwned, slot); + auto ret = new CStack(&base, owner, stackID, attackerOwned, slot); ret->position = getAvaliableHex (base.getCreatureID(), attackerOwned, position); //TODO: what if no free tile on battlefield was found? - ret->state.insert(EBattleStackState::ALIVE); //alive state indication + ret->state.insert(EBattleStackState::ALIVE); //alive state indication return ret; } @@ -125,9 +125,9 @@ CStack * BattleInfo::generateNewStack(const CStackBasicDescriptor &base, bool at { int stackID = getIdForNewStack(); PlayerColor owner = sides[attackerOwned ? 0 : 1].color; - auto ret = new CStack(&base, owner, stackID, attackerOwned, slot); + auto ret = new CStack(&base, owner, stackID, attackerOwned, slot); ret->position = position; - ret->state.insert(EBattleStackState::ALIVE); //alive state indication + ret->state.insert(EBattleStackState::ALIVE); //alive state indication return ret; } @@ -261,7 +261,7 @@ struct RangeGenerator std::function myRand; }; -BattleInfo * BattleInfo::setupBattle( int3 tile, ETerrainType terrain, BFieldType battlefieldType, const CArmedInstance *armies[2], const CGHeroInstance * heroes[2], bool creatureBank, const CGTownInstance *town ) +BattleInfo * BattleInfo::setupBattle(int3 tile, ETerrainType terrain, BFieldType battlefieldType, const CArmedInstance * armies[2], const CGHeroInstance * heroes[2], bool creatureBank, const CGTownInstance * town) { CMP_stack cmpst; auto curB = new BattleInfo(); @@ -644,7 +644,7 @@ BattleInfo * BattleInfo::setupBattle( int3 tile, ETerrainType terrain, BFieldTyp return curB; } -const CGHeroInstance * BattleInfo::getHero( PlayerColor player ) const +const CGHeroInstance * BattleInfo::getHero(PlayerColor player) const { for(int i = 0; i < sides.size(); i++) if(sides[i].color == player) @@ -741,7 +741,7 @@ CGHeroInstance * BattleInfo::battleGetFightingHero(ui8 side) const } -bool CMP_stack::operator()( const CStack* a, const CStack* b ) +bool CMP_stack::operator()(const CStack* a, const CStack* b) { switch(phase) { @@ -772,7 +772,7 @@ bool CMP_stack::operator()( const CStack* a, const CStack* b ) } -CMP_stack::CMP_stack( int Phase /*= 1*/, int Turn ) +CMP_stack::CMP_stack(int Phase /*= 1*/, int Turn) { phase = Phase; turn = Turn; diff --git a/lib/BattleInfo.h b/lib/battle/BattleInfo.h similarity index 89% rename from lib/BattleInfo.h rename to lib/battle/BattleInfo.h index 7acbf1b96..10f48a2e7 100644 --- a/lib/BattleInfo.h +++ b/lib/battle/BattleInfo.h @@ -11,7 +11,7 @@ #include "SiegeInfo.h" #include "SideInBattle.h" #include "HeroBonus.h" -#include "CBattleCallback.h" +#include "battle/CBattleInfoCallback.h" #include "int3.h" class CStack; @@ -61,7 +61,7 @@ struct DLL_LINKAGE BattleInfo : public CBonusSystemNode, public CBattleInfoCallb //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::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; @@ -69,7 +69,7 @@ struct DLL_LINKAGE BattleInfo : public CBonusSystemNode, public CBattleInfoCallb 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 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 @@ -84,7 +84,7 @@ struct DLL_LINKAGE BattleInfo : public CBonusSystemNode, public CBattleInfoCallb 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 ); + 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; diff --git a/lib/CBattleCallback.cpp b/lib/battle/CBattleInfoCallback.cpp similarity index 63% rename from lib/CBattleCallback.cpp rename to lib/battle/CBattleInfoCallback.cpp index 11b9f5cfc..513add877 100644 --- a/lib/CBattleCallback.cpp +++ b/lib/battle/CBattleInfoCallback.cpp @@ -1,14 +1,3 @@ -#include "StdInc.h" -#include "CBattleCallback.h" -#include "CStack.h" -#include "BattleInfo.h" -#include "CGameState.h" -#include "NetPacks.h" -#include "spells/CSpellHandler.h" -#include "VCMI_Lib.h" -#include "CTownHandler.h" -#include "mapObjects/CGTownInstance.h" - /* * CBattleCallback.cpp, part of VCMI engine * @@ -18,337 +7,99 @@ * Full text of license available in license.txt file, in main folder * */ +#include "StdInc.h" +#include "CBattleInfoCallback.h" +#include "CStack.h" +#include "BattleInfo.h" +#include "NetPacks.h" +#include "spells/CSpellHandler.h" +#include "mapObjects/CGTownInstance.h" -#define RETURN_IF_NOT_BATTLE(X) if(!duringBattle()) {logGlobal->errorStream() << __FUNCTION__ << " called when no battle!"; return X; } - -namespace SiegeStuffThatShouldBeMovedToHandlers // <=== TODO +namespace SiegeStuffThatShouldBeMovedToHandlers // <=== TODO { - static void retreiveTurretDamageRange(const CGTownInstance * town, const CStack *turret, double &outMinDmg, double &outMaxDmg) +static void retreiveTurretDamageRange(const CGTownInstance * town, const CStack * turret, double & outMinDmg, double & outMaxDmg) +{ + assert(turret->getCreature()->idNumber == CreatureID::ARROW_TOWERS); + assert(town); + assert(turret->position >= -4 && turret->position <= -2); + + float multiplier = (turret->position == -2) ? 1 : 0.5; + + int baseMin = 6; + int baseMax = 10; + + outMinDmg = multiplier * (baseMin + town->getTownLevel() * 2); + outMaxDmg = multiplier * (baseMax + town->getTownLevel() * 3); +} + +static BattleHex lineToWallHex(int line) //returns hex with wall in given line (y coordinate) +{ + static const BattleHex lineToHex[] = {12, 29, 45, 62, 78, 95, 112, 130, 147, 165, 182}; + + return lineToHex[line]; +} + +static bool sameSideOfWall(BattleHex pos1, BattleHex pos2) +{ + const int wallInStackLine = lineToWallHex(pos1.getY()); + const int wallInDestLine = lineToWallHex(pos2.getY()); + + const bool stackLeft = pos1 < wallInStackLine; + const bool destLeft = pos2 < wallInDestLine; + + return stackLeft == destLeft; +} + +// parts of wall +static const std::pair wallParts[] = +{ + std::make_pair(50, EWallPart::KEEP), + std::make_pair(183, EWallPart::BOTTOM_TOWER), + std::make_pair(182, EWallPart::BOTTOM_WALL), + std::make_pair(130, EWallPart::BELOW_GATE), + std::make_pair(78, EWallPart::OVER_GATE), + std::make_pair(29, EWallPart::UPPER_WALL), + std::make_pair(12, EWallPart::UPPER_TOWER), + std::make_pair(95, EWallPart::INDESTRUCTIBLE_PART_OF_GATE), + std::make_pair(96, EWallPart::GATE), + std::make_pair(45, EWallPart::INDESTRUCTIBLE_PART), + std::make_pair(62, EWallPart::INDESTRUCTIBLE_PART), + std::make_pair(112, EWallPart::INDESTRUCTIBLE_PART), + std::make_pair(147, EWallPart::INDESTRUCTIBLE_PART), + std::make_pair(165, EWallPart::INDESTRUCTIBLE_PART) +}; + +static EWallPart::EWallPart hexToWallPart(BattleHex hex) +{ + for(auto & elem : wallParts) { - assert(turret->getCreature()->idNumber == CreatureID::ARROW_TOWERS); - assert(town); - assert(turret->position >= -4 && turret->position <= -2); - - float multiplier = (turret->position == -2) ? 1 : 0.5; - - int baseMin = 6; - int baseMax = 10; - - outMinDmg = multiplier * (baseMin + town->getTownLevel() * 2); - outMaxDmg = multiplier * (baseMax + town->getTownLevel() * 3); + if(elem.first == hex) + return elem.second; } - static BattleHex lineToWallHex(int line) //returns hex with wall in given line (y coordinate) - { - static const BattleHex lineToHex[] = {12, 29, 45, 62, 78, 95, 112, 130, 147, 165, 182}; + return EWallPart::INVALID; //not found! +} - return lineToHex[line]; +static BattleHex WallPartToHex(EWallPart::EWallPart part) +{ + for(auto & elem : wallParts) + { + if(elem.second == part) + return elem.first; } - static bool sameSideOfWall(BattleHex pos1, BattleHex pos2) - { - const int wallInStackLine = lineToWallHex(pos1.getY()); - const int wallInDestLine = lineToWallHex(pos2.getY()); - - const bool stackLeft = pos1 < wallInStackLine; - const bool destLeft = pos2 < wallInDestLine; - - return stackLeft == destLeft; - } - - // parts of wall - static const std::pair wallParts[] = - { - std::make_pair(50, EWallPart::KEEP), - std::make_pair(183, EWallPart::BOTTOM_TOWER), - std::make_pair(182, EWallPart::BOTTOM_WALL), - std::make_pair(130, EWallPart::BELOW_GATE), - std::make_pair(78, EWallPart::OVER_GATE), - std::make_pair(29, EWallPart::UPPER_WALL), - std::make_pair(12, EWallPart::UPPER_TOWER), - std::make_pair(95, EWallPart::INDESTRUCTIBLE_PART_OF_GATE), - std::make_pair(96, EWallPart::GATE), - std::make_pair(45, EWallPart::INDESTRUCTIBLE_PART), - std::make_pair(62, EWallPart::INDESTRUCTIBLE_PART), - std::make_pair(112, EWallPart::INDESTRUCTIBLE_PART), - std::make_pair(147, EWallPart::INDESTRUCTIBLE_PART), - std::make_pair(165, EWallPart::INDESTRUCTIBLE_PART) - }; - - static EWallPart::EWallPart hexToWallPart(BattleHex hex) - { - for(auto & elem : wallParts) - { - if(elem.first == hex) - return elem.second; - } - - return EWallPart::INVALID; //not found! - } - - static BattleHex WallPartToHex(EWallPart::EWallPart part) - { - for(auto & elem : wallParts) - { - if(elem.second == part) - return elem.first; - } - - return BattleHex::INVALID; //not found! - } + return BattleHex::INVALID; //not found! +} } using namespace SiegeStuffThatShouldBeMovedToHandlers; -bool CCallbackBase::duringBattle() const -{ - return getBattle() != nullptr; -} - -void CCallbackBase::setBattle(const BattleInfo *B) -{ - battle = B; -} - -boost::optional CCallbackBase::getPlayerID() const -{ - return player; -} - -ETerrainType CBattleInfoEssentials::battleTerrainType() const -{ - RETURN_IF_NOT_BATTLE(ETerrainType::WRONG); - return getBattle()->terrainType; -} - -BFieldType CBattleInfoEssentials::battleGetBattlefieldType() const -{ - RETURN_IF_NOT_BATTLE(BFieldType::NONE); - return getBattle()->battlefieldType; -} - -std::vector > CBattleInfoEssentials::battleGetAllObstacles(boost::optional perspective /*= boost::none*/) const -{ - std::vector > ret; - RETURN_IF_NOT_BATTLE(ret); - - if(!perspective) - { - //if no particular perspective request, use default one - perspective = battleGetMySide(); - } - else - { - if(!!player && *perspective != battleGetMySide()) - { - logGlobal->errorStream() << "Unauthorized access attempt!"; - assert(0); //I want to notice if that happens - //perspective = battleGetMySide(); - } - } - - for(auto oi : getBattle()->obstacles) - { - if(getBattle()->battleIsObstacleVisibleForSide(*oi, *perspective)) - ret.push_back(oi); - } - - return ret; -} - -bool CBattleInfoEssentials::battleIsObstacleVisibleForSide(const CObstacleInstance & coi, BattlePerspective::BattlePerspective side) const -{ - RETURN_IF_NOT_BATTLE(false); - return side == BattlePerspective::ALL_KNOWING || coi.visibleForSide(side, battleHasNativeStack(side)); -} - -bool CBattleInfoEssentials::battleHasNativeStack(ui8 side) const -{ - RETURN_IF_NOT_BATTLE(false); - - for(const CStack *s : battleGetAllStacks()) - { - if(s->attackerOwned == !side && s->getCreature()->isItNativeTerrain(getBattle()->terrainType)) - return true; - } - - return false; -} - -TStacks CBattleInfoEssentials::battleGetAllStacks(bool includeTurrets /*= false*/) const -{ - return battleGetStacksIf([=](const CStack * s) - { - return !s->isGhost() && (includeTurrets || !s->isTurret()); - }); -} - -TStacks CBattleInfoEssentials::battleGetStacksIf(TStackFilter predicate) const -{ - TStacks ret; - RETURN_IF_NOT_BATTLE(ret); - - vstd::copy_if(getBattle()->stacks, std::back_inserter(ret), predicate); - - return ret; -} - -TStacks CBattleInfoEssentials::battleAliveStacks() const -{ - return battleGetStacksIf([](const CStack * s){ - return s->isValidTarget(false); - }); -} - -TStacks CBattleInfoEssentials::battleAliveStacks(ui8 side) const -{ - return battleGetStacksIf([=](const CStack * s){ - return s->isValidTarget(false) && s->attackerOwned == !side; - }); -} - -int CBattleInfoEssentials::battleGetMoatDmg() const -{ - RETURN_IF_NOT_BATTLE(0); - - auto town = getBattle()->town; - if(!town) - return 0; - - return town->town->moatDamage; -} - -const CGTownInstance * CBattleInfoEssentials::battleGetDefendedTown() const -{ - RETURN_IF_NOT_BATTLE(nullptr); - - - if(!getBattle() || getBattle()->town == nullptr) - return nullptr; - - return getBattle()->town; -} - -BattlePerspective::BattlePerspective CBattleInfoEssentials::battleGetMySide() const -{ - RETURN_IF_NOT_BATTLE(BattlePerspective::INVALID); - if(!player || player.get().isSpectator()) - return BattlePerspective::ALL_KNOWING; - if(*player == getBattle()->sides[0].color) - return BattlePerspective::LEFT_SIDE; - if(*player == getBattle()->sides[1].color) - return BattlePerspective::RIGHT_SIDE; - - logGlobal->errorStream() << "Cannot find player " << *player << " in battle!"; - return BattlePerspective::INVALID; -} - -const CStack * CBattleInfoEssentials::battleActiveStack() const -{ - RETURN_IF_NOT_BATTLE(nullptr); - return battleGetStackByID(getBattle()->activeStack); -} - -const CStack* CBattleInfoEssentials::battleGetStackByID(int ID, bool onlyAlive) const -{ - RETURN_IF_NOT_BATTLE(nullptr); - - auto stacks = battleGetStacksIf([=](const CStack * s) - { - return s->ID == ID && (!onlyAlive || s->alive()); - }); - - if(stacks.empty()) - return nullptr; - else - return stacks[0]; -} - -bool CBattleInfoEssentials::battleDoWeKnowAbout(ui8 side) const -{ - RETURN_IF_NOT_BATTLE(false); - auto p = battleGetMySide(); - return p == BattlePerspective::ALL_KNOWING || p == side; -} - -si8 CBattleInfoEssentials::battleTacticDist() const -{ - RETURN_IF_NOT_BATTLE(0); - return getBattle()->tacticDistance; -} - -si8 CBattleInfoEssentials::battleGetTacticsSide() const -{ - RETURN_IF_NOT_BATTLE(-1); - return getBattle()->tacticsSide; -} - -const CGHeroInstance * CBattleInfoEssentials::battleGetFightingHero(ui8 side) const -{ - RETURN_IF_NOT_BATTLE(nullptr); - if(side > 1) - { - logGlobal->errorStream() << "FIXME: " << __FUNCTION__ << " wrong argument!"; - return nullptr; - } - - if(!battleDoWeKnowAbout(side)) - { - logGlobal->errorStream() << "FIXME: " << __FUNCTION__ << " access check "; - return nullptr; - } - - return getBattle()->sides[side].hero; -} - -const CArmedInstance * CBattleInfoEssentials::battleGetArmyObject(ui8 side) const -{ - RETURN_IF_NOT_BATTLE(nullptr); - if(side > 1) - { - logGlobal->errorStream() << "FIXME: " << __FUNCTION__ << " wrong argument!"; - return nullptr; - } - - if(!battleDoWeKnowAbout(side)) - { - logGlobal->errorStream() << "FIXME: " << __FUNCTION__ << " access check "; - return nullptr; - } - - return getBattle()->sides[side].armyObject; -} - -InfoAboutHero CBattleInfoEssentials::battleGetHeroInfo( ui8 side ) const -{ - auto hero = getBattle()->sides[side].hero; - if(!hero) - { - logGlobal->warnStream() << __FUNCTION__ << ": side " << (int)side << " does not have hero!"; - return InfoAboutHero(); - } - - InfoAboutHero::EInfoLevel infoLevel = battleDoWeKnowAbout(side) ? InfoAboutHero::EInfoLevel::DETAILED : InfoAboutHero::EInfoLevel::BASIC; - return InfoAboutHero(hero, infoLevel); -} - -int CBattleInfoEssentials::battleCastSpells(ui8 side) const -{ - RETURN_IF_NOT_BATTLE(-1); - return getBattle()->sides[side].castSpellsCount; -} - -const IBonusBearer * CBattleInfoEssentials::getBattleNode() const -{ - return getBattle(); -} - ESpellCastProblem::ESpellCastProblem CBattleInfoCallback::battleCanCastSpell(const ISpellCaster * caster, ECastingMode::ECastingMode mode) const { RETURN_IF_NOT_BATTLE(ESpellCastProblem::INVALID); if(caster == nullptr) { - logGlobal->errorStream() << "CBattleInfoCallback::battleCanCastSpell: no spellcaster."; + logGlobal->error("CBattleInfoCallback::battleCanCastSpell: no spellcaster."); return ESpellCastProblem::INVALID; } const PlayerColor player = caster->getOwner(); @@ -367,17 +118,17 @@ ESpellCastProblem::ESpellCastProblem CBattleInfoCallback::battleCanCastSpell(con switch (mode) { case ECastingMode::HERO_CASTING: - { - if(battleCastSpells(side) > 0) - return ESpellCastProblem::ALREADY_CASTED_THIS_TURN; + { + if(battleCastSpells(side) > 0) + return ESpellCastProblem::ALREADY_CASTED_THIS_TURN; - auto hero = dynamic_cast(caster); + auto hero = dynamic_cast(caster); - if(!hero) - return ESpellCastProblem::NO_HERO_TO_CAST_SPELL; - if(hero->hasBonusOfType(Bonus::BLOCK_ALL_MAGIC)) - return ESpellCastProblem::MAGIC_IS_BLOCKED; - } + if(!hero) + return ESpellCastProblem::NO_HERO_TO_CAST_SPELL; + if(hero->hasBonusOfType(Bonus::BLOCK_ALL_MAGIC)) + return ESpellCastProblem::MAGIC_IS_BLOCKED; + } break; default: break; @@ -386,126 +137,12 @@ ESpellCastProblem::ESpellCastProblem CBattleInfoCallback::battleCanCastSpell(con return ESpellCastProblem::OK; } -bool CBattleInfoEssentials::battleCanFlee(PlayerColor player) const -{ - RETURN_IF_NOT_BATTLE(false); - const si8 mySide = playerToSide(player); - const CGHeroInstance *myHero = battleGetFightingHero(mySide); - - //current player have no hero - if(!myHero) - return false; - - //eg. one of heroes is wearing shakles of war - if(myHero->hasBonusOfType(Bonus::BATTLE_NO_FLEEING)) - return false; - - //we are besieged defender - if(mySide == BattleSide::DEFENDER && battleGetSiegeLevel()) - { - auto town = battleGetDefendedTown(); - if(!town->hasBuilt(BuildingID::ESCAPE_TUNNEL, ETownType::STRONGHOLD)) - return false; - } - - return true; -} - -si8 CBattleInfoEssentials::playerToSide(PlayerColor player) const -{ - RETURN_IF_NOT_BATTLE(-1); - int ret = vstd::find_pos_if(getBattle()->sides, [=](const SideInBattle &side){ return side.color == player; }); - if(ret < 0) - logGlobal->warnStream() << "Cannot find side for player " << player; - - return ret; -} - -bool CBattleInfoEssentials::playerHasAccessToHeroInfo(PlayerColor player, const CGHeroInstance * h) const -{ - RETURN_IF_NOT_BATTLE(false); - const si8 playerSide = playerToSide(player); - if (playerSide >= 0) - { - if (getBattle()->sides[!playerSide].hero == h) - return true; - } - return false; -} - -ui8 CBattleInfoEssentials::battleGetSiegeLevel() const -{ - RETURN_IF_NOT_BATTLE(0); - return getBattle()->town ? getBattle()->town->fortLevel() : CGTownInstance::NONE; -} - -bool CBattleInfoEssentials::battleCanSurrender(PlayerColor player) const -{ - RETURN_IF_NOT_BATTLE(false); - ui8 mySide = playerToSide(player); - bool iAmSiegeDefender = ( mySide == BattleSide::DEFENDER && battleGetSiegeLevel() ); - //conditions like for fleeing (except escape tunnel presence) + enemy must have a hero - return battleCanFlee(player) && !iAmSiegeDefender && battleHasHero(!mySide); -} - -bool CBattleInfoEssentials::battleHasHero(ui8 side) const -{ - RETURN_IF_NOT_BATTLE(false); - assert(side < 2); - return getBattle()->sides[side].hero; -} - -si8 CBattleInfoEssentials::battleGetWallState(int partOfWall) const -{ - RETURN_IF_NOT_BATTLE(0); - if(getBattle()->town == nullptr || getBattle()->town->fortLevel() == CGTownInstance::NONE) - return EWallState::NONE; - - assert(partOfWall >= 0 && partOfWall < EWallPart::PARTS_COUNT); - return getBattle()->si.wallState[partOfWall]; -} - -EGateState CBattleInfoEssentials::battleGetGateState() const -{ - RETURN_IF_NOT_BATTLE(EGateState::NONE); - if(getBattle()->town == nullptr || getBattle()->town->fortLevel() == CGTownInstance::NONE) - return EGateState::NONE; - - return getBattle()->si.gateState; -} - -PlayerColor CBattleInfoEssentials::battleGetOwner(const CStack * stack) const -{ - RETURN_IF_NOT_BATTLE(PlayerColor::CANNOT_DETERMINE); - if(stack->hasBonusOfType(Bonus::HYPNOTIZED)) - return getBattle()->theOtherPlayer(stack->owner); - else - return stack->owner; -} - -const CGHeroInstance * CBattleInfoEssentials::battleGetOwnerHero(const CStack * stack) const -{ - RETURN_IF_NOT_BATTLE(nullptr); - return getBattle()->sides.at(playerToSide(battleGetOwner(stack))).hero; -} - -bool CBattleInfoEssentials::battleMatchOwner(const CStack * attacker, const CStack * defender, const boost::logic::tribool positivness /* = false*/) const -{ - RETURN_IF_NOT_BATTLE(false); - if(boost::logic::indeterminate(positivness)) - return true; - else if(defender->owner != battleGetOwner(defender)) - return true;//mind controlled unit is attackable for both sides - else - return (battleGetOwner(attacker) == battleGetOwner(defender)) == positivness; -} - -si8 CBattleInfoCallback::battleHasWallPenalty( const CStack * stack, BattleHex destHex ) const +si8 CBattleInfoCallback::battleHasWallPenalty(const CStack * stack, BattleHex destHex) const { return battleHasWallPenalty(stack, stack->position, destHex); } -si8 CBattleInfoCallback::battleHasWallPenalty(const IBonusBearer *bonusBearer, BattleHex shooterPosition, BattleHex destHex) const +si8 CBattleInfoCallback::battleHasWallPenalty(const IBonusBearer * bonusBearer, BattleHex shooterPosition, BattleHex destHex) const { RETURN_IF_NOT_BATTLE(false); if (!battleGetSiegeLevel() || bonusBearer->hasBonusOfType(Bonus::NO_WALL_PENALTY)) @@ -545,7 +182,7 @@ si8 CBattleInfoCallback::battleCanTeleportTo(const CStack * stack, BattleHex des return true; } -std::set CBattleInfoCallback::battleGetAttackedHexes(const CStack* attacker, BattleHex destinationTile, BattleHex attackerPos /*= BattleHex::INVALID*/) const +std::set CBattleInfoCallback::battleGetAttackedHexes(const CStack* attacker, BattleHex destinationTile, BattleHex attackerPos /*= BattleHex::INVALID*/) const { std::set attackedHexes; RETURN_IF_NOT_BATTLE(attackedHexes); @@ -591,7 +228,7 @@ const CStack* CBattleInfoCallback::battleGetStackByPos(BattleHex pos, bool onlyA RETURN_IF_NOT_BATTLE(nullptr); for(auto s : battleGetAllStacks(true)) if(vstd::contains(s->getHexes(), pos) && (!onlyAlive || s->alive())) - return s; + return s; return nullptr; } @@ -603,9 +240,9 @@ void CBattleInfoCallback::battleGetStackQueue(std::vector &out, //let's define a huge lambda auto takeStack = [&](std::vector &st) -> const CStack* { - const CStack *ret = nullptr; + const CStack * ret = nullptr; unsigned i, //fastest stack - j=0; //fastest stack of the other side + j=0; //fastest stack of the other side for(i = 0; i < st.size(); i++) if(st[i]) break; @@ -614,7 +251,7 @@ void CBattleInfoCallback::battleGetStackQueue(std::vector &out, if(i == st.size()) return nullptr; - const CStack *fastest = st[i], *other = nullptr; + const CStack * fastest = st[i], *other = nullptr; int bestSpeed = fastest->Speed(turn); //FIXME: comparison between bool and integer. Logic does not makes sense either @@ -662,7 +299,7 @@ void CBattleInfoCallback::battleGetStackQueue(std::vector &out, // [3] - rest of waited cres std::vector phase[4]; int toMove = 0; //how many stacks still has move - const CStack *active = battleActiveStack(); + const CStack * active = battleActiveStack(); //active stack hasn't taken any action yet - must be placed at the beginning of queue, no matter what if(!turn && active && active->willMove() && !active->waited()) @@ -683,8 +320,8 @@ void CBattleInfoCallback::battleGetStackQueue(std::vector &out, for(auto s : battleGetAllStacks(true)) { if((turn <= 0 && !s->willMove()) //we are considering current round and stack won't move - || (turn > 0 && !s->canMove(turn)) //stack won't be able to move in later rounds - || (turn <= 0 && s == active && out.size() && s == out.front())) //it's active stack already added at the beginning of queue + || (turn > 0 && !s->canMove(turn)) //stack won't be able to move in later rounds + || (turn <= 0 && s == active && out.size() && s == out.front())) //it's active stack already added at the beginning of queue { continue; } @@ -697,7 +334,7 @@ void CBattleInfoCallback::battleGetStackQueue(std::vector &out, else p = 3; } - else if(s->getCreature()->idNumber == CreatureID::CATAPULT || s->getCreature()->idNumber == CreatureID::ARROW_TOWERS) //catapult and turrets are first + else if(s->getCreature()->idNumber == CreatureID::CATAPULT || s->getCreature()->idNumber == CreatureID::ARROW_TOWERS) //catapult and turrets are first { p = 0; } @@ -738,7 +375,7 @@ void CBattleInfoCallback::battleGetStackQueue(std::vector &out, int pi = 1; while(out.size() < howMany) { - const CStack *hlp = takeStack(phase[pi]); + const CStack * hlp = takeStack(phase[pi]); if(!hlp) { pi++; @@ -811,11 +448,11 @@ std::vector CBattleInfoCallback::battleGetAvailableHexes(const CStack // Return true if given hex has at least one available neighbour. // Available hexes are already present in ret vector. auto availableNeighbor = boost::find_if(ret, [=] (BattleHex availableHex) - { return BattleHex::mutualPosition(hex, availableHex) >= 0; }); - + { + return BattleHex::mutualPosition(hex, availableHex) >= 0; + }); return availableNeighbor != ret.end(); }; - for(const CStack * otherSt : battleAliveStacks(stack->attackerOwned)) { if(!otherSt->isValidTarget(false)) @@ -873,7 +510,7 @@ bool CBattleInfoCallback::battleCanShoot(const CStack * stack, BattleHex dest) c if(battleTacticDist()) //no shooting during tactics return false; - const CStack *dst = battleGetStackByPos(dest); + const CStack * dst = battleGetStackByPos(dest); if(!stack || !dst) return false; @@ -893,11 +530,11 @@ bool CBattleInfoCallback::battleCanShoot(const CStack * stack, BattleHex dest) c return false; if(stack->hasBonusOfType(Bonus::SHOOTER)//it's shooter - && battleMatchOwner(stack, dst) - && dst->alive() - && (!battleIsStackBlocked(stack) || stack->hasBonusOfType(Bonus::FREE_SHOOTING)) - && stack->shots - ) + && battleMatchOwner(stack, dst) + && dst->alive() + && (!battleIsStackBlocked(stack) || stack->hasBonusOfType(Bonus::FREE_SHOOTING)) + && stack->shots + ) return true; return false; } @@ -908,25 +545,25 @@ TDmgRange CBattleInfoCallback::calculateDmgRange(const CStack* attacker, const C return calculateDmgRange(attacker, defender, attacker->count, shooting, charge, lucky, unlucky, deathBlow, ballistaDoubleDmg); } -TDmgRange CBattleInfoCallback::calculateDmgRange(const BattleAttackInfo &info) const +TDmgRange CBattleInfoCallback::calculateDmgRange(const BattleAttackInfo & info) const { auto battleBonusValue = [&](const IBonusBearer * bearer, CSelector selector) -> int { auto noLimit = Selector::effectRange(Bonus::NO_LIMIT); auto limitMatches = info.shooting - ? Selector::effectRange(Bonus::ONLY_DISTANCE_FIGHT) - : Selector::effectRange(Bonus::ONLY_MELEE_FIGHT); + ? Selector::effectRange(Bonus::ONLY_DISTANCE_FIGHT) + : Selector::effectRange(Bonus::ONLY_MELEE_FIGHT); //any regular bonuses or just ones for melee/ranged return bearer->getBonuses(selector, noLimit.Or(limitMatches))->totalValue(); }; double additiveBonus = 1.0, multBonus = 1.0, - minDmg = info.attackerBonuses->getMinDamage() * info.attackerCount,//TODO: ONLY_MELEE_FIGHT / ONLY_DISTANCE_FIGHT - maxDmg = info.attackerBonuses->getMaxDamage() * info.attackerCount; + minDmg = info.attackerBonuses->getMinDamage() * info.attackerCount,//TODO: ONLY_MELEE_FIGHT / ONLY_DISTANCE_FIGHT + maxDmg = info.attackerBonuses->getMaxDamage() * info.attackerCount; const CCreature *attackerType = info.attacker->getCreature(), - *defenderType = info.defender->getCreature(); + *defenderType = info.defender->getCreature(); if(attackerType->idNumber == CreatureID::ARROW_TOWERS) { @@ -964,9 +601,9 @@ TDmgRange CBattleInfoCallback::calculateDmgRange(const BattleAttackInfo &info) c { for(const std::shared_ptr b : VLC->creh->creatures[g]->getBonusList()) { - if ( (b->type == Bonus::KING3 && spLevel >= 3) || //expert - (b->type == Bonus::KING2 && spLevel >= 2) || //adv + - (b->type == Bonus::KING1 && spLevel >= 0) ) //none or basic + + if ((b->type == Bonus::KING3 && spLevel >= 3) || //expert + (b->type == Bonus::KING2 && spLevel >= 2) || //adv + + (b->type == Bonus::KING1 && spLevel >= 0)) //none or basic + { affectedIds.push_back(g); break; @@ -996,12 +633,10 @@ TDmgRange CBattleInfoCallback::calculateDmgRange(const BattleAttackInfo &info) c additiveBonus += inc; } - //applying jousting bonus - if( info.attackerBonuses->hasBonusOfType(Bonus::JOUSTING) && !info.defenderBonuses->hasBonusOfType(Bonus::CHARGE_IMMUNITY) ) + if(info.attackerBonuses->hasBonusOfType(Bonus::JOUSTING) && !info.defenderBonuses->hasBonusOfType(Bonus::CHARGE_IMMUNITY)) additiveBonus += info.chargedFields * 0.05; - //handling secondary abilities and artifacts giving premies to them if(info.shooting) additiveBonus += info.attackerBonuses->valOfBonuses(Bonus::SECONDARY_SKILL_PREMY, SecondarySkill::ARCHERY) / 100.0; @@ -1078,8 +713,8 @@ TDmgRange CBattleInfoCallback::calculateDmgRange(const BattleAttackInfo &info) c auto isAdvancedAirShield = [](const Bonus* bonus) { return bonus->source == Bonus::SPELL_EFFECT - && bonus->sid == SpellID::AIR_SHIELD - && bonus->val >= SecSkillLevel::ADVANCED; + && bonus->sid == SpellID::AIR_SHIELD + && bonus->val >= SecSkillLevel::ADVANCED; }; //wall / distance penalty + advanced air shield @@ -1104,7 +739,7 @@ TDmgRange CBattleInfoCallback::calculateDmgRange(const BattleAttackInfo &info) c // psychic elementals versus mind immune units 50% if(attackerType->idNumber == CreatureID::PSYCHIC_ELEMENTAL - && info.defenderBonuses->hasBonusOfType(Bonus::MIND_IMMUNITY)) + && info.defenderBonuses->hasBonusOfType(Bonus::MIND_IMMUNITY)) { multBonus *= 0.5; } @@ -1125,11 +760,11 @@ TDmgRange CBattleInfoCallback::calculateDmgRange(const BattleAttackInfo &info) c else if(blessEffects->size()) //bless handling { maxDmg += curseBlessAdditiveModifier; - returnedVal = std::make_pair(int(maxDmg), int(maxDmg)); + returnedVal = std::make_pair(int(maxDmg), int(maxDmg)); } else { - returnedVal = std::make_pair(int(minDmg), int(maxDmg)); + returnedVal = std::make_pair(int(minDmg), int(maxDmg)); } //damage cannot be less than 1 @@ -1139,8 +774,8 @@ TDmgRange CBattleInfoCallback::calculateDmgRange(const BattleAttackInfo &info) c return returnedVal; } -TDmgRange CBattleInfoCallback::calculateDmgRange( const CStack* attacker, const CStack* defender, TQuantity attackerCount, - bool shooting, ui8 charge, bool lucky, bool unlucky, bool deathBlow, bool ballistaDoubleDmg ) const +TDmgRange CBattleInfoCallback::calculateDmgRange(const CStack* attacker, const CStack* defender, TQuantity attackerCount, + bool shooting, ui8 charge, bool lucky, bool unlucky, bool deathBlow, bool ballistaDoubleDmg) const { BattleAttackInfo bai(attacker, defender, shooting); bai.attackerCount = attackerCount; @@ -1160,7 +795,7 @@ TDmgRange CBattleInfoCallback::battleEstimateDamage(CRandomGenerator & rand, con return battleEstimateDamage(rand, bai, retaliationDmg); } -std::pair CBattleInfoCallback::battleEstimateDamage(CRandomGenerator & rand, const BattleAttackInfo &bai, std::pair * retaliationDmg /*= nullptr*/) const +std::pair CBattleInfoCallback::battleEstimateDamage(CRandomGenerator & rand, const BattleAttackInfo & bai, std::pair * retaliationDmg /*= nullptr*/) const { RETURN_IF_NOT_BATTLE(std::make_pair(0, 0)); @@ -1201,7 +836,7 @@ std::shared_ptr CBattleInfoCallback::battleGetObstacleO for(auto &obs : battleGetAllObstacles()) { if(vstd::contains(obs->getBlockedTiles(), tile) - || (!onlyBlocking && vstd::contains(obs->getAffectedTiles(), tile))) + || (!onlyBlocking && vstd::contains(obs->getAffectedTiles(), tile))) { return obs; } @@ -1225,7 +860,7 @@ AccessibilityInfo CBattleInfoCallback::getAccesibility() const //gate -> should be before stacks if(battleGetSiegeLevel() > 0) { - EAccessibility::EAccessibility accessability = EAccessibility::ACCESSIBLE; + EAccessibility accessability = EAccessibility::ACCESSIBLE; switch(battleGetGateState()) { case EGateState::CLOSED: @@ -1281,12 +916,12 @@ AccessibilityInfo CBattleInfoCallback::getAccesibility() const return ret; } -AccessibilityInfo CBattleInfoCallback::getAccesibility(const CStack *stack) const +AccessibilityInfo CBattleInfoCallback::getAccesibility(const CStack * stack) const { return getAccesibility(stack->getHexes()); } -AccessibilityInfo CBattleInfoCallback::getAccesibility(const std::vector &accessibleHexes) const +AccessibilityInfo CBattleInfoCallback::getAccesibility(const std::vector & accessibleHexes) const { auto ret = getAccesibility(); for(auto hex : accessibleHexes) @@ -1296,7 +931,7 @@ AccessibilityInfo CBattleInfoCallback::getAccesibility(const std::vector quicksands = getStoppers(params.perspective); //const bool twoHexCreature = params.doubleWide; - std::queue hexq; //bfs queue //first element @@ -1334,7 +968,7 @@ ReachabilityInfo CBattleInfoCallback::makeBFS(const AccessibilityInfo &accessibi const bool accessible = accessibility.accessible(neighbour, params.doubleWide, params.attackerOwned); const int costFoundSoFar = ret.distances[neighbour]; - if(accessible && costToNeighbour < costFoundSoFar) + if(accessible && costToNeighbour < costFoundSoFar) { hexq.push(neighbour); ret.distances[neighbour] = costToNeighbour; @@ -1346,7 +980,7 @@ ReachabilityInfo CBattleInfoCallback::makeBFS(const AccessibilityInfo &accessibi return ret; } -ReachabilityInfo CBattleInfoCallback::makeBFS(const CStack *stack) const +ReachabilityInfo CBattleInfoCallback::makeBFS(const CStack * stack) const { return makeBFS(getAccesibility(stack), ReachabilityInfo::Parameters(stack)); } @@ -1377,7 +1011,7 @@ std::pair CBattleInfoCallback::getNearestStack(const { int distanceToPred; BattleHex destination; - const CStack *stack; + const CStack * stack; }; std::vector stackPairs; @@ -1423,7 +1057,7 @@ bool CBattleInfoCallback::isInTacticRange(BattleHex dest) const auto dist = battleGetTacticDist(); return ((!side && dest.getX() > 0 && dest.getX() <= dist) - || (side && dest.getX() < GameConstants::BFIELD_WIDTH - 1 && dest.getX() >= GameConstants::BFIELD_WIDTH - dist - 1)); + || (side && dest.getX() < GameConstants::BFIELD_WIDTH - 1 && dest.getX() >= GameConstants::BFIELD_WIDTH - dist - 1)); } ReachabilityInfo CBattleInfoCallback::getReachability(const CStack *stack) const @@ -1516,7 +1150,7 @@ AttackableTiles CBattleInfoCallback::getPotentiallyAttackableHexes (const CStack case -WN: //-17 //left-up or right-up case WN + 1: //18 //right-down case -WN + 1: //-16 //right-up - BattleHex::checkAndPush (destinationTile.hex + pseudoVector + (((hex/WN)%2) ? 1 : -1 ), hexes); + BattleHex::checkAndPush (destinationTile.hex + pseudoVector + (((hex/WN)%2) ? 1 : -1), hexes); break; case WN-1: //16 //left-down case -WN-1: //-18 //left-up @@ -1595,7 +1229,7 @@ bool CBattleInfoCallback::isToReverseHlp (BattleHex hexFrom, BattleHex hexTo, bo //TODO: this should apply also to mechanics and cursor interface bool CBattleInfoCallback::isToReverse (BattleHex hexFrom, BattleHex hexTo, bool curDir, bool toDoubleWide, bool toDir) const { - if (hexTo < 0 || hexFrom < 0) //turret + if (hexTo < 0 || hexFrom < 0) //turret return false; if (toDoubleWide) @@ -1652,7 +1286,6 @@ si8 CBattleInfoCallback::battleHasDistancePenalty(const IBonusBearer *bonusBeare for(auto hex : dstStack->getHexes()) if(BattleHex::getDistance(shooterPosition, hex) <= GameConstants::BATTLE_PENALTY_DISTANCE) return false; - //TODO what about two-hex shooters? } else @@ -1680,7 +1313,7 @@ bool CBattleInfoCallback::isWallPartPotentiallyAttackable(EWallPart::EWallPart w { RETURN_IF_NOT_BATTLE(false); return wallPart != EWallPart::INDESTRUCTIBLE_PART && wallPart != EWallPart::INDESTRUCTIBLE_PART_OF_GATE && - wallPart != EWallPart::INVALID; + wallPart != EWallPart::INVALID; } std::vector CBattleInfoCallback::getAttackableBattleHexes() const @@ -1715,14 +1348,13 @@ ui32 CBattleInfoCallback::battleGetSpellCost(const CSpell * sp, const CGHeroInst si32 manaReduction = 0; si32 manaIncrease = 0; - for(auto stack : battleAliveStacks()) { - if(stack->owner == caster->tempOwner && stack->hasBonusOfType(Bonus::CHANGES_SPELL_COST_FOR_ALLY) ) + if(stack->owner == caster->tempOwner && stack->hasBonusOfType(Bonus::CHANGES_SPELL_COST_FOR_ALLY)) { vstd::amax(manaReduction, stack->valOfBonuses(Bonus::CHANGES_SPELL_COST_FOR_ALLY)); } - if( stack->owner != caster->tempOwner && stack->hasBonusOfType(Bonus::CHANGES_SPELL_COST_FOR_ENEMY) ) + if(stack->owner != caster->tempOwner && stack->hasBonusOfType(Bonus::CHANGES_SPELL_COST_FOR_ENEMY)) { vstd::amax(manaIncrease, stack->valOfBonuses(Bonus::CHANGES_SPELL_COST_FOR_ENEMY)); } @@ -1736,9 +1368,12 @@ const CStack * CBattleInfoCallback::getStackIf(std::functionhasBonusOfType(Bonus::SIEGE_WEAPON)) //siege weapons cannot be blocked return false; - for(const CStack * s : batteAdjacentCreatures(stack)) + for(const CStack * s : batteAdjacentCreatures(stack)) { if (s->owner != stack->owner) //blocked by enemy stack return true; @@ -1762,7 +1397,7 @@ std::set CBattleInfoCallback:: batteAdjacentCreatures(const CStac RETURN_IF_NOT_BATTLE(stacks); for (BattleHex hex : stack->getSurroundingHexes()) - if(const CStack *neighbour = battleGetStackByPos(hex, true)) + if(const CStack * neighbour = battleGetStackByPos(hex, true)) stacks.insert(neighbour); return stacks; @@ -1798,7 +1433,7 @@ SpellID CBattleInfoCallback::getRandomBeneficialSpell(CRandomGenerator & rand, c }; std::vector beneficialSpells; - auto getAliveEnemy = [=](const std::function & pred) + auto getAliveEnemy = [=](const std::function & pred) { return getStackIf([=](const CStack * stack) { @@ -1812,33 +1447,33 @@ SpellID CBattleInfoCallback::getRandomBeneficialSpell(CRandomGenerator & rand, c cachingStr << "source_" << Bonus::SPELL_EFFECT << "id_" << spellID.num; if(subject->hasBonus(Selector::source(Bonus::SPELL_EFFECT, spellID), Selector::all, cachingStr.str()) - //TODO: this ability has special limitations - || spellID.toSpell()->canBeCastAt(this, subject, ECastingMode::CREATURE_ACTIVE_CASTING, subject->position) != ESpellCastProblem::OK) + //TODO: this ability has special limitations + || spellID.toSpell()->canBeCastAt(this, subject, ECastingMode::CREATURE_ACTIVE_CASTING, subject->position) != ESpellCastProblem::OK) continue; switch (spellID) { case SpellID::SHIELD: case SpellID::FIRE_SHIELD: // not if all enemy units are shooters + { + auto walker = getAliveEnemy([&](const CStack * stack) //look for enemy, non-shooting stack { - auto walker = getAliveEnemy([&](const CStack * stack) //look for enemy, non-shooting stack - { - return !stack->shots; - }); + return !stack->shots; + }); - if (!walker) - continue; - } + if (!walker) + continue; + } break; case SpellID::AIR_SHIELD: //only against active shooters + { + auto shooter = getAliveEnemy([&](const CStack * stack) //look for enemy, non-shooting stack { - auto shooter = getAliveEnemy([&](const CStack * stack) //look for enemy, non-shooting stack - { - return stack->hasBonusOfType(Bonus::SHOOTER) && stack->shots; - }); - if (!shooter) - continue; - } + return stack->hasBonusOfType(Bonus::SHOOTER) && stack->shots; + }); + if (!shooter) + continue; + } break; case SpellID::ANTI_MAGIC: case SpellID::MAGIC_MIRROR: @@ -1846,46 +1481,46 @@ SpellID CBattleInfoCallback::getRandomBeneficialSpell(CRandomGenerator & rand, c case SpellID::PROTECTION_FROM_EARTH: case SpellID::PROTECTION_FROM_FIRE: case SpellID::PROTECTION_FROM_WATER: - { - const ui8 enemySide = (ui8)subject->attackerOwned; - //todo: only if enemy has spellbook - if (!battleHasHero(enemySide)) //only if there is enemy hero - continue; - } + { + const ui8 enemySide = (ui8)subject->attackerOwned; + //todo: only if enemy has spellbook + if (!battleHasHero(enemySide)) //only if there is enemy hero + continue; + } break; case SpellID::CURE: //only damaged units - { - //do not cast on affected by debuffs - if (subject->firstHPleft >= subject->MaxHealth()) - continue; - } + { + //do not cast on affected by debuffs + if (subject->firstHPleft >= subject->MaxHealth()) + continue; + } break; case SpellID::BLOODLUST: - { - if (subject->shots) //if can shoot - only if enemy uits are adjacent - continue; - } + { + if (subject->shots) //if can shoot - only if enemy uits are adjacent + continue; + } break; case SpellID::PRECISION: - { - if (!(subject->hasBonusOfType(Bonus::SHOOTER) && subject->shots)) - continue; - } + { + if (!(subject->hasBonusOfType(Bonus::SHOOTER) && subject->shots)) + continue; + } break; case SpellID::SLAYER://only if monsters are present + { + auto kingMonster = getAliveEnemy([&](const CStack * stack) -> bool //look for enemy, non-shooting stack { - auto kingMonster = getAliveEnemy([&](const CStack *stack) -> bool //look for enemy, non-shooting stack - { - const auto isKing = Selector::type(Bonus::KING1) - .Or(Selector::type(Bonus::KING2)) - .Or(Selector::type(Bonus::KING3)); + const auto isKing = Selector::type(Bonus::KING1) + .Or(Selector::type(Bonus::KING2)) + .Or(Selector::type(Bonus::KING3)); - return stack->hasBonus(isKing); - }); + return stack->hasBonus(isKing); + }); - if (!kingMonster) - continue; - } + if (!kingMonster) + continue; + } break; } beneficialSpells.push_back(spellID); @@ -1942,7 +1577,7 @@ int CBattleInfoCallback::battleGetSurrenderCost(PlayerColor Player) const if(s->base) //we pay for our stack that comes from our army slots - condition eliminates summoned cres and war machines ret += s->getCreature()->cost[Res::GOLD] * s->count; - if(const CGHeroInstance *h = battleGetFightingHero(playerSide)) + if(const CGHeroInstance * h = battleGetFightingHero(playerSide)) discount += h->valOfBonuses(Bonus::SURRENDER_DISCOUNT); ret *= (100.0 - discount) / 100.0; @@ -1993,140 +1628,3 @@ boost::optional CBattleInfoCallback::battleIsFinished() const return 1; return boost::none; } - -bool AccessibilityInfo::accessible(BattleHex tile, const CStack *stack) const -{ - return accessible(tile, stack->doubleWide(), stack->attackerOwned); -} - -bool AccessibilityInfo::accessible(BattleHex tile, bool doubleWide, bool attackerOwned) const -{ - // All hexes that stack would cover if standing on tile have to be accessible. - for(auto hex : CStack::getHexes(tile, doubleWide, attackerOwned)) - { - // If the hex is out of range then the tile isn't accessible - if(!hex.isValid()) - return false; - // If we're no defender which step on gate and the hex isn't accessible, then the tile - // isn't accessible - else if(at(hex) != EAccessibility::ACCESSIBLE && - !(at(hex) == EAccessibility::GATE && !attackerOwned)) - { - return false; - } - } - return true; -} - -bool AccessibilityInfo::occupiable(const CStack *stack, BattleHex tile) const -{ - //obviously, we can occupy tile by standing on it - if(accessible(tile, stack)) - return true; - - if(stack->doubleWide()) - { - //Check the tile next to -> if stack stands there, it'll also occupy considered hex - const BattleHex anotherTile = tile + (stack->attackerOwned ? BattleHex::RIGHT : BattleHex::LEFT); - if(accessible(anotherTile, stack)) - return true; - } - - return false; -} - -ReachabilityInfo::Parameters::Parameters() -{ - stack = nullptr; - perspective = BattlePerspective::ALL_KNOWING; - attackerOwned = doubleWide = flying = false; -} - -ReachabilityInfo::Parameters::Parameters(const CStack *Stack) -{ - stack = Stack; - perspective = (BattlePerspective::BattlePerspective)(!Stack->attackerOwned); - startPosition = Stack->position; - doubleWide = stack->doubleWide(); - attackerOwned = stack->attackerOwned; - flying = stack->hasBonusOfType(Bonus::FLYING); - knownAccessible = stack->getHexes(); -} - -bool CPlayerBattleCallback::battleCanFlee() const -{ - RETURN_IF_NOT_BATTLE(false); - ASSERT_IF_CALLED_WITH_PLAYER - return CBattleInfoEssentials::battleCanFlee(*player); -} - -TStacks CPlayerBattleCallback::battleGetStacks(EStackOwnership whose /*= MINE_AND_ENEMY*/, bool onlyAlive /*= true*/) const -{ - if(whose != MINE_AND_ENEMY) - { - ASSERT_IF_CALLED_WITH_PLAYER - } - - return battleGetStacksIf([=](const CStack * s){ - const bool ownerMatches = (whose == MINE_AND_ENEMY) - || (whose == ONLY_MINE && s->owner == player) - || (whose == ONLY_ENEMY && s->owner != player); - - return ownerMatches && s->isValidTarget(!onlyAlive); - }); -} - -int CPlayerBattleCallback::battleGetSurrenderCost() const -{ - RETURN_IF_NOT_BATTLE(-3) - ASSERT_IF_CALLED_WITH_PLAYER - return CBattleInfoCallback::battleGetSurrenderCost(*player); -} - -const CGHeroInstance * CPlayerBattleCallback::battleGetMyHero() const -{ - return CBattleInfoEssentials::battleGetFightingHero(battleGetMySide()); -} - -InfoAboutHero CPlayerBattleCallback::battleGetEnemyHero() const -{ - return battleGetHeroInfo(!battleGetMySide()); -} - -BattleAttackInfo::BattleAttackInfo(const CStack *Attacker, const CStack *Defender, bool Shooting) -{ - attacker = Attacker; - defender = Defender; - - attackerBonuses = Attacker; - defenderBonuses = Defender; - - attackerPosition = Attacker->position; - defenderPosition = Defender->position; - - attackerCount = Attacker->count; - defenderCount = Defender->count; - - shooting = Shooting; - chargedFields = 0; - - luckyHit = false; - unluckyHit = false; - deathBlow = false; - ballistaDoubleDamage = false; -} - -BattleAttackInfo BattleAttackInfo::reverse() const -{ - BattleAttackInfo ret = *this; - std::swap(ret.attacker, ret.defender); - std::swap(ret.attackerBonuses, ret.defenderBonuses); - std::swap(ret.attackerPosition, ret.defenderPosition); - std::swap(ret.attackerCount, ret.defenderCount); - - ret.shooting = false; - ret.chargedFields = 0; - ret.luckyHit = ret.ballistaDoubleDamage = ret.deathBlow = false; - - return ret; -} diff --git a/lib/battle/CBattleInfoCallback.h b/lib/battle/CBattleInfoCallback.h new file mode 100644 index 000000000..fdd727a11 --- /dev/null +++ b/lib/battle/CBattleInfoCallback.h @@ -0,0 +1,110 @@ +/* + * CBattleCallback.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 "CCallbackBase.h" +#include "ReachabilityInfo.h" +#include "BattleAttackInfo.h" + +class CGHeroInstance; +class CStack; +class ISpellCaster; +class CSpell; +struct CObstacleInstance; +class IBonusBearer; +class CRandomGenerator; + +struct DLL_LINKAGE AttackableTiles +{ + std::set hostileCreaturePositions; + std::set friendlyCreaturePositions; //for Dragon Breath + template void serialize(Handler &h, const int version) + { + h & hostileCreaturePositions & friendlyCreaturePositions; + } +}; + +class DLL_LINKAGE CBattleInfoCallback : public virtual CBattleInfoEssentials +{ +public: + enum ERandomSpell + { + RANDOM_GENIE, RANDOM_AIMED + }; + //battle + boost::optional battleIsFinished() const; //return none if battle is ongoing; otherwise the victorious side (0/1) or 2 if it is a draw + + std::shared_ptr battleGetObstacleOnPos(BattleHex tile, bool onlyBlocking = true) const; //blocking obstacles makes tile inaccessible, others cause special effects (like Land Mines, Moat, Quicksands) + const CStack * battleGetStackByPos(BattleHex pos, bool onlyAlive = true) const; //returns stack info by given pos + void battleGetStackQueue(std::vector &out, const int howMany, const int turn = 0, int lastMoved = -1) const; + void battleGetStackCountOutsideHexes(bool *ac) const; // returns hexes which when in front of a stack cause us to move the amount box back + + std::vector battleGetAvailableHexes(const CStack * stack, bool addOccupiable, std::vector * attackable = nullptr) const; //returns hexes reachable by creature with id ID (valid movement destinations), DOES contain stack current position + + int battleGetSurrenderCost(PlayerColor Player) const; //returns cost of surrendering battle, -1 if surrendering is not possible + ReachabilityInfo::TDistances battleGetDistances(const CStack * stack, BattleHex hex = BattleHex::INVALID, BattleHex * predecessors = nullptr) const; //returns vector of distances to [dest hex number] + std::set battleGetAttackedHexes(const CStack* attacker, BattleHex destinationTile, BattleHex attackerPos = BattleHex::INVALID) const; + + bool battleCanAttack(const CStack * stack, const CStack * target, BattleHex dest) const; //determines if stack with given ID can attack target at the selected destination + bool battleCanShoot(const CStack * stack, BattleHex dest) const; //determines if stack with given ID shoot at the selected destination + bool battleIsStackBlocked(const CStack * stack) const; //returns true if there is neighboring enemy stack + std::set batteAdjacentCreatures (const CStack * stack) const; + + TDmgRange calculateDmgRange(const BattleAttackInfo & info) const; //charge - number of hexes travelled before attack (for champion's jousting); returns pair + TDmgRange calculateDmgRange(const CStack* attacker, const CStack* defender, TQuantity attackerCount, bool shooting, ui8 charge, bool lucky, bool unlucky, bool deathBlow, bool ballistaDoubleDmg) const; //charge - number of hexes travelled before attack (for champion's jousting); returns pair + TDmgRange calculateDmgRange(const CStack* attacker, const CStack* defender, bool shooting, ui8 charge, bool lucky, bool unlucky, bool deathBlow, bool ballistaDoubleDmg) const; //charge - number of hexes travelled before attack (for champion's jousting); returns pair + + //hextowallpart //int battleGetWallUnderHex(BattleHex hex) const; //returns part of destructible wall / gate / keep under given hex or -1 if not found + std::pair battleEstimateDamage(CRandomGenerator & rand, const BattleAttackInfo & bai, std::pair * retaliationDmg = nullptr) const; //estimates damage dealt by attacker to defender; it may be not precise especially when stack has randomly working bonuses; returns pair + std::pair battleEstimateDamage(CRandomGenerator & rand, const CStack * attacker, const CStack * defender, std::pair * retaliationDmg = nullptr) const; //estimates damage dealt by attacker to defender; it may be not precise especially when stack has randomly working bonuses; returns pair + si8 battleHasDistancePenalty(const CStack * stack, BattleHex destHex) const; + si8 battleHasDistancePenalty(const IBonusBearer * bonusBearer, BattleHex shooterPosition, BattleHex destHex) const; + si8 battleHasWallPenalty(const CStack * stack, BattleHex destHex) const; //checks if given stack has wall penalty + si8 battleHasWallPenalty(const IBonusBearer * bonusBearer, BattleHex shooterPosition, BattleHex destHex) const; //checks if given stack has wall penalty + + BattleHex wallPartToBattleHex(EWallPart::EWallPart part) const; + EWallPart::EWallPart battleHexToWallPart(BattleHex hex) const; //returns part of destructible wall / gate / keep under given hex or -1 if not found + bool isWallPartPotentiallyAttackable(EWallPart::EWallPart wallPart) const; // returns true if the wall part is potentially attackable (independent of wall state), false if not + std::vector getAttackableBattleHexes() const; + + //*** MAGIC + si8 battleMaxSpellLevel(ui8 side) const; //calculates minimum spell level possible to be cast on battlefield - takes into account artifacts of both heroes; if no effects are set, 0 is returned + ui32 battleGetSpellCost(const CSpell * sp, const CGHeroInstance * caster) const; //returns cost of given spell + ESpellCastProblem::ESpellCastProblem battleCanCastSpell(const ISpellCaster * caster, ECastingMode::ECastingMode mode) const; //returns true if there are no general issues preventing from casting a spell + + SpellID battleGetRandomStackSpell(CRandomGenerator & rand, const CStack * stack, ERandomSpell mode) const; + SpellID getRandomBeneficialSpell(CRandomGenerator & rand, const CStack * subject) const; + SpellID getRandomCastedSpell(CRandomGenerator & rand, const CStack * caster) const; //called at the beginning of turn for Faerie Dragon + + const CStack * getStackIf(std::function pred) const; + + si8 battleHasShootingPenalty(const CStack * stack, BattleHex destHex); + si8 battleCanTeleportTo(const CStack * stack, BattleHex destHex, int telportLevel) const; //checks if teleportation of given stack to given position can take place + + //convenience methods using the ones above + bool isInTacticRange(BattleHex dest) const; + si8 battleGetTacticDist() const; //returns tactic distance for calling player or 0 if this player is not in tactic phase (for ALL_KNOWING actual distance for tactic side) + + AttackableTiles getPotentiallyAttackableHexes(const CStack* attacker, BattleHex destinationTile, BattleHex attackerPos) const; //TODO: apply rotation to two-hex attacker + std::set getAttackedCreatures(const CStack* attacker, BattleHex destinationTile, BattleHex attackerPos = BattleHex::INVALID) const; //calculates range of multi-hex attacks + bool isToReverse(BattleHex hexFrom, BattleHex hexTo, bool curDir /*if true, creature is in attacker's direction*/, bool toDoubleWide, bool toDir) const; //determines if creature should be reversed (it stands on hexFrom and should 'see' hexTo) + bool isToReverseHlp(BattleHex hexFrom, BattleHex hexTo, bool curDir) const; //helper for isToReverse + + ReachabilityInfo getReachability(const CStack *stack) const; + ReachabilityInfo getReachability(const ReachabilityInfo::Parameters & params) const; + AccessibilityInfo getAccesibility() const; + AccessibilityInfo getAccesibility(const CStack *stack) const; //Hexes ocupied by stack will be marked as accessible. + AccessibilityInfo getAccesibility(const std::vector & accessibleHexes) const; //given hexes will be marked as accessible + std::pair getNearestStack(const CStack * closest, boost::logic::tribool attackerOwned) const; +protected: + ReachabilityInfo getFlyingReachability(const ReachabilityInfo::Parameters & params) const; + ReachabilityInfo makeBFS(const AccessibilityInfo & accessibility, const ReachabilityInfo::Parameters & params) const; + ReachabilityInfo makeBFS(const CStack * stack) const; //uses default parameters -> stack position and owner's perspective + std::set getStoppers(BattlePerspective::BattlePerspective whichSidePerspective) const; //get hexes with stopping obstacles (quicksands) +}; diff --git a/lib/battle/CBattleInfoEssentials.cpp b/lib/battle/CBattleInfoEssentials.cpp new file mode 100644 index 000000000..97edb4d56 --- /dev/null +++ b/lib/battle/CBattleInfoEssentials.cpp @@ -0,0 +1,349 @@ +/* + * CBattleInfoEssentials.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 "CBattleInfoEssentials.h" +#include "CStack.h" +#include "BattleInfo.h" +#include "NetPacks.h" +#include "mapObjects/CGTownInstance.h" + +ETerrainType CBattleInfoEssentials::battleTerrainType() const +{ + RETURN_IF_NOT_BATTLE(ETerrainType::WRONG); + return getBattle()->terrainType; +} + +BFieldType CBattleInfoEssentials::battleGetBattlefieldType() const +{ + RETURN_IF_NOT_BATTLE(BFieldType::NONE); + return getBattle()->battlefieldType; +} + +std::vector > CBattleInfoEssentials::battleGetAllObstacles(boost::optional perspective /*= boost::none*/) const +{ + std::vector > ret; + RETURN_IF_NOT_BATTLE(ret); + + if(!perspective) + { + //if no particular perspective request, use default one + perspective = battleGetMySide(); + } + else + { + if(!!player && *perspective != battleGetMySide()) + { + logGlobal->error("Unauthorized access attempt!"); + assert(0); //I want to notice if that happens + //perspective = battleGetMySide(); + } + } + + for(auto oi : getBattle()->obstacles) + { + if(getBattle()->battleIsObstacleVisibleForSide(*oi, *perspective)) + ret.push_back(oi); + } + + return ret; +} + +bool CBattleInfoEssentials::battleIsObstacleVisibleForSide(const CObstacleInstance & coi, BattlePerspective::BattlePerspective side) const +{ + RETURN_IF_NOT_BATTLE(false); + return side == BattlePerspective::ALL_KNOWING || coi.visibleForSide(side, battleHasNativeStack(side)); +} + +bool CBattleInfoEssentials::battleHasNativeStack(ui8 side) const +{ + RETURN_IF_NOT_BATTLE(false); + + for(const CStack * s : battleGetAllStacks()) + { + if(s->attackerOwned == !side && s->getCreature()->isItNativeTerrain(getBattle()->terrainType)) + return true; + } + + return false; +} + +TStacks CBattleInfoEssentials::battleGetAllStacks(bool includeTurrets /*= false*/) const +{ + return battleGetStacksIf([=](const CStack * s) + { + return !s->isGhost() && (includeTurrets || !s->isTurret()); + }); +} + +TStacks CBattleInfoEssentials::battleGetStacksIf(TStackFilter predicate) const +{ + TStacks ret; + RETURN_IF_NOT_BATTLE(ret); + + vstd::copy_if(getBattle()->stacks, std::back_inserter(ret), predicate); + + return ret; +} + +TStacks CBattleInfoEssentials::battleAliveStacks() const +{ + return battleGetStacksIf([](const CStack * s){ + return s->isValidTarget(false); + }); +} + +TStacks CBattleInfoEssentials::battleAliveStacks(ui8 side) const +{ + return battleGetStacksIf([=](const CStack * s){ + return s->isValidTarget(false) && s->attackerOwned == !side; + }); +} + +int CBattleInfoEssentials::battleGetMoatDmg() const +{ + RETURN_IF_NOT_BATTLE(0); + auto town = getBattle()->town; + if(!town) + return 0; + return town->town->moatDamage; +} + +const CGTownInstance * CBattleInfoEssentials::battleGetDefendedTown() const +{ + RETURN_IF_NOT_BATTLE(nullptr); + if(!getBattle() || getBattle()->town == nullptr) + return nullptr; + return getBattle()->town; +} + +BattlePerspective::BattlePerspective CBattleInfoEssentials::battleGetMySide() const +{ + RETURN_IF_NOT_BATTLE(BattlePerspective::INVALID); + if(!player || player.get().isSpectator()) + return BattlePerspective::ALL_KNOWING; + if(*player == getBattle()->sides[0].color) + return BattlePerspective::LEFT_SIDE; + if(*player == getBattle()->sides[1].color) + return BattlePerspective::RIGHT_SIDE; + + logGlobal->errorStream() << "Cannot find player " << *player << " in battle!"; + return BattlePerspective::INVALID; +} + +const CStack * CBattleInfoEssentials::battleActiveStack() const +{ + RETURN_IF_NOT_BATTLE(nullptr); + return battleGetStackByID(getBattle()->activeStack); +} + +const CStack* CBattleInfoEssentials::battleGetStackByID(int ID, bool onlyAlive) const +{ + RETURN_IF_NOT_BATTLE(nullptr); + + auto stacks = battleGetStacksIf([=](const CStack * s) + { + return s->ID == ID && (!onlyAlive || s->alive()); + }); + + if(stacks.empty()) + return nullptr; + else + return stacks[0]; +} + +bool CBattleInfoEssentials::battleDoWeKnowAbout(ui8 side) const +{ + RETURN_IF_NOT_BATTLE(false); + auto p = battleGetMySide(); + return p == BattlePerspective::ALL_KNOWING || p == side; +} + +si8 CBattleInfoEssentials::battleTacticDist() const +{ + RETURN_IF_NOT_BATTLE(0); + return getBattle()->tacticDistance; +} + +si8 CBattleInfoEssentials::battleGetTacticsSide() const +{ + RETURN_IF_NOT_BATTLE(-1); + return getBattle()->tacticsSide; +} + +const CGHeroInstance * CBattleInfoEssentials::battleGetFightingHero(ui8 side) const +{ + RETURN_IF_NOT_BATTLE(nullptr); + if(side > 1) + { + logGlobal->errorStream() << "FIXME: " << __FUNCTION__ << " wrong argument!"; + return nullptr; + } + + if(!battleDoWeKnowAbout(side)) + { + logGlobal->errorStream() << "FIXME: " << __FUNCTION__ << " access check "; + return nullptr; + } + + return getBattle()->sides[side].hero; +} + +const CArmedInstance * CBattleInfoEssentials::battleGetArmyObject(ui8 side) const +{ + RETURN_IF_NOT_BATTLE(nullptr); + if(side > 1) + { + logGlobal->errorStream() << "FIXME: " << __FUNCTION__ << " wrong argument!"; + return nullptr; + } + if(!battleDoWeKnowAbout(side)) + { + logGlobal->errorStream() << "FIXME: " << __FUNCTION__ << " access check "; + return nullptr; + } + return getBattle()->sides[side].armyObject; +} + +InfoAboutHero CBattleInfoEssentials::battleGetHeroInfo(ui8 side) const +{ + auto hero = getBattle()->sides[side].hero; + if(!hero) + { + logGlobal->warnStream() << __FUNCTION__ << ": side " << (int)side << " does not have hero!"; + return InfoAboutHero(); + } + InfoAboutHero::EInfoLevel infoLevel = battleDoWeKnowAbout(side) ? InfoAboutHero::EInfoLevel::DETAILED : InfoAboutHero::EInfoLevel::BASIC; + return InfoAboutHero(hero, infoLevel); +} + +int CBattleInfoEssentials::battleCastSpells(ui8 side) const +{ + RETURN_IF_NOT_BATTLE(-1); + return getBattle()->sides[side].castSpellsCount; +} + +const IBonusBearer * CBattleInfoEssentials::getBattleNode() const +{ + return getBattle(); +} + +bool CBattleInfoEssentials::battleCanFlee(PlayerColor player) const +{ + RETURN_IF_NOT_BATTLE(false); + const si8 mySide = playerToSide(player); + const CGHeroInstance *myHero = battleGetFightingHero(mySide); + + //current player have no hero + if(!myHero) + return false; + + //eg. one of heroes is wearing shakles of war + if(myHero->hasBonusOfType(Bonus::BATTLE_NO_FLEEING)) + return false; + + //we are besieged defender + if(mySide == BattleSide::DEFENDER && battleGetSiegeLevel()) + { + auto town = battleGetDefendedTown(); + if(!town->hasBuilt(BuildingID::ESCAPE_TUNNEL, ETownType::STRONGHOLD)) + return false; + } + + return true; +} + +si8 CBattleInfoEssentials::playerToSide(PlayerColor player) const +{ + RETURN_IF_NOT_BATTLE(-1); + int ret = vstd::find_pos_if(getBattle()->sides, [=](const SideInBattle &side){ return side.color == player; }); + if(ret < 0) + logGlobal->warnStream() << "Cannot find side for player " << player; + + return ret; +} + +bool CBattleInfoEssentials::playerHasAccessToHeroInfo(PlayerColor player, const CGHeroInstance * h) const +{ + RETURN_IF_NOT_BATTLE(false); + const si8 playerSide = playerToSide(player); + if (playerSide >= 0) + { + if (getBattle()->sides[!playerSide].hero == h) + return true; + } + return false; +} + +ui8 CBattleInfoEssentials::battleGetSiegeLevel() const +{ + RETURN_IF_NOT_BATTLE(0); + return getBattle()->town ? getBattle()->town->fortLevel() : CGTownInstance::NONE; +} + +bool CBattleInfoEssentials::battleCanSurrender(PlayerColor player) const +{ + RETURN_IF_NOT_BATTLE(false); + ui8 mySide = playerToSide(player); + bool iAmSiegeDefender = (mySide == BattleSide::DEFENDER && battleGetSiegeLevel()); + //conditions like for fleeing (except escape tunnel presence) + enemy must have a hero + return battleCanFlee(player) && !iAmSiegeDefender && battleHasHero(!mySide); +} + +bool CBattleInfoEssentials::battleHasHero(ui8 side) const +{ + RETURN_IF_NOT_BATTLE(false); + assert(side < 2); + return getBattle()->sides[side].hero; +} + +si8 CBattleInfoEssentials::battleGetWallState(int partOfWall) const +{ + RETURN_IF_NOT_BATTLE(0); + if(getBattle()->town == nullptr || getBattle()->town->fortLevel() == CGTownInstance::NONE) + return EWallState::NONE; + + assert(partOfWall >= 0 && partOfWall < EWallPart::PARTS_COUNT); + return getBattle()->si.wallState[partOfWall]; +} + +EGateState CBattleInfoEssentials::battleGetGateState() const +{ + RETURN_IF_NOT_BATTLE(EGateState::NONE); + if(getBattle()->town == nullptr || getBattle()->town->fortLevel() == CGTownInstance::NONE) + return EGateState::NONE; + + return getBattle()->si.gateState; +} + +PlayerColor CBattleInfoEssentials::battleGetOwner(const CStack * stack) const +{ + RETURN_IF_NOT_BATTLE(PlayerColor::CANNOT_DETERMINE); + if(stack->hasBonusOfType(Bonus::HYPNOTIZED)) + return getBattle()->theOtherPlayer(stack->owner); + else + return stack->owner; +} + +const CGHeroInstance * CBattleInfoEssentials::battleGetOwnerHero(const CStack * stack) const +{ + RETURN_IF_NOT_BATTLE(nullptr); + return getBattle()->sides.at(playerToSide(battleGetOwner(stack))).hero; +} + +bool CBattleInfoEssentials::battleMatchOwner(const CStack * attacker, const CStack * defender, const boost::logic::tribool positivness /* = false*/) const +{ + RETURN_IF_NOT_BATTLE(false); + if(boost::logic::indeterminate(positivness)) + return true; + else if(defender->owner != battleGetOwner(defender)) + return true; //mind controlled unit is attackable for both sides + else + return (battleGetOwner(attacker) == battleGetOwner(defender)) == positivness; +} diff --git a/lib/battle/CBattleInfoEssentials.h b/lib/battle/CBattleInfoEssentials.h new file mode 100644 index 000000000..7eaa37517 --- /dev/null +++ b/lib/battle/CBattleInfoEssentials.h @@ -0,0 +1,108 @@ +/* + * CBattleInfoEssentials.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 "CCallbackBase.h" + +class CGTownInstance; +class CGHeroInstance; +class CStack; +struct CObstacleInstance; +class IBonusBearer; +struct InfoAboutHero; +class CArmedInstance; + +typedef std::vector TStacks; +typedef std::function TStackFilter; + +namespace BattlePerspective +{ + enum BattlePerspective + { + INVALID = -2, + ALL_KNOWING = -1, + LEFT_SIDE, + RIGHT_SIDE + }; +} + +namespace BattleSide +{ + enum {ATTACKER = 0, DEFENDER = 1}; +} + + +class DLL_LINKAGE CBattleInfoEssentials : public virtual CCallbackBase +{ +protected: + bool battleDoWeKnowAbout(ui8 side) const; + const IBonusBearer * getBattleNode() const; +public: + enum EStackOwnership + { + ONLY_MINE, ONLY_ENEMY, MINE_AND_ENEMY + }; + + BattlePerspective::BattlePerspective battleGetMySide() const; + + ETerrainType battleTerrainType() const; + BFieldType battleGetBattlefieldType() const; + std::vector > battleGetAllObstacles(boost::optional perspective = boost::none) const; //returns all obstacles on the battlefield + + /** @brief Main method for getting battle stacks + * + * @param predicate Functor that shall return true for desired stack + * @return filtered stacks + * + */ + TStacks battleGetStacksIf(TStackFilter predicate) const; + + bool battleHasNativeStack(ui8 side) const; + int battleGetMoatDmg() const; //what dmg unit will suffer if ending turn in the moat + const CGTownInstance * battleGetDefendedTown() const; //returns defended town if current battle is a siege, nullptr instead + const CStack *battleActiveStack() const; + si8 battleTacticDist() const; //returns tactic distance in current tactics phase; 0 if not in tactics phase + si8 battleGetTacticsSide() const; //returns which side is in tactics phase, undefined if none (?) + bool battleCanFlee(PlayerColor player) const; + bool battleCanSurrender(PlayerColor player) const; + si8 playerToSide(PlayerColor player) const; + bool playerHasAccessToHeroInfo(PlayerColor player, const CGHeroInstance * h) const; + ui8 battleGetSiegeLevel() const; //returns 0 when there is no siege, 1 if fort, 2 is citadel, 3 is castle + bool battleHasHero(ui8 side) const; + int battleCastSpells(ui8 side) const; //how many spells has given side cast + const CGHeroInstance * battleGetFightingHero(ui8 side) const; //depracated for players callback, easy to get wrong + const CArmedInstance * battleGetArmyObject(ui8 side) const; + InfoAboutHero battleGetHeroInfo(ui8 side) const; + + // for determining state of a part of the wall; format: parameter [0] - keep, [1] - bottom tower, [2] - bottom wall, + // [3] - below gate, [4] - over gate, [5] - upper wall, [6] - uppert tower, [7] - gate; returned value: 1 - intact, 2 - damaged, 3 - destroyed; 0 - no battle + si8 battleGetWallState(int partOfWall) const; + EGateState battleGetGateState() const; + + //helpers + ///returns all stacks, alive or dead or undead or mechanical :) + TStacks battleGetAllStacks(bool includeTurrets = false) const; + + ///returns all alive stacks excluding turrets + TStacks battleAliveStacks() const; + ///returns all alive stacks from particular side excluding turrets + TStacks battleAliveStacks(ui8 side) const; + const CStack * battleGetStackByID(int ID, bool onlyAlive = true) const; //returns stack info by given ID + bool battleIsObstacleVisibleForSide(const CObstacleInstance & coi, BattlePerspective::BattlePerspective side) const; + + ///returns player that controls given stack; mind control included + PlayerColor battleGetOwner(const CStack * stack) const; + + ///returns hero that controls given stack; nullptr if none; mind control included + const CGHeroInstance * battleGetOwnerHero(const CStack * stack) const; + + ///check that stacks are controlled by same|other player(s) depending on positiveness + ///mind control included + bool battleMatchOwner(const CStack * attacker, const CStack * defender, const boost::logic::tribool positivness = false) const; +}; diff --git a/lib/battle/CCallbackBase.cpp b/lib/battle/CCallbackBase.cpp new file mode 100644 index 000000000..fdddaf636 --- /dev/null +++ b/lib/battle/CCallbackBase.cpp @@ -0,0 +1,42 @@ +/* + * CCallbackBase.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 "CCallbackBase.h" +#include "BattleInfo.h" +#include "CGameState.h" + +bool CCallbackBase::duringBattle() const +{ + return getBattle() != nullptr; +} + +const BattleInfo *CCallbackBase::getBattle() const +{ + return battle; +} + +CCallbackBase::CCallbackBase(CGameState * GS, boost::optional Player) + : battle(nullptr), gs(GS), player(Player) +{} + +CCallbackBase::CCallbackBase() + : battle(nullptr), gs(nullptr) +{} + +void CCallbackBase::setBattle(const BattleInfo * B) +{ + battle = B; +} + +boost::optional CCallbackBase::getPlayerID() const +{ + return player; +} + diff --git a/lib/battle/CCallbackBase.h b/lib/battle/CCallbackBase.h new file mode 100644 index 000000000..048d99ac6 --- /dev/null +++ b/lib/battle/CCallbackBase.h @@ -0,0 +1,42 @@ +/* + * CCallbackBase.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" + +#define RETURN_IF_NOT_BATTLE(X) if(!duringBattle()) {logGlobal->errorStream() << __FUNCTION__ << " called when no battle!"; return X; } + +class CGameState; +struct BattleInfo; + +class CBattleInfoEssentials; + +//Basic class for various callbacks (interfaces called by players to get info about game and so forth) +class DLL_LINKAGE CCallbackBase +{ + const BattleInfo * battle; //battle to which the player is engaged, nullptr if none or not applicable + + const BattleInfo * getBattle() const; + +protected: + CGameState * gs; + boost::optional player; // not set gives access to all information, otherwise callback provides only information "visible" for player + + CCallbackBase(CGameState * GS, boost::optional Player); + CCallbackBase(); + + void setBattle(const BattleInfo * B); + bool duringBattle() const; + +public: + boost::optional getPlayerID() const; + + friend class CBattleInfoEssentials; +}; + diff --git a/lib/CObstacleInstance.cpp b/lib/battle/CObstacleInstance.cpp similarity index 86% rename from lib/CObstacleInstance.cpp rename to lib/battle/CObstacleInstance.cpp index 5443f3d09..7c3e34f11 100644 --- a/lib/CObstacleInstance.cpp +++ b/lib/battle/CObstacleInstance.cpp @@ -69,7 +69,7 @@ std::vector CObstacleInstance::getAffectedTiles() const // bool CObstacleInstance::spellGenerated() const // { -// if(obstacleType == USUAL || obstacleType == ABSOLUTE_OBSTACLE) +// if(obstacleType == USUAL || obstacleType == ABSOLUTE_OBSTACLE) // return false; // // return true; @@ -83,12 +83,12 @@ bool CObstacleInstance::visibleForSide(ui8 side, bool hasNativeStack) const bool CObstacleInstance::stopsMovement() const { - return obstacleType == QUICKSAND || obstacleType == MOAT; + return obstacleType == QUICKSAND || obstacleType == MOAT; } bool CObstacleInstance::blocksTiles() const { - return obstacleType == USUAL || obstacleType == ABSOLUTE_OBSTACLE || obstacleType == FORCE_FIELD; + return obstacleType == USUAL || obstacleType == ABSOLUTE_OBSTACLE || obstacleType == FORCE_FIELD; } SpellCreatedObstacle::SpellCreatedObstacle() @@ -113,7 +113,7 @@ bool SpellCreatedObstacle::visibleForSide(ui8 side, bool hasNativeStack) const //we hide mines and not discovered quicksands //quicksands are visible to the caster or if owned unit stepped into that particular patch //additionally if side has a native unit, mines/quicksands will be visible - return casterSide == side || visibleForAnotherSide || hasNativeStack; + return casterSide == side || visibleForAnotherSide || hasNativeStack; default: assert(0); return false; diff --git a/lib/CObstacleInstance.h b/lib/battle/CObstacleInstance.h similarity index 100% rename from lib/CObstacleInstance.h rename to lib/battle/CObstacleInstance.h diff --git a/lib/battle/CPlayerBattleCallback.cpp b/lib/battle/CPlayerBattleCallback.cpp new file mode 100644 index 000000000..150a75f38 --- /dev/null +++ b/lib/battle/CPlayerBattleCallback.cpp @@ -0,0 +1,54 @@ +/* + * CPlayerBattleCallback.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 "CPlayerBattleCallback.h" +#include "CStack.h" +#include "CGameState.h" + +bool CPlayerBattleCallback::battleCanFlee() const +{ + RETURN_IF_NOT_BATTLE(false); + ASSERT_IF_CALLED_WITH_PLAYER + return CBattleInfoEssentials::battleCanFlee(*player); +} + +TStacks CPlayerBattleCallback::battleGetStacks(EStackOwnership whose /*= MINE_AND_ENEMY*/, bool onlyAlive /*= true*/) const +{ + if(whose != MINE_AND_ENEMY) + { + ASSERT_IF_CALLED_WITH_PLAYER + } + + return battleGetStacksIf([=](const CStack * s){ + const bool ownerMatches = (whose == MINE_AND_ENEMY) + || (whose == ONLY_MINE && s->owner == player) + || (whose == ONLY_ENEMY && s->owner != player); + + return ownerMatches && s->isValidTarget(!onlyAlive); + }); +} + +int CPlayerBattleCallback::battleGetSurrenderCost() const +{ + RETURN_IF_NOT_BATTLE(-3) + ASSERT_IF_CALLED_WITH_PLAYER + return CBattleInfoCallback::battleGetSurrenderCost(*player); +} + +const CGHeroInstance * CPlayerBattleCallback::battleGetMyHero() const +{ + return CBattleInfoEssentials::battleGetFightingHero(battleGetMySide()); +} + +InfoAboutHero CPlayerBattleCallback::battleGetEnemyHero() const +{ + return battleGetHeroInfo(!battleGetMySide()); +} + diff --git a/lib/battle/CPlayerBattleCallback.h b/lib/battle/CPlayerBattleCallback.h new file mode 100644 index 000000000..38ac80baa --- /dev/null +++ b/lib/battle/CPlayerBattleCallback.h @@ -0,0 +1,26 @@ +/* + * CPlayerBattleCallback.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 "CBattleInfoCallback.h" + +class CGHeroInstance; + +class DLL_LINKAGE CPlayerBattleCallback : public CBattleInfoCallback +{ +public: + bool battleCanFlee() const; //returns true if caller can flee from the battle + TStacks battleGetStacks(EStackOwnership whose = MINE_AND_ENEMY, bool onlyAlive = true) const; //returns stacks on battlefield + + int battleGetSurrenderCost() const; //returns cost of surrendering battle, -1 if surrendering is not possible + + const CGHeroInstance * battleGetMyHero() const; + InfoAboutHero battleGetEnemyHero() const; +}; + diff --git a/lib/battle/ReachabilityInfo.cpp b/lib/battle/ReachabilityInfo.cpp new file mode 100644 index 000000000..191ef21a0 --- /dev/null +++ b/lib/battle/ReachabilityInfo.cpp @@ -0,0 +1,43 @@ +/* + * ReachabilityInfo.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 "ReachabilityInfo.h" +#include "CStack.h" + + +ReachabilityInfo::Parameters::Parameters() +{ + stack = nullptr; + perspective = BattlePerspective::ALL_KNOWING; + attackerOwned = doubleWide = flying = false; +} + +ReachabilityInfo::Parameters::Parameters(const CStack * Stack) +{ + stack = Stack; + perspective = (BattlePerspective::BattlePerspective)(!Stack->attackerOwned); + startPosition = Stack->position; + doubleWide = stack->doubleWide(); + attackerOwned = stack->attackerOwned; + flying = stack->hasBonusOfType(Bonus::FLYING); + knownAccessible = stack->getHexes(); +} + +ReachabilityInfo::ReachabilityInfo() +{ + distances.fill(INFINITE_DIST); + predecessors.fill(BattleHex::INVALID); +} + +bool ReachabilityInfo::isReachable(BattleHex hex) const +{ + return distances[hex] < INFINITE_DIST; +} diff --git a/lib/battle/ReachabilityInfo.h b/lib/battle/ReachabilityInfo.h new file mode 100644 index 000000000..cf70ee375 --- /dev/null +++ b/lib/battle/ReachabilityInfo.h @@ -0,0 +1,52 @@ +/* + * ReachabilityInfo.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 "CBattleInfoEssentials.h" +#include "AccessibilityInfo.h" + +class CStack; + +// Reachability info is result of BFS calculation. It's dependent on stack (it's owner, whether it's flying), +// startPosition and perpective. +struct DLL_LINKAGE ReachabilityInfo +{ + typedef std::array TDistances; + typedef std::array TPredecessors; + + enum { INFINITE_DIST = 1000000 }; + + struct DLL_LINKAGE Parameters + { + const CStack * stack; //stack for which calculation is mage => not required (kept for debugging mostly), following variables are enough + + bool attackerOwned; + bool doubleWide; + bool flying; + std::vector knownAccessible; //hexes that will be treated as accessible, even if they're occupied by stack (by default - tiles occupied by stack we do reachability for, so it doesn't block itself) + + BattleHex startPosition; //assumed position of stack + BattlePerspective::BattlePerspective perspective; //some obstacles (eg. quicksands) may be invisible for some side + + Parameters(); + Parameters(const CStack * Stack); + }; + + Parameters params; + AccessibilityInfo accessibility; + TDistances distances; + TPredecessors predecessors; + + ReachabilityInfo(); + + bool isReachable(BattleHex hex) const; +}; + + diff --git a/lib/registerTypes/RegisterTypes.h b/lib/registerTypes/RegisterTypes.h index caca90f9c..e8e862b25 100644 --- a/lib/registerTypes/RegisterTypes.h +++ b/lib/registerTypes/RegisterTypes.h @@ -11,7 +11,7 @@ #include "../mapObjects/CRewardableConstructor.h" #include "../mapObjects/CommonConstructors.h" #include "../mapObjects/MapObjects.h" -#include "../CObstacleInstance.h" +#include "../battle/CObstacleInstance.h" /* * RegisterTypes.h, part of VCMI engine diff --git a/lib/registerTypes/TypesClientPacks2.cpp b/lib/registerTypes/TypesClientPacks2.cpp index a38b35f3a..086c823f0 100644 --- a/lib/registerTypes/TypesClientPacks2.cpp +++ b/lib/registerTypes/TypesClientPacks2.cpp @@ -3,7 +3,7 @@ #include "../StartInfo.h" #include "../CStack.h" -#include "../BattleInfo.h" +#include "../battle/BattleInfo.h" #include "../CGameState.h" #include "../mapping/CMap.h" #include "../CModHandler.h" diff --git a/lib/registerTypes/TypesMapObjects2.cpp b/lib/registerTypes/TypesMapObjects2.cpp index 03184fded..b4156c7cd 100644 --- a/lib/registerTypes/TypesMapObjects2.cpp +++ b/lib/registerTypes/TypesMapObjects2.cpp @@ -3,7 +3,7 @@ #include "../StartInfo.h" #include "../CStack.h" -#include "../BattleInfo.h" +#include "../battle/BattleInfo.h" #include "../CGameState.h" #include "../mapping/CMap.h" #include "../CModHandler.h" diff --git a/lib/spells/BattleSpellMechanics.cpp b/lib/spells/BattleSpellMechanics.cpp index 2b29f98a7..b4c69cbb4 100644 --- a/lib/spells/BattleSpellMechanics.cpp +++ b/lib/spells/BattleSpellMechanics.cpp @@ -13,7 +13,7 @@ #include "../NetPacks.h" #include "../CStack.h" -#include "../BattleInfo.h" +#include "../battle/BattleInfo.h" #include "../mapObjects/CGHeroInstance.h" #include "../mapObjects/CGTownInstance.h" diff --git a/lib/spells/CDefaultSpellMechanics.cpp b/lib/spells/CDefaultSpellMechanics.cpp index 4bd4ee546..185bdd0ab 100644 --- a/lib/spells/CDefaultSpellMechanics.cpp +++ b/lib/spells/CDefaultSpellMechanics.cpp @@ -13,7 +13,7 @@ #include "CDefaultSpellMechanics.h" #include "../CStack.h" -#include "../BattleInfo.h" +#include "../battle/BattleInfo.h" #include "../CGeneralTextHandler.h" diff --git a/lib/spells/CSpellHandler.cpp b/lib/spells/CSpellHandler.cpp index 38e20c3fd..648d50d8e 100644 --- a/lib/spells/CSpellHandler.cpp +++ b/lib/spells/CSpellHandler.cpp @@ -23,8 +23,8 @@ #include "../StringConstants.h" #include "../CStack.h" -#include "../BattleInfo.h" -#include "../CBattleCallback.h" +#include "../battle/BattleInfo.h" +#include "../battle/CBattleInfoCallback.h" #include "../CGameState.h" //todo: remove #include "../NetPacks.h" //todo: remove diff --git a/lib/spells/CSpellHandler.h b/lib/spells/CSpellHandler.h index a60d84d68..03fc8f26e 100644 --- a/lib/spells/CSpellHandler.h +++ b/lib/spells/CSpellHandler.h @@ -14,7 +14,7 @@ #include "../ConstTransitivePtr.h" #include "../int3.h" #include "../GameConstants.h" -#include "../BattleHex.h" +#include "../battle/BattleHex.h" #include "../HeroBonus.h" class CGObjectInstance; diff --git a/lib/spells/CreatureSpellMechanics.cpp b/lib/spells/CreatureSpellMechanics.cpp index 7ed5ace1e..c9c1ee665 100644 --- a/lib/spells/CreatureSpellMechanics.cpp +++ b/lib/spells/CreatureSpellMechanics.cpp @@ -14,7 +14,7 @@ #include "../NetPacks.h" #include "../CStack.h" -#include "../BattleInfo.h" +#include "../battle/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 9ffc6ec1b..b1f961740 100644 --- a/lib/spells/ISpellMechanics.cpp +++ b/lib/spells/ISpellMechanics.cpp @@ -12,7 +12,7 @@ #include "ISpellMechanics.h" #include "../CStack.h" -#include "../BattleInfo.h" +#include "../battle/BattleInfo.h" #include "../NetPacks.h" diff --git a/lib/spells/ISpellMechanics.h b/lib/spells/ISpellMechanics.h index 3ee4059d1..6bf0fc499 100644 --- a/lib/spells/ISpellMechanics.h +++ b/lib/spells/ISpellMechanics.h @@ -11,7 +11,7 @@ #pragma once #include "CSpellHandler.h" -#include "../BattleHex.h" +#include "../battle/BattleHex.h" ///callback to be provided by server diff --git a/server/CGameHandler.cpp b/server/CGameHandler.cpp index d258e0084..d0428f00d 100644 --- a/server/CGameHandler.cpp +++ b/server/CGameHandler.cpp @@ -16,7 +16,7 @@ #include "../lib/CCreatureHandler.h" #include "../lib/CGameState.h" #include "../lib/CStack.h" -#include "../lib/BattleInfo.h" +#include "../lib/battle/BattleInfo.h" #include "../lib/CondSh.h" #include "../lib/NetPacks.h" #include "../lib/VCMI_Lib.h" diff --git a/server/CGameHandler.h b/server/CGameHandler.h index 6a04c6d50..485efc93e 100644 --- a/server/CGameHandler.h +++ b/server/CGameHandler.h @@ -3,7 +3,7 @@ #include "../lib/FunctionList.h" #include "../lib/IGameCallback.h" -#include "../lib/BattleAction.h" +#include "../lib/battle/BattleAction.h" #include "CQuery.h" diff --git a/server/CQuery.cpp b/server/CQuery.cpp index 773283689..f9a35e071 100644 --- a/server/CQuery.cpp +++ b/server/CQuery.cpp @@ -1,7 +1,7 @@ #include "StdInc.h" #include "CQuery.h" #include "CGameHandler.h" -#include "../lib/BattleInfo.h" +#include "../lib/battle/BattleInfo.h" #include "../lib/mapObjects/MiscObjects.h" boost::mutex Queries::mx; diff --git a/server/NetPacksServer.cpp b/server/NetPacksServer.cpp index b5ed4d514..c7b8c3b1b 100644 --- a/server/NetPacksServer.cpp +++ b/server/NetPacksServer.cpp @@ -6,8 +6,8 @@ #include "../lib/mapping/CMap.h" #include "../lib/CGameState.h" #include "../lib/CStack.h" -#include "../lib/BattleInfo.h" -#include "../lib/BattleAction.h" +#include "../lib/battle/BattleInfo.h" +#include "../lib/battle/BattleAction.h" #include "../lib/serializer/Connection.h" diff --git a/test/Battlefield.cpp b/test/Battlefield.cpp index b1bd02f69..a60726bd1 100644 --- a/test/Battlefield.cpp +++ b/test/Battlefield.cpp @@ -9,7 +9,7 @@ */ #include "StdInc.h" #include -#include "../lib/BattleHex.h" +#include "../lib/battle/BattleHex.h" BOOST_AUTO_TEST_SUITE(BattlefieldHex_Suite) BOOST_AUTO_TEST_CASE(getNeighbouringTiles)