diff --git a/AI/Nullkiller/AIGateway.cpp b/AI/Nullkiller/AIGateway.cpp index ea7113458..f43f29132 100644 --- a/AI/Nullkiller/AIGateway.cpp +++ b/AI/Nullkiller/AIGateway.cpp @@ -19,6 +19,7 @@ #include "../../lib/mapObjects/MapObjects.h" #include "../../lib/mapObjects/ObjectTemplate.h" #include "../../lib/mapObjects/CGHeroInstance.h" +#include "../../lib/mapping/TerrainTile.h" #include "../../lib/CConfigHandler.h" #include "../../lib/IGameSettings.h" #include "../../lib/gameState/CGameState.h" diff --git a/AI/Nullkiller/AIUtility.cpp b/AI/Nullkiller/AIUtility.cpp index f6919ef46..6b2c389d4 100644 --- a/AI/Nullkiller/AIUtility.cpp +++ b/AI/Nullkiller/AIUtility.cpp @@ -17,7 +17,7 @@ #include "../../lib/entities/artifact/CArtifact.h" #include "../../lib/mapObjects/MapObjects.h" #include "../../lib/mapObjects/CQuest.h" -#include "../../lib/mapping/CMapDefines.h" +#include "../../lib/mapping/TerrainTile.h" #include "../../lib/gameState/QuestInfo.h" #include "../../lib/IGameSettings.h" #include "../../lib/bonuses/Limiters.h" diff --git a/AI/Nullkiller/Analyzers/ArmyManager.cpp b/AI/Nullkiller/Analyzers/ArmyManager.cpp index fd0951568..418a64cd8 100644 --- a/AI/Nullkiller/Analyzers/ArmyManager.cpp +++ b/AI/Nullkiller/Analyzers/ArmyManager.cpp @@ -12,7 +12,7 @@ #include "ArmyManager.h" #include "../Engine/Nullkiller.h" #include "../../../lib/mapObjects/MapObjects.h" -#include "../../../lib/mapping/CMapDefines.h" +#include "../../../lib/mapping/TerrainTile.h" #include "../../../lib/IGameSettings.h" #include "../../../lib/GameConstants.h" #include "../../../lib/TerrainHandler.h" diff --git a/AI/Nullkiller/Engine/PriorityEvaluator.cpp b/AI/Nullkiller/Engine/PriorityEvaluator.cpp index 6c189492d..668057fe0 100644 --- a/AI/Nullkiller/Engine/PriorityEvaluator.cpp +++ b/AI/Nullkiller/Engine/PriorityEvaluator.cpp @@ -15,7 +15,7 @@ #include "../../../lib/mapObjectConstructors/AObjectTypeHandler.h" #include "../../../lib/mapObjectConstructors/CObjectClassesHandler.h" #include "../../../lib/mapObjects/CGResource.h" -#include "../../../lib/mapping/CMapDefines.h" +#include "../../../lib/mapping/TerrainTile.h" #include "../../../lib/RoadHandler.h" #include "../../../lib/CCreatureHandler.h" #include "../../../lib/GameLibrary.h" diff --git a/AI/Nullkiller/Pathfinding/Actors.cpp b/AI/Nullkiller/Pathfinding/Actors.cpp index 77070808b..7df7776e3 100644 --- a/AI/Nullkiller/Pathfinding/Actors.cpp +++ b/AI/Nullkiller/Pathfinding/Actors.cpp @@ -12,7 +12,7 @@ #include "../AIGateway.h" #include "../Engine/Nullkiller.h" #include "../../../lib/mapObjects/MapObjects.h" -#include "../../../lib/mapping/CMapDefines.h" +#include "../../../lib/mapping/TerrainTile.h" #include "../../../lib/pathfinder/TurnInfo.h" #include "Actions/BuyArmyAction.h" diff --git a/AI/VCAI/AIUtility.cpp b/AI/VCAI/AIUtility.cpp index 26847b838..6f412aabd 100644 --- a/AI/VCAI/AIUtility.cpp +++ b/AI/VCAI/AIUtility.cpp @@ -18,7 +18,7 @@ #include "../../lib/entities/artifact/CArtifact.h" #include "../../lib/mapObjects/CGTownInstance.h" #include "../../lib/mapObjects/CQuest.h" -#include "../../lib/mapping/CMapDefines.h" +#include "../../lib/mapping/TerrainTile.h" extern FuzzyHelper * fh; diff --git a/AI/VCAI/Pathfinding/AIPathfinder.cpp b/AI/VCAI/Pathfinding/AIPathfinder.cpp index 799842892..768373ff2 100644 --- a/AI/VCAI/Pathfinding/AIPathfinder.cpp +++ b/AI/VCAI/Pathfinding/AIPathfinder.cpp @@ -10,7 +10,8 @@ #include "StdInc.h" #include "AIPathfinder.h" #include "AIPathfinderConfig.h" -#include "../../../lib/mapping/CMapDefines.h" + +#include "../../../lib/mapping/TerrainTile.h" #include diff --git a/AI/VCAI/Pathfinding/PathfindingManager.cpp b/AI/VCAI/Pathfinding/PathfindingManager.cpp index 54e3a2993..ed289593b 100644 --- a/AI/VCAI/Pathfinding/PathfindingManager.cpp +++ b/AI/VCAI/Pathfinding/PathfindingManager.cpp @@ -14,7 +14,6 @@ #include "../Goals/Goals.h" #include "../Goals/CompleteQuest.h" #include "../../../lib/gameState/QuestInfo.h" -#include "../../../lib/mapping/CMapDefines.h" #include "../../../lib/mapObjects/CQuest.h" PathfindingManager::PathfindingManager(CPlayerSpecificInfoCallback * CB, VCAI * AI) diff --git a/AI/VCAI/VCAI.cpp b/AI/VCAI/VCAI.cpp index 41a4026ea..ad1d5f935 100644 --- a/AI/VCAI/VCAI.cpp +++ b/AI/VCAI/VCAI.cpp @@ -33,6 +33,7 @@ #include "../../lib/entities/artifact/CArtifact.h" #include "../../lib/entities/building/CBuilding.h" #include "../../lib/mapObjects/CQuest.h" +#include "../../lib/mapping/TerrainTile.h" #include "../../lib/networkPacks/PacksForClient.h" #include "../../lib/networkPacks/PacksForClientBattle.h" #include "../../lib/networkPacks/PacksForServer.h" diff --git a/client/CPlayerInterface.cpp b/client/CPlayerInterface.cpp index 59a538548..b33d0cd94 100644 --- a/client/CPlayerInterface.cpp +++ b/client/CPlayerInterface.cpp @@ -68,6 +68,7 @@ #include "../lib/callback/CDynLibHandler.h" #include "../lib/CConfigHandler.h" +#include "../lib/GameLibrary.h" #include "../lib/texts/CGeneralTextHandler.h" #include "../lib/CPlayerState.h" #include "../lib/CRandomGenerator.h" diff --git a/client/CServerHandler.cpp b/client/CServerHandler.cpp index 020e18ac7..99e24029d 100644 --- a/client/CServerHandler.cpp +++ b/client/CServerHandler.cpp @@ -32,6 +32,7 @@ #include "mainmenu/CHighScoreScreen.h" #include "../lib/CConfigHandler.h" +#include "../lib/GameLibrary.h" #include "../lib/texts/CGeneralTextHandler.h" #include "../lib/ConditionalWait.h" #include "../lib/CThreadHelper.h" diff --git a/client/Client.cpp b/client/Client.cpp index 54e9ce3ec..d80c82769 100644 --- a/client/Client.cpp +++ b/client/Client.cpp @@ -23,6 +23,7 @@ #include "mapView/mapHandler.h" #include "../lib/CConfigHandler.h" +#include "../lib/GameLibrary.h" #include "../lib/battle/BattleInfo.h" #include "../lib/battle/CPlayerBattleCallback.h" #include "../lib/callback/CCallback.h" @@ -35,8 +36,8 @@ #include "../lib/VCMIDirs.h" #include "../lib/UnlockGuard.h" #include "../lib/serializer/Connection.h" +#include "../lib/mapObjects/army/CArmedInstance.h" #include "../lib/mapping/CMapService.h" -#include "../lib/mapObjects/CArmedInstance.h" #include "../lib/pathfinder/CGPathNode.h" #include "../lib/filesystem/Filesystem.h" diff --git a/client/GameChatHandler.cpp b/client/GameChatHandler.cpp index 92e3ac002..efd9c88e0 100644 --- a/client/GameChatHandler.cpp +++ b/client/GameChatHandler.cpp @@ -21,7 +21,7 @@ #include "../lib/callback/CCallback.h" #include "../lib/networkPacks/PacksForLobby.h" -#include "../lib/mapObjects/CArmedInstance.h" +#include "../lib/mapObjects/army/CArmedInstance.h" #include "../lib/CConfigHandler.h" #include "../lib/GameLibrary.h" #include "../lib/texts/CGeneralTextHandler.h" diff --git a/client/HeroMovementController.cpp b/client/HeroMovementController.cpp index 97ae56c71..e4e5ef58b 100644 --- a/client/HeroMovementController.cpp +++ b/client/HeroMovementController.cpp @@ -26,6 +26,7 @@ #include "../lib/callback/CCallback.h" #include "../lib/pathfinder/CGPathNode.h" #include "../lib/mapObjects/CGHeroInstance.h" +#include "../lib/mapping/TerrainTile.h" #include "../lib/networkPacks/PacksForClient.h" #include "../lib/RoadHandler.h" #include "../lib/TerrainHandler.h" diff --git a/client/UIHelper.cpp b/client/UIHelper.cpp index e5b36213b..25bf7e19a 100644 --- a/client/UIHelper.cpp +++ b/client/UIHelper.cpp @@ -16,7 +16,6 @@ #include "../lib/mapObjects/CGHeroInstance.h" #include "../lib/networkPacks/ArtifactLocation.h" #include "../lib/CRandomGenerator.h" -#include "../lib/CCreatureSet.h" std::vector UIHelper::getArtifactsComponents(const CArtifactSet & artSet, const std::vector & movedPack) { diff --git a/client/adventureMap/AdventureMapInterface.cpp b/client/adventureMap/AdventureMapInterface.cpp index ebe264fcd..363889b48 100644 --- a/client/adventureMap/AdventureMapInterface.cpp +++ b/client/adventureMap/AdventureMapInterface.cpp @@ -37,6 +37,7 @@ #include "../PlayerLocalState.h" #include "../CPlayerInterface.h" +#include "../../lib/GameLibrary.h" #include "../../lib/IGameSettings.h" #include "../../lib/StartInfo.h" #include "../../lib/callback/CCallback.h" @@ -44,7 +45,6 @@ #include "../../lib/spells/CSpellHandler.h" #include "../../lib/mapObjects/CGHeroInstance.h" #include "../../lib/mapObjects/CGTownInstance.h" -#include "../../lib/mapping/CMapDefines.h" #include "../../lib/pathfinder/CGPathNode.h" #include "../../lib/pathfinder/TurnInfo.h" #include "../../lib/spells/ISpellMechanics.h" diff --git a/client/adventureMap/CInGameConsole.cpp b/client/adventureMap/CInGameConsole.cpp index 1afb65466..88294b574 100644 --- a/client/adventureMap/CInGameConsole.cpp +++ b/client/adventureMap/CInGameConsole.cpp @@ -29,7 +29,7 @@ #include "../../lib/CConfigHandler.h" #include "../../lib/CThreadHelper.h" -#include "../../lib/mapObjects/CArmedInstance.h" +#include "../../lib/texts/MetaString.h" #include "../../lib/texts/TextOperations.h" CInGameConsole::CInGameConsole() diff --git a/client/adventureMap/CMinimap.cpp b/client/adventureMap/CMinimap.cpp index df1e38de4..54c70d928 100644 --- a/client/adventureMap/CMinimap.cpp +++ b/client/adventureMap/CMinimap.cpp @@ -30,7 +30,7 @@ #include "../../lib/TerrainHandler.h" #include "../../lib/callback/CCallback.h" #include "../../lib/mapObjects/CGHeroInstance.h" -#include "../../lib/mapping/CMapDefines.h" +#include "../../lib/mapping/TerrainTile.h" #include "../../lib/texts/CGeneralTextHandler.h" ColorRGBA CMinimapInstance::getTileColor(const int3 & pos) const diff --git a/client/adventureMap/MapAudioPlayer.cpp b/client/adventureMap/MapAudioPlayer.cpp index 419c4e31a..8b5246fb0 100644 --- a/client/adventureMap/MapAudioPlayer.cpp +++ b/client/adventureMap/MapAudioPlayer.cpp @@ -20,7 +20,6 @@ #include "../../lib/CRandomGenerator.h" #include "../../lib/TerrainHandler.h" #include "../../lib/callback/CCallback.h" -#include "../../lib/mapObjects/CArmedInstance.h" #include "../../lib/mapObjects/CGHeroInstance.h" #include "../../lib/mapping/CMap.h" diff --git a/client/mapView/MapRenderer.cpp b/client/mapView/MapRenderer.cpp index f7c2c5d32..beeeb71d3 100644 --- a/client/mapView/MapRenderer.cpp +++ b/client/mapView/MapRenderer.cpp @@ -29,7 +29,7 @@ #include "../../lib/mapObjects/CGHeroInstance.h" #include "../../lib/mapObjects/MiscObjects.h" #include "../../lib/mapObjects/ObjectTemplate.h" -#include "../../lib/mapping/CMapDefines.h" +#include "../../lib/mapping/TerrainTile.h" #include "../../lib/pathfinder/CGPathNode.h" struct NeighborTilesInfo diff --git a/lib/CCreatureSet.h b/lib/CCreatureSet.h deleted file mode 100644 index b90c34378..000000000 --- a/lib/CCreatureSet.h +++ /dev/null @@ -1,356 +0,0 @@ -/* - * CCreatureSet.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 "CCreatureHandler.h" -#include "GameConstants.h" -#include "bonuses/Bonus.h" -#include "bonuses/BonusCache.h" -#include "bonuses/CBonusSystemNode.h" -#include "callback/GameCallbackHolder.h" -#include "serializer/Serializeable.h" -#include "mapObjects/CGObjectInstance.h" -#include "entities/artifact/CArtifactSet.h" - -#include - -VCMI_LIB_NAMESPACE_BEGIN - -class JsonNode; -class CCreature; -class CGHeroInstance; -class CArmedInstance; -class CCreatureArtifactSet; -class JsonSerializeFormat; - -class DLL_LINKAGE CStackBasicDescriptor -{ - CreatureID typeID; - TQuantity count = -1; //exact quantity or quantity ID from CCreature::getQuantityID when getting info about enemy army - -public: - CStackBasicDescriptor(); - CStackBasicDescriptor(const CreatureID & id, TQuantity Count); - CStackBasicDescriptor(const CCreature *c, TQuantity Count); - virtual ~CStackBasicDescriptor() = default; - - const Creature * getType() const; - const CCreature * getCreature() const; - CreatureID getId() const; - TQuantity getCount() const; - - virtual void setType(const CCreature * c); - virtual void setCount(TQuantity amount); - - friend bool operator== (const CStackBasicDescriptor & l, const CStackBasicDescriptor & r); - - template void serialize(Handler &h) - { - if(h.saving) - { - h & typeID; - } - else - { - CreatureID creatureID; - h & creatureID; - if(creatureID != CreatureID::NONE) - setType(creatureID.toCreature()); - } - - h & count; - } - - void serializeJson(JsonSerializeFormat & handler); -}; - -class DLL_LINKAGE CStackInstance : public CBonusSystemNode, public CStackBasicDescriptor, public CArtifactSet, public ACreature, public GameCallbackHolder -{ - BonusValueCache nativeTerrain; - BonusValueCache initiative; - - CArmedInstance * armyInstance = nullptr; //stack must be part of some army, army must be part of some object - - IGameInfoCallback * getCallback() const final { return cb; } - - TExpType totalExperience;//commander needs same amount of exp as hero -public: - struct RandomStackInfo - { - uint8_t level; - uint8_t upgrade; - }; - // helper variable used during loading map, when object (hero or town) have creatures that must have same alignment. - std::optional randomStack; - - CArmedInstance * getArmy(); - const CArmedInstance * getArmy() const; //stack must be part of some army, army must be part of some object - void setArmy(CArmedInstance *ArmyObj); - - TExpType getTotalExperience() const; - TExpType getAverageExperience() const; - virtual bool canGainExperience() const; - - template void serialize(Handler &h) - { - h & static_cast(*this); - h & static_cast(*this); - h & static_cast(*this); - - if (h.hasFeature(Handler::Version::STACK_INSTANCE_ARMY_FIX)) - { - // no-op - } - if (h.hasFeature(Handler::Version::NO_RAW_POINTERS_IN_SERIALIZER)) - { - ObjectInstanceID dummyID; - h & dummyID; - } - else - { - std::shared_ptr army; - h & army; - } - - h & totalExperience; - if (!h.hasFeature(Handler::Version::STACK_INSTANCE_EXPERIENCE_FIX)) - { - totalExperience *= getCount(); - } - } - - void serializeJson(JsonSerializeFormat & handler); - - //overrides CBonusSystemNode - std::string bonusToString(const std::shared_ptr& bonus) const override; // how would bonus description look for this particular type of node - ImagePath bonusToGraphics(const std::shared_ptr & bonus) const; //file name of graphics from StackSkills , in future possibly others - - //IConstBonusProvider - const IBonusBearer* getBonusBearer() const override; - //INativeTerrainProvider - FactionID getFactionID() const override; - - virtual ui64 getPower() const; - /// Returns total market value of resources needed to recruit this unit - virtual ui64 getMarketValue() const; - CCreature::CreatureQuantityId getQuantityID() const; - std::string getQuantityTXT(bool capitalized = true) const; - virtual int getExpRank() const; - virtual int getLevel() const; //different for regular stack and commander - CreatureID getCreatureID() const; //-1 if not available - std::string getName() const; //plural or singular - CStackInstance(IGameInfoCallback *cb); - CStackInstance(IGameInfoCallback *cb, BonusNodeType nodeType, bool isHypothetic = false); - CStackInstance(IGameInfoCallback *cb, const CreatureID & id, TQuantity count, bool isHypothetic = false); - virtual ~CStackInstance() = default; - - void setType(const CreatureID & creID); - void setType(const CCreature * c) final; - void setCount(TQuantity amount) final; - - /// Gives specified amount of stack experience that will not be scaled by unit size - void giveAverageStackExperience(TExpType exp); - void giveTotalStackExperience(TExpType exp); - - bool valid(bool allowUnrandomized) const; - ArtPlacementMap putArtifact(const ArtifactPosition & pos, const CArtifactInstance * art) override;//from CArtifactSet - void removeArtifact(const ArtifactPosition & pos) override; - ArtBearer bearerType() const override; //from CArtifactSet - std::string nodeName() const override; //from CBonusSystemnode - PlayerColor getOwner() const override; - - int32_t getInitiative(int turn = 0) const final; - TerrainId getNativeTerrain() const final; - TerrainId getCurrentTerrain() const; -}; - -class DLL_LINKAGE CCommanderInstance : public CStackInstance -{ -public: - //TODO: what if Commander is not a part of creature set? - - //commander class is determined by its base creature - ui8 alive; //maybe change to bool when breaking save compatibility? - ui8 level; //required only to count callbacks - std::string name; // each Commander has different name - std::vector secondarySkills; //ID -> level - std::set specialSkills; - //std::vector arts; - CCommanderInstance(IGameInfoCallback *cb); - CCommanderInstance(IGameInfoCallback *cb, const CreatureID & id); - void setAlive (bool alive); - void levelUp (); - - bool canGainExperience() const override; - bool gainsLevel() const; //true if commander has lower level than should upon his experience - ui64 getPower() const override {return 0;}; - int getExpRank() const override; - int getLevel() const override; - ArtBearer bearerType() const override; //from CArtifactSet - - template void serialize(Handler &h) - { - h & static_cast(*this); - h & alive; - h & level; - h & name; - h & secondarySkills; - h & specialSkills; - } -}; - -using TSlots = std::map>; -using TSimpleSlots = std::map>; - -using TPairCreatureSlot = std::pair; -using TMapCreatureSlot = std::map; - -struct DLL_LINKAGE CreatureSlotComparer -{ - bool operator()(const TPairCreatureSlot & lhs, const TPairCreatureSlot & rhs); -}; - -using TCreatureQueue = std::priority_queue, CreatureSlotComparer>; - -class IArmyDescriptor -{ -public: - virtual void clearSlots() = 0; - virtual bool setCreature(SlotID slot, CreatureID cre, TQuantity count) = 0; -}; - -//simplified version of CCreatureSet -class DLL_LINKAGE CSimpleArmy : public IArmyDescriptor -{ -public: - TSimpleSlots army; - void clearSlots() override; - bool setCreature(SlotID slot, CreatureID cre, TQuantity count) override; - operator bool() const; - - template void serialize(Handler &h) - { - h & army; - } -}; - -namespace NArmyFormation -{ - static const std::vector names{ "wide", "tight" }; -} - -class DLL_LINKAGE CCreatureSet : public IArmyDescriptor, public virtual Serializeable //seven combined creatures -{ - CCreatureSet(const CCreatureSet &) = delete; - CCreatureSet &operator=(const CCreatureSet&); - -public: - TSlots stacks; //slots[slot_id]->> pair(creature_id,creature_quantity) - EArmyFormation formation = EArmyFormation::LOOSE; //0 - wide, 1 - tight - - CCreatureSet() = default; //Should be here to avoid compile errors - virtual ~CCreatureSet(); - virtual void armyChanged(); - - const CStackInstance & operator[](const SlotID & slot) const; - - const TSlots &Slots() const {return stacks;} - - void addToSlot(const SlotID & slot, const CreatureID & cre, TQuantity count, bool allowMerging = true); //Adds stack to slot. Slot must be empty or with same type creature - void addToSlot(const SlotID & slot, std::unique_ptr stack, bool allowMerging = true); //Adds stack to slot. Slot must be empty or with same type creature - void clearSlots() override; - void setFormation(EArmyFormation tight); - virtual CArmedInstance * getArmy() { return nullptr; } - virtual const CArmedInstance * getArmy() const { return nullptr; } - - //basic operations - void putStack(const SlotID & slot, std::unique_ptr stack); //adds new stack to the army, slot must be empty - void setStackCount(const SlotID & slot, TQuantity count); //stack must exist! - std::unique_ptr detachStack(const SlotID & slot); //removes stack from army but doesn't destroy it (so it can be moved somewhere else or safely deleted) - void setStackType(const SlotID & slot, const CreatureID & type); - - /// Give specified amount of experience to all units in army - /// Amount of granted experience is scaled by unit stack size - void giveAverageStackExperience(TExpType exp); - - /// Give specified amount of experience to unit in specified slot - /// Amount of granted experience is not scaled by unit stack size - void giveTotalStackExperience(const SlotID & slot, TExpType exp); - - /// Erased stack from specified slot. Slot must be non-empty - void eraseStack(const SlotID & slot); - - /// Joins stack into stack that occupies targeted slot. - /// Slot must be non-empty and contain same creature type - void joinStack(const SlotID & slot, std::unique_ptr stack); //adds new stack to the existing stack of the same type - - /// Splits off some units of specified stack and returns newly created stack - /// Slot must be non-empty and contain more units that split quantity - std::unique_ptr splitStack(const SlotID & slot, TQuantity toSplit); - - void changeStackCount(const SlotID & slot, TQuantity toAdd); //stack must exist! - bool setCreature (SlotID slot, CreatureID type, TQuantity quantity) override; //replaces creature in stack; slots 0 to 6, if quantity=0 erases stack - void setToArmy(CSimpleArmy &src); //erases all our army and moves stacks from src to us; src MUST NOT be an armed object! WARNING: use it wisely. Or better do not use at all. - - const CStackInstance & getStack(const SlotID & slot) const; //stack must exist - CStackInstance * getStackPtr(const SlotID & slot) const; //if stack doesn't exist, returns nullptr - const CCreature * getCreature(const SlotID & slot) const; //workaround of map issue; - int getStackCount(const SlotID & slot) const; - TExpType getStackTotalExperience(const SlotID & slot) const; - TExpType getStackAverageExperience(const SlotID & slot) const; - SlotID findStack(const CStackInstance *stack) const; //-1 if none - SlotID getSlotFor(const CreatureID & creature, ui32 slotsAmount = GameConstants::ARMY_SIZE) const; //returns -1 if no slot available - SlotID getSlotFor(const CCreature *c, ui32 slotsAmount = GameConstants::ARMY_SIZE) const; //returns -1 if no slot available - bool hasCreatureSlots(const CCreature * c, const SlotID & exclude) const; - std::vector getCreatureSlots(const CCreature * c, const SlotID & exclude, TQuantity ignoreAmount = -1) const; - bool isCreatureBalanced(const CCreature* c, TQuantity ignoreAmount = 1) const; // Check if the creature is evenly distributed across slots - - SlotID getFreeSlot(ui32 slotsAmount = GameConstants::ARMY_SIZE) const; //returns first free slot - std::vector getFreeSlots(ui32 slotsAmount = GameConstants::ARMY_SIZE) const; - std::queue getFreeSlotsQueue(ui32 slotsAmount = GameConstants::ARMY_SIZE) const; - - TMapCreatureSlot getCreatureMap() const; - TCreatureQueue getCreatureQueue(const SlotID & exclude) const; - - bool mergeableStacks(std::pair & out, const SlotID & preferable = SlotID()) const; //looks for two same stacks, returns slot positions; - bool validTypes(bool allowUnrandomized = false) const; //checks if all types of creatures are set properly - bool slotEmpty(const SlotID & slot) const; - int stacksCount() const; - virtual bool needsLastStack() const; //true if last stack cannot be taken - ui64 getArmyStrength(int fortLevel = 0) const; //sum of AI values of creatures - ui64 getArmyCost() const; //sum of cost of creatures - ui64 getPower(const SlotID & slot) const; //value of specific stack - std::string getRoughAmount(const SlotID & slot, int mode = 0) const; //rough size of specific stack - std::string getArmyDescription() const; - bool hasStackAtSlot(const SlotID & slot) const; - - bool contains(const CStackInstance *stack) const; - bool canBeMergedWith(const CCreatureSet &cs, bool allowMergingStacks = true) const; - - /// Returns true if this creature set contains all listed units - /// If requireLastStack is true, then this function will also - /// require presence of any unit other than requested (or more units than requested) - bool hasUnits(const std::vector & units, bool requireLastStack = true) const; - - template void serialize(Handler &h) - { - h & stacks; - h & formation; - } - - void serializeJson(JsonSerializeFormat & handler, const std::string & armyFieldName, const std::optional fixedSize = std::nullopt); - - operator bool() const - { - return !stacks.empty(); - } -}; - -VCMI_LIB_NAMESPACE_END diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt index 9405dfe8d..59658a3ae 100644 --- a/lib/CMakeLists.txt +++ b/lib/CMakeLists.txt @@ -142,7 +142,6 @@ set(lib_MAIN_SRCS mapObjectConstructors/HillFortInstanceConstructor.cpp mapObjectConstructors/ShipyardInstanceConstructor.cpp - mapObjects/CArmedInstance.cpp mapObjects/CGCreature.cpp mapObjects/CGDwelling.cpp mapObjects/CGHeroInstance.cpp @@ -162,6 +161,12 @@ set(lib_MAIN_SRCS mapObjects/ObjectTemplate.cpp mapObjects/ObstacleSetHandler.cpp + mapObjects/army/CArmedInstance.cpp + mapObjects/army/CCommanderInstance.cpp + mapObjects/army/CCreatureSet.cpp + mapObjects/army/CStackBasicDescriptor.cpp + mapObjects/army/CStackInstance.cpp + mapping/CDrawRoadsOperation.cpp mapping/CMap.cpp mapping/CMapHeader.cpp @@ -251,7 +256,6 @@ set(lib_MAIN_SRCS serializer/SerializerReflection.cpp spells/AbilityCaster.cpp - spells/AdventureSpellMechanics.cpp spells/BattleSpellMechanics.cpp spells/BonusCaster.cpp spells/CSpellHandler.cpp @@ -264,6 +268,13 @@ set(lib_MAIN_SRCS spells/TargetCondition.cpp spells/ViewSpellInt.cpp + spells/adventure/AdventureSpellMechanics.cpp + spells/adventure/DimensionDoorMechanics.cpp + spells/adventure/ScuttleBoatMechanics.cpp + spells/adventure/SummonBoatMechanics.cpp + spells/adventure/TownPortalMechanics.cpp + spells/adventure/ViewWorldMechanics.cpp + spells/effects/Catapult.cpp spells/effects/Clone.cpp spells/effects/Damage.cpp @@ -293,7 +304,6 @@ set(lib_MAIN_SRCS CAndroidVMHelper.cpp CBonusTypeHandler.cpp CCreatureHandler.cpp - CCreatureSet.cpp CPlayerState.cpp CRandomGenerator.cpp CScriptingModule.cpp @@ -558,7 +568,6 @@ set(lib_MAIN_HEADERS mapObjectConstructors/ShipyardInstanceConstructor.h mapObjectConstructors/SObjectSounds.h - mapObjects/CArmedInstance.h mapObjects/CGCreature.h mapObjects/CGDwelling.h mapObjects/CGHeroInstance.h @@ -581,9 +590,17 @@ set(lib_MAIN_HEADERS mapObjects/ObjectTemplate.h mapObjects/ObstacleSetHandler.h + mapObjects/army/CArmedInstance.h + mapObjects/army/CCommanderInstance.h + mapObjects/army/CCreatureSet.h + mapObjects/army/CSimpleArmy.h + mapObjects/army/CStackBasicDescriptor.h + mapObjects/army/CStackInstance.h + mapping/CDrawRoadsOperation.h - mapping/CMapDefines.h + mapping/CCastleEvent.h mapping/CMapEditManager.h + mapping/CMapEvent.h mapping/CMapHeader.h mapping/CMap.h mapping/CMapInfo.h @@ -598,6 +615,7 @@ set(lib_MAIN_HEADERS mapping/MapReaderH3M.h mapping/MapFormatJson.h mapping/ObstacleProxy.h + mapping/TerrainTile.h modding/ActiveModsInSaveList.h modding/CModHandler.h @@ -700,7 +718,6 @@ set(lib_MAIN_HEADERS serializer/SerializerReflection.h spells/AbilityCaster.h - spells/AdventureSpellMechanics.h spells/BattleSpellMechanics.h spells/BonusCaster.h spells/CSpellHandler.h @@ -713,6 +730,13 @@ set(lib_MAIN_HEADERS spells/TargetCondition.h spells/ViewSpellInt.h + spells/adventure/AdventureSpellMechanics.h + spells/adventure/DimensionDoorMechanics.h + spells/adventure/ScuttleBoatMechanics.h + spells/adventure/SummonBoatMechanics.h + spells/adventure/TownPortalMechanics.h + spells/adventure/ViewWorldMechanics.h + spells/effects/Catapult.h spells/effects/Clone.h spells/effects/Damage.h @@ -744,7 +768,6 @@ set(lib_MAIN_HEADERS CAndroidVMHelper.h CBonusTypeHandler.h CCreatureHandler.h - CCreatureSet.h ConditionalWait.h Color.h CPlayerState.h diff --git a/lib/battle/BattleLayout.cpp b/lib/battle/BattleLayout.cpp index 99e170134..a90f9e3c5 100644 --- a/lib/battle/BattleLayout.cpp +++ b/lib/battle/BattleLayout.cpp @@ -13,8 +13,8 @@ #include "../GameSettings.h" #include "../GameLibrary.h" #include "../callback/IGameInfoCallback.h" +#include "../mapObjects/army/CArmedInstance.h" #include "../json/JsonNode.h" -#include "../mapObjects/CArmedInstance.h" VCMI_LIB_NAMESPACE_BEGIN diff --git a/lib/battle/BattleStateInfoForRetreat.cpp b/lib/battle/BattleStateInfoForRetreat.cpp index a6f0118af..f933c0658 100644 --- a/lib/battle/BattleStateInfoForRetreat.cpp +++ b/lib/battle/BattleStateInfoForRetreat.cpp @@ -12,7 +12,6 @@ #include "BattleStateInfoForRetreat.h" #include "Unit.h" #include "CBattleInfoCallback.h" -#include "../CCreatureSet.h" #include "../mapObjects/CGHeroInstance.h" VCMI_LIB_NAMESPACE_BEGIN diff --git a/lib/battle/DamageCalculator.cpp b/lib/battle/DamageCalculator.cpp index 2d8020d2d..65ad1f772 100644 --- a/lib/battle/DamageCalculator.cpp +++ b/lib/battle/DamageCalculator.cpp @@ -15,6 +15,7 @@ #include "Unit.h" #include "../bonuses/Bonus.h" +#include "../CCreatureHandler.h" #include "../mapObjects/CGTownInstance.h" #include "../spells/CSpellHandler.h" #include "../IGameSettings.h" diff --git a/lib/bonuses/Bonus.cpp b/lib/bonuses/Bonus.cpp index 19bb36bfb..c4953edbe 100644 --- a/lib/bonuses/Bonus.cpp +++ b/lib/bonuses/Bonus.cpp @@ -16,7 +16,6 @@ #include "../CBonusTypeHandler.h" #include "../CCreatureHandler.h" -#include "../CCreatureSet.h" #include "../CSkillHandler.h" #include "../TerrainHandler.h" #include "../GameLibrary.h" diff --git a/lib/bonuses/Limiters.cpp b/lib/bonuses/Limiters.cpp index 70f9db377..567d86a98 100644 --- a/lib/bonuses/Limiters.cpp +++ b/lib/bonuses/Limiters.cpp @@ -16,7 +16,6 @@ #include "../GameLibrary.h" #include "../entities/faction/CTownHandler.h" #include "../CCreatureHandler.h" -#include "../CCreatureSet.h" #include "../CStack.h" #include "../TerrainHandler.h" #include "../constants/StringConstants.h" diff --git a/lib/gameState/CGameState.cpp b/lib/gameState/CGameState.cpp index 82af19c03..9a2c31e05 100644 --- a/lib/gameState/CGameState.cpp +++ b/lib/gameState/CGameState.cpp @@ -50,6 +50,7 @@ #include "../mapObjects/CGTownInstance.h" #include "../mapObjects/CQuest.h" #include "../mapObjects/MiscObjects.h" +#include "../mapping/CCastleEvent.h" #include "../mapping/CMap.h" #include "../mapping/CMapEditManager.h" #include "../mapping/CMapService.h" diff --git a/lib/gameState/EVictoryLossCheckResult.h b/lib/gameState/EVictoryLossCheckResult.h index f7860d78d..816faeccf 100644 --- a/lib/gameState/EVictoryLossCheckResult.h +++ b/lib/gameState/EVictoryLossCheckResult.h @@ -9,6 +9,8 @@ */ #pragma once +#include "../texts/MetaString.h" + VCMI_LIB_NAMESPACE_BEGIN class DLL_LINKAGE EVictoryLossCheckResult diff --git a/lib/gameState/InfoAboutArmy.h b/lib/gameState/InfoAboutArmy.h index bdbd47e9e..195e3d721 100644 --- a/lib/gameState/InfoAboutArmy.h +++ b/lib/gameState/InfoAboutArmy.h @@ -9,7 +9,7 @@ */ #pragma once -#include "CCreatureSet.h" +#include "../mapObjects/army/CStackBasicDescriptor.h" VCMI_LIB_NAMESPACE_BEGIN diff --git a/lib/json/JsonRandom.cpp b/lib/json/JsonRandom.cpp index c70dd54d3..8f1e2878f 100644 --- a/lib/json/JsonRandom.cpp +++ b/lib/json/JsonRandom.cpp @@ -23,13 +23,13 @@ #include "../constants/StringConstants.h" #include "../GameLibrary.h" #include "../CCreatureHandler.h" -#include "../CCreatureSet.h" #include "../spells/CSpellHandler.h" #include "../CSkillHandler.h" #include "../entities/artifact/CArtHandler.h" #include "../entities/hero/CHero.h" #include "../entities/hero/CHeroClass.h" #include "../gameState/CGameState.h" +#include "../mapObjects/army/CStackBasicDescriptor.h" #include "../mapObjects/IObjectInterface.h" #include "../modding/IdentifierStorage.h" #include "../modding/ModScope.h" diff --git a/lib/mapObjectConstructors/CommonConstructors.cpp b/lib/mapObjectConstructors/CommonConstructors.cpp index fecec9187..6d33d85c4 100644 --- a/lib/mapObjectConstructors/CommonConstructors.cpp +++ b/lib/mapObjectConstructors/CommonConstructors.cpp @@ -26,8 +26,8 @@ #include "../mapObjects/CGTownInstance.h" #include "../mapObjects/MiscObjects.h" #include "../mapObjects/ObjectTemplate.h" +#include "../mapping/TerrainTile.h" #include "../modding/IdentifierStorage.h" -#include "../mapping/CMapDefines.h" VCMI_LIB_NAMESPACE_BEGIN diff --git a/lib/mapObjectConstructors/DwellingInstanceConstructor.cpp b/lib/mapObjectConstructors/DwellingInstanceConstructor.cpp index a9c9b99fc..c468ea3e2 100644 --- a/lib/mapObjectConstructors/DwellingInstanceConstructor.cpp +++ b/lib/mapObjectConstructors/DwellingInstanceConstructor.cpp @@ -14,6 +14,7 @@ #include "../texts/CGeneralTextHandler.h" #include "../json/JsonRandom.h" #include "../GameLibrary.h" +#include "../mapObjects/army/CStackInstance.h" #include "../mapObjects/CGDwelling.h" #include "../mapObjects/ObjectTemplate.h" #include "../modding/IdentifierStorage.h" diff --git a/lib/mapObjects/CGCreature.h b/lib/mapObjects/CGCreature.h index 864d43b1e..fa2d49eaf 100644 --- a/lib/mapObjects/CGCreature.h +++ b/lib/mapObjects/CGCreature.h @@ -9,7 +9,7 @@ */ #pragma once -#include "CArmedInstance.h" +#include "army/CArmedInstance.h" #include "../ResourceSet.h" VCMI_LIB_NAMESPACE_BEGIN diff --git a/lib/mapObjects/CGDwelling.h b/lib/mapObjects/CGDwelling.h index bf578f1d7..e69ce3dd1 100644 --- a/lib/mapObjects/CGDwelling.h +++ b/lib/mapObjects/CGDwelling.h @@ -10,9 +10,10 @@ #pragma once -#include "CArmedInstance.h" #include "IOwnableObject.h" +#include "army/CArmedInstance.h" + VCMI_LIB_NAMESPACE_BEGIN class CGDwelling; diff --git a/lib/mapObjects/CGHeroInstance.h b/lib/mapObjects/CGHeroInstance.h index ed15184ff..df6e5c7e2 100644 --- a/lib/mapObjects/CGHeroInstance.h +++ b/lib/mapObjects/CGHeroInstance.h @@ -11,9 +11,11 @@ #include -#include "CArmedInstance.h" #include "IOwnableObject.h" +#include "army/CArmedInstance.h" +#include "army/CCommanderInstance.h" + #include "../bonuses/BonusCache.h" #include "../entities/hero/EHeroGender.h" diff --git a/lib/mapObjects/CGResource.h b/lib/mapObjects/CGResource.h index 5256943d1..5330b6c1d 100644 --- a/lib/mapObjects/CGResource.h +++ b/lib/mapObjects/CGResource.h @@ -9,7 +9,7 @@ */ #pragma once -#include "CArmedInstance.h" +#include "army/CArmedInstance.h" VCMI_LIB_NAMESPACE_BEGIN diff --git a/lib/mapObjects/CGTownInstance.cpp b/lib/mapObjects/CGTownInstance.cpp index 0ad298a90..d700f402c 100644 --- a/lib/mapObjects/CGTownInstance.cpp +++ b/lib/mapObjects/CGTownInstance.cpp @@ -22,6 +22,7 @@ #include "../texts/CGeneralTextHandler.h" #include "../gameState/CGameState.h" #include "../gameState/UpgradeInfo.h" +#include "../mapping/CCastleEvent.h" #include "../mapping/CMap.h" #include "../CPlayerState.h" #include "../StartInfo.h" diff --git a/lib/mapObjects/CGTownInstance.h b/lib/mapObjects/CGTownInstance.h index ff436df35..132a5f358 100644 --- a/lib/mapObjects/CGTownInstance.h +++ b/lib/mapObjects/CGTownInstance.h @@ -167,7 +167,7 @@ public: void removeAllBuildings(); std::set getBuildings() const; - TResources getBuildingCost(const BuildingID & buildingID) const; + ResourceSet getBuildingCost(const BuildingID & buildingID) const; ResourceSet dailyIncome() const override; std::vector providedCreatures() const override; diff --git a/lib/mapObjects/CRewardableObject.h b/lib/mapObjects/CRewardableObject.h index 50e0a489f..ebc84883f 100644 --- a/lib/mapObjects/CRewardableObject.h +++ b/lib/mapObjects/CRewardableObject.h @@ -9,7 +9,8 @@ */ #pragma once -#include "CArmedInstance.h" +#include "army/CArmedInstance.h" + #include "../rewardable/Interface.h" VCMI_LIB_NAMESPACE_BEGIN diff --git a/lib/mapObjects/IObjectInterface.cpp b/lib/mapObjects/IObjectInterface.cpp index 18e9ab6bb..7e013b1d3 100644 --- a/lib/mapObjects/IObjectInterface.cpp +++ b/lib/mapObjects/IObjectInterface.cpp @@ -18,6 +18,7 @@ #include "../callback/IGameInfoCallback.h" #include "../callback/IGameEventCallback.h" #include "../mapObjects/CGHeroInstance.h" +#include "../mapping/TerrainTile.h" #include "../networkPacks/PacksForClient.h" VCMI_LIB_NAMESPACE_BEGIN diff --git a/lib/mapObjects/MapObjects.h b/lib/mapObjects/MapObjects.h index 1ea66d139..918a05e14 100644 --- a/lib/mapObjects/MapObjects.h +++ b/lib/mapObjects/MapObjects.h @@ -13,7 +13,6 @@ // Possible TODO - remove this header after CObjectHandler.cpp will be fully split into smaller files #include "CObjectHandler.h" -#include "CArmedInstance.h" #include "CGDwelling.h" #include "CGHeroInstance.h" #include "CGMarket.h" diff --git a/lib/mapObjects/MiscObjects.h b/lib/mapObjects/MiscObjects.h index b62102e93..25c0fe5c2 100644 --- a/lib/mapObjects/MiscObjects.h +++ b/lib/mapObjects/MiscObjects.h @@ -9,8 +9,9 @@ */ #pragma once -#include "CArmedInstance.h" #include "IOwnableObject.h" +#include "army/CArmedInstance.h" +#include "../entities/artifact/CArtifactInstance.h" #include "../texts/MetaString.h" VCMI_LIB_NAMESPACE_BEGIN diff --git a/lib/mapObjects/CArmedInstance.cpp b/lib/mapObjects/army/CArmedInstance.cpp similarity index 76% rename from lib/mapObjects/CArmedInstance.cpp rename to lib/mapObjects/army/CArmedInstance.cpp index ac0d54f50..bcd129fde 100644 --- a/lib/mapObjects/CArmedInstance.cpp +++ b/lib/mapObjects/army/CArmedInstance.cpp @@ -11,22 +11,20 @@ #include "StdInc.h" #include "CArmedInstance.h" -#include "../CCreatureHandler.h" -#include "../CPlayerState.h" -#include "../callback/IGameInfoCallback.h" -#include "../entities/faction/CFaction.h" -#include "../entities/faction/CTown.h" -#include "../entities/faction/CTownHandler.h" -#include "../GameLibrary.h" -#include "../gameState/CGameState.h" -#include "../mapping/CMapDefines.h" -#include "../texts/CGeneralTextHandler.h" +#include "CStackInstance.h" + +#include "../../CPlayerState.h" +#include "../../entities/faction/CTown.h" +#include "../../entities/faction/CTownHandler.h" +#include "../../mapping/TerrainTile.h" +#include "../../GameLibrary.h" +#include "../../gameState/CGameState.h" VCMI_LIB_NAMESPACE_BEGIN void CArmedInstance::randomizeArmy(FactionID type) { - for (auto & elem : stacks) + for(auto & elem : stacks) { if(elem.second->randomStack) { @@ -41,16 +39,16 @@ void CArmedInstance::randomizeArmy(FactionID type) } } -CArmedInstance::CArmedInstance(IGameInfoCallback *cb) - :CArmedInstance(cb, BonusNodeType::ARMY, false) +CArmedInstance::CArmedInstance(IGameInfoCallback * cb) + : CArmedInstance(cb, BonusNodeType::ARMY, false) { } -CArmedInstance::CArmedInstance(IGameInfoCallback *cb, BonusNodeType nodeType, bool isHypothetic): - CGObjectInstance(cb), - CBonusSystemNode(nodeType, isHypothetic), - nonEvilAlignmentMix(this, Selector::type()(BonusType::NONEVIL_ALIGNMENT_MIX)), // Take Angelic Alliance troop-mixing freedom of non-evil units into account. - battle(nullptr) +CArmedInstance::CArmedInstance(IGameInfoCallback * cb, BonusNodeType nodeType, bool isHypothetic) + : CGObjectInstance(cb) + , CBonusSystemNode(nodeType, isHypothetic) + , nonEvilAlignmentMix(this, Selector::type()(BonusType::NONEVIL_ALIGNMENT_MIX)) // Take Angelic Alliance troop-mixing freedom of non-evil units into account. + , battle(nullptr) { } @@ -60,7 +58,7 @@ void CArmedInstance::updateMoraleBonusFromArmy() return; auto b = getExportedBonusList().getFirst(Selector::sourceType()(BonusSource::ARMY).And(Selector::type()(BonusType::MORALE))); - if(!b) + if(!b) { b = std::make_shared(BonusDuration::PERMANENT, BonusType::MORALE, BonusSource::ARMY, 0, BonusSourceID()); addNewBonus(b); @@ -72,11 +70,11 @@ void CArmedInstance::updateMoraleBonusFromArmy() for(const auto & slot : Slots()) { - const auto * creature = slot.second->getCreatureID().toEntity(LIBRARY); + const auto * creature = slot.second->getCreatureID().toEntity(LIBRARY); factions.insert(creature->getFactionID()); // Check for undead flag instead of faction (undead mummies are neutral) - if (!hasUndead) + if(!hasUndead) { //this is costly check, let's skip it at first undead hasUndead |= slot.second->hasBonusOfType(BonusType::UNDEAD); @@ -85,16 +83,16 @@ void CArmedInstance::updateMoraleBonusFromArmy() size_t factionsInArmy = factions.size(); //town garrison seems to take both sets into account - if (nonEvilAlignmentMix.hasBonus()) + if(nonEvilAlignmentMix.hasBonus()) { size_t mixableFactions = 0; for(auto f : factions) { - if (LIBRARY->factions()->getById(f)->getAlignment() != EAlignment::EVIL) + if(LIBRARY->factions()->getById(f)->getAlignment() != EAlignment::EVIL) mixableFactions++; } - if (mixableFactions > 0) + if(mixableFactions > 0) factionsInArmy -= mixableFactions - 1; } @@ -105,20 +103,20 @@ void CArmedInstance::updateMoraleBonusFromArmy() b->val = +1; bonusDescription.appendTextID("core.arraytxt.115"); //All troops of one alignment +1 } - else if (!factions.empty()) // no bonus from empty garrison + else if(!factions.empty()) // no bonus from empty garrison { b->val = 2 - static_cast(factionsInArmy); bonusDescription.appendTextID("core.arraytxt.114"); //Troops of %d alignments %d bonusDescription.replaceNumber(factionsInArmy); } - + b->description = bonusDescription; nodeHasChanged(); //-1 modifier for any Undead unit in army auto undeadModifier = getExportedBonusList().getFirst(Selector::source(BonusSource::ARMY, BonusCustomSource::undeadMoraleDebuff)); - if(hasUndead) + if(hasUndead) { if(!undeadModifier) { @@ -129,7 +127,6 @@ void CArmedInstance::updateMoraleBonusFromArmy() } else if(undeadModifier) removeBonus(undeadModifier); - } void CArmedInstance::armyChanged() @@ -176,7 +173,7 @@ void CArmedInstance::attachUnitsToArmy() elem.second->setArmy(getArmy()); } -const IBonusBearer* CArmedInstance::getBonusBearer() const +const IBonusBearer * CArmedInstance::getBonusBearer() const { return this; } @@ -189,7 +186,7 @@ void CArmedInstance::serializeJsonOptions(JsonSerializeFormat & handler) TerrainId CArmedInstance::getCurrentTerrain() const { - if (anchorPos().isValid()) + if(anchorPos().isValid()) return cb->getTile(visitablePos())->getTerrainID(); else return TerrainId::NONE; diff --git a/lib/mapObjects/CArmedInstance.h b/lib/mapObjects/army/CArmedInstance.h similarity index 66% rename from lib/mapObjects/CArmedInstance.h rename to lib/mapObjects/army/CArmedInstance.h index a5da54ee1..640f376aa 100644 --- a/lib/mapObjects/CArmedInstance.h +++ b/lib/mapObjects/army/CArmedInstance.h @@ -9,10 +9,14 @@ */ #pragma once -#include "CGObjectInstance.h" -#include "../CCreatureSet.h" -#include "../bonuses/CBonusSystemNode.h" -#include "../bonuses/BonusCache.h" +#include "CCreatureSet.h" + +#include "../CGObjectInstance.h" + +#include "../../bonuses/BonusCache.h" +#include "../../bonuses/CBonusSystemNode.h" + +#include VCMI_LIB_NAMESPACE_BEGIN @@ -32,26 +36,32 @@ protected: virtual CBonusSystemNode & whatShouldBeAttached(); public: - BattleInfo *battle; //set to the current battle, if engaged + BattleInfo * battle; //set to the current battle, if engaged void randomizeArmy(FactionID type); virtual void updateMoraleBonusFromArmy(); void armyChanged() override; - CArmedInstance * getArmy() final { return this; } - const CArmedInstance * getArmy() const final { return this; } + CArmedInstance * getArmy() final + { + return this; + } + const CArmedInstance * getArmy() const final + { + return this; + } ////////////////////////////////////////////////////////////////////////// //IConstBonusProvider - const IBonusBearer* getBonusBearer() const override; + const IBonusBearer * getBonusBearer() const override; void attachToBonusSystem(CGameState & gs) override; void detachFromBonusSystem(CGameState & gs) override; void restoreBonusSystem(CGameState & gs) override; ////////////////////////////////////////////////////////////////////////// - CArmedInstance(IGameInfoCallback *cb); - CArmedInstance(IGameInfoCallback *cb, BonusNodeType nodeType, bool isHypothetic); + CArmedInstance(IGameInfoCallback * cb); + CArmedInstance(IGameInfoCallback * cb, BonusNodeType nodeType, bool isHypothetic); PlayerColor getOwner() const override { @@ -59,14 +69,15 @@ public: } TerrainId getCurrentTerrain() const; - + void serializeJsonOptions(JsonSerializeFormat & handler) override; - template void serialize(Handler &h) + template + void serialize(Handler & h) { - h & static_cast(*this); - h & static_cast(*this); - h & static_cast(*this); + h & static_cast(*this); + h & static_cast(*this); + h & static_cast(*this); if(!h.saving && h.loadingGamestate) attachUnitsToArmy(); diff --git a/lib/mapObjects/army/CCommanderInstance.cpp b/lib/mapObjects/army/CCommanderInstance.cpp new file mode 100644 index 000000000..654ab7730 --- /dev/null +++ b/lib/mapObjects/army/CCommanderInstance.cpp @@ -0,0 +1,80 @@ +/* + * CCommanderInstance.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 "CCommanderInstance.h" + +#include "../../GameLibrary.h" +#include "../../entities/hero/CHeroHandler.h" + +VCMI_LIB_NAMESPACE_BEGIN + +CCommanderInstance::CCommanderInstance(IGameInfoCallback * cb) + : CStackInstance(cb) +{ +} + +CCommanderInstance::CCommanderInstance(IGameInfoCallback * cb, const CreatureID & id) + : CStackInstance(cb, BonusNodeType::COMMANDER, false) + , name("Commando") +{ + alive = true; + level = 1; + setCount(1); + setType(nullptr); + secondarySkills.resize(ECommander::SPELL_POWER + 1); + setType(id); + //TODO - parse them +} + +void CCommanderInstance::setAlive(bool Alive) +{ + //TODO: helm of immortality + alive = Alive; + if(!alive) + { + removeBonusesRecursive(Bonus::UntilCommanderKilled); + } +} + +bool CCommanderInstance::canGainExperience() const +{ + return alive; +} + +int CCommanderInstance::getExpRank() const +{ + return LIBRARY->heroh->level(getTotalExperience()); +} + +int CCommanderInstance::getLevel() const +{ + return std::max(1, getExpRank()); +} + +void CCommanderInstance::levelUp() +{ + level++; + for(const auto & bonus : LIBRARY->creh->commanderLevelPremy) + { //grant all regular level-up bonuses + accumulateBonus(bonus); + } +} + +ArtBearer CCommanderInstance::bearerType() const +{ + return ArtBearer::COMMANDER; +} + +bool CCommanderInstance::gainsLevel() const +{ + return getTotalExperience() >= LIBRARY->heroh->reqExp(level + 1); +} + +VCMI_LIB_NAMESPACE_END diff --git a/lib/mapObjects/army/CCommanderInstance.h b/lib/mapObjects/army/CCommanderInstance.h new file mode 100644 index 000000000..f0984de8f --- /dev/null +++ b/lib/mapObjects/army/CCommanderInstance.h @@ -0,0 +1,55 @@ +/* + * CCommanderInstance.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 "CStackInstance.h" + +VCMI_LIB_NAMESPACE_BEGIN + +class DLL_LINKAGE CCommanderInstance : public CStackInstance +{ +public: + //TODO: what if Commander is not a part of creature set? + + //commander class is determined by its base creature + ui8 alive; //maybe change to bool when breaking save compatibility? + ui8 level; //required only to count callbacks + std::string name; // each Commander has different name + std::vector secondarySkills; //ID -> level + std::set specialSkills; + //std::vector arts; + CCommanderInstance(IGameInfoCallback * cb); + CCommanderInstance(IGameInfoCallback * cb, const CreatureID & id); + void setAlive(bool alive); + void levelUp(); + + bool canGainExperience() const override; + bool gainsLevel() const; //true if commander has lower level than should upon his experience + ui64 getPower() const override + { + return 0; + }; + int getExpRank() const override; + int getLevel() const override; + ArtBearer bearerType() const override; //from CArtifactSet + + template + void serialize(Handler & h) + { + h & static_cast(*this); + h & alive; + h & level; + h & name; + h & secondarySkills; + h & specialSkills; + } +}; + +VCMI_LIB_NAMESPACE_END diff --git a/lib/CCreatureSet.cpp b/lib/mapObjects/army/CCreatureSet.cpp similarity index 51% rename from lib/CCreatureSet.cpp rename to lib/mapObjects/army/CCreatureSet.cpp index 80d8ac5b6..1d9bf3fa6 100644 --- a/lib/CCreatureSet.cpp +++ b/lib/mapObjects/army/CCreatureSet.cpp @@ -10,27 +10,15 @@ #include "StdInc.h" #include "CCreatureSet.h" -#include "CConfigHandler.h" -#include "CCreatureHandler.h" -#include "GameLibrary.h" -#include "IGameSettings.h" -#include "callback/IGameInfoCallback.h" -#include "entities/hero/CHeroHandler.h" -#include "mapObjects/CGHeroInstance.h" -#include "modding/ModScope.h" -#include "texts/CGeneralTextHandler.h" -#include "spells/CSpellHandler.h" -#include "IBonusTypeHandler.h" -#include "serializer/JsonSerializeFormat.h" -#include "gameState/CGameState.h" +#include "../CGHeroInstance.h" -#include -#include +#include "../../CConfigHandler.h" +#include "../../texts/CGeneralTextHandler.h" +#include "../../serializer/JsonSerializeFormat.h" VCMI_LIB_NAMESPACE_BEGIN - -bool CreatureSlotComparer::operator()(const TPairCreatureSlot & lhs, const TPairCreatureSlot & rhs) + bool CreatureSlotComparer::operator()(const TPairCreatureSlot & lhs, const TPairCreatureSlot & rhs) { return lhs.first->getAIValue() < rhs.first->getAIValue(); // Descendant order sorting } @@ -38,7 +26,7 @@ bool CreatureSlotComparer::operator()(const TPairCreatureSlot & lhs, const TPair const CStackInstance & CCreatureSet::operator[](const SlotID & slot) const { auto i = stacks.find(slot); - if (i != stacks.end()) + if(i != stacks.end()) return *i->second; else throw std::runtime_error("That slot is empty!"); @@ -47,7 +35,7 @@ const CStackInstance & CCreatureSet::operator[](const SlotID & slot) const const CCreature * CCreatureSet::getCreature(const SlotID & slot) const { auto i = stacks.find(slot); - if (i != stacks.end()) + if(i != stacks.end()) return i->second->getCreature(); else return nullptr; @@ -82,7 +70,7 @@ SlotID CCreatureSet::getSlotFor(const CreatureID & creature, ui32 slotsAmount) c return getSlotFor(creature.toCreature(), slotsAmount); } -SlotID CCreatureSet::getSlotFor(const CCreature *c, ui32 slotsAmount) const +SlotID CCreatureSet::getSlotFor(const CCreature * c, ui32 slotsAmount) const { assert(c); for(const auto & elem : stacks) @@ -149,7 +137,6 @@ bool CCreatureSet::isCreatureBalanced(const CCreature * c, TQuantity ignoreAmoun if(count == ignoreAmount || count < 1) continue; - if(count > max) max = count; if(count < min) @@ -162,7 +149,7 @@ bool CCreatureSet::isCreatureBalanced(const CCreature * c, TQuantity ignoreAmoun SlotID CCreatureSet::getFreeSlot(ui32 slotsAmount) const { - for(ui32 i=0; i CCreatureSet::getFreeSlotsQueue(ui32 slotsAmount) const { std::queue freeSlots; - for (ui32 i = 0; i < slotsAmount; i++) + for(ui32 i = 0; i < slotsAmount; i++) { auto slot = SlotID(i); @@ -236,7 +223,7 @@ TCreatureQueue CCreatureSet::getCreatureQueue(const SlotID & exclude) const TQuantity CCreatureSet::getStackCount(const SlotID & slot) const { - if (!hasStackAtSlot(slot)) + if(!hasStackAtSlot(slot)) return 0; return stacks.at(slot)->getCount(); } @@ -254,9 +241,9 @@ TExpType CCreatureSet::getStackAverageExperience(const SlotID & slot) const bool CCreatureSet::mergeableStacks(std::pair & out, const SlotID & preferable) const /*looks for two same stacks, returns slot positions */ { //try to match creature to our preferred stack - if(preferable.validSlot() && vstd::contains(stacks, preferable)) + if(preferable.validSlot() && vstd::contains(stacks, preferable)) { - const CCreature *cr = stacks.find(preferable)->second->getCreature(); + const CCreature * cr = stacks.find(preferable)->second->getCreature(); for(const auto & elem : stacks) { if(cr == elem.second->getType() && elem.first != preferable) @@ -285,7 +272,7 @@ bool CCreatureSet::mergeableStacks(std::pair & out, const SlotID void CCreatureSet::addToSlot(const SlotID & slot, const CreatureID & cre, TQuantity count, bool allowMerging) { - const CCreature *c = cre.toCreature(); + const CCreature * c = cre.toCreature(); if(!hasStackAtSlot(slot)) { @@ -342,18 +329,18 @@ bool CCreatureSet::needsLastStack() const ui64 CCreatureSet::getArmyStrength(int fortLevel) const { ui64 ret = 0; - for (const auto& elem : stacks) + for(const auto & elem : stacks) { ui64 powerToAdd = elem.second->getPower(); - if (fortLevel > 0) + if(fortLevel > 0) { - if (!elem.second->hasBonusOfType(BonusType::FLYING)) + if(!elem.second->hasBonusOfType(BonusType::FLYING)) { powerToAdd /= fortLevel; - if (!elem.second->hasBonusOfType(BonusType::SHOOTER)) + if(!elem.second->hasBonusOfType(BonusType::SHOOTER)) powerToAdd /= fortLevel; } - } + } ret += powerToAdd; } return ret; @@ -362,7 +349,7 @@ ui64 CCreatureSet::getArmyStrength(int fortLevel) const ui64 CCreatureSet::getArmyCost() const { ui64 ret = 0; - for (const auto& elem : stacks) + for(const auto & elem : stacks) ret += elem.second->getMarketValue(); return ret; } @@ -383,7 +370,7 @@ std::string CCreatureSet::getRoughAmount(const SlotID & slot, int mode) const if(settings["gameTweaks"]["numericCreaturesQuantities"].Bool()) return CCreature::getQuantityRangeStringForId(quantity); - return LIBRARY->generaltexth->arraytxt[(174 + mode) + 3*(int)quantity]; + return LIBRARY->generaltexth->arraytxt[(174 + mode) + 3 * (int)quantity]; } return ""; } @@ -461,7 +448,8 @@ CStackInstance * CCreatureSet::getStackPtr(const SlotID & slot) const { if(hasStackAtSlot(slot)) return stacks.find(slot)->second.get(); - else return nullptr; + else + return nullptr; } void CCreatureSet::eraseStack(const SlotID & slot) @@ -470,7 +458,7 @@ void CCreatureSet::eraseStack(const SlotID & slot) detachStack(slot); } -bool CCreatureSet::contains(const CStackInstance *stack) const +bool CCreatureSet::contains(const CStackInstance * stack) const { if(!stack) return false; @@ -482,10 +470,10 @@ bool CCreatureSet::contains(const CStackInstance *stack) const return false; } -SlotID CCreatureSet::findStack(const CStackInstance *stack) const +SlotID CCreatureSet::findStack(const CStackInstance * stack) const { const auto * h = dynamic_cast(this); - if (h && h->getCommander() == stack) + if(h && h->getCommander() == stack) return SlotID::COMMANDER_SLOT_PLACEHOLDER; if(!stack) @@ -510,7 +498,7 @@ void CCreatureSet::putStack(const SlotID & slot, std::unique_ptr void CCreatureSet::joinStack(const SlotID & slot, std::unique_ptr stack) { - [[maybe_unused]] const CCreature *c = getCreature(slot); + [[maybe_unused]] const CCreature * c = getCreature(slot); assert(c == stack->getType()); assert(c); @@ -541,7 +529,7 @@ void CCreatureSet::changeStackCount(const SlotID & slot, TQuantity toAdd) CCreatureSet::~CCreatureSet() = default; -void CCreatureSet::setToArmy(CSimpleArmy &src) +void CCreatureSet::setToArmy(CSimpleArmy & src) { clearSlots(); while(src) @@ -577,12 +565,12 @@ void CCreatureSet::setStackType(const SlotID & slot, const CreatureID & type) armyChanged(); } -bool CCreatureSet::canBeMergedWith(const CCreatureSet &cs, bool allowMergingStacks) const +bool CCreatureSet::canBeMergedWith(const CCreatureSet & cs, bool allowMergingStacks) const { if(!allowMergingStacks) { int freeSlots = stacksCount() - GameConstants::ARMY_SIZE; - std::set cresToAdd; + std::set cresToAdd; for(const auto & elem : cs.stacks) { SlotID dest = getSlotFor(elem.second->getCreature()); @@ -598,13 +586,13 @@ bool CCreatureSet::canBeMergedWith(const CCreatureSet &cs, bool allowMergingStac //get types of creatures that need their own slot for(const auto & elem : cs.stacks) - if ((j = cres.getSlotFor(elem.second->getCreature())).validSlot()) - cres.addToSlot(j, elem.second->getId(), 1, true); //merge if possible - //cres.addToSlot(elem.first, elem.second->type->getId(), 1, true); + if((j = cres.getSlotFor(elem.second->getCreature())).validSlot()) + cres.addToSlot(j, elem.second->getId(), 1, true); //merge if possible + //cres.addToSlot(elem.first, elem.second->type->getId(), 1, true); for(const auto & elem : stacks) { - if ((j = cres.getSlotFor(elem.second->getCreature())).validSlot()) - cres.addToSlot(j, elem.second->getId(), 1, true); //merge if possible + if((j = cres.getSlotFor(elem.second->getCreature())).validSlot()) + cres.addToSlot(j, elem.second->getId(), 1, true); //merge if possible else return false; //no place found } @@ -622,22 +610,22 @@ bool CCreatureSet::hasUnits(const std::vector & units, bo for(const auto & slot : Slots()) { const auto & heroStack = slot.second; - if (heroStack->getType() == reqStack.getType()) + if(heroStack->getType() == reqStack.getType()) { count += heroStack->getCount(); testedSlots += 1; } } - if (count > reqStack.getCount()) + if(count > reqStack.getCount()) foundExtraCreatures = true; - if (count < reqStack.getCount()) //not enough creatures of this kind + if(count < reqStack.getCount()) //not enough creatures of this kind return false; } - if (requireLastStack) + if(requireLastStack) { - if (!foundExtraCreatures && testedSlots >= Slots().size()) + if(!foundExtraCreatures && testedSlots >= Slots().size()) return false; } @@ -649,16 +637,7 @@ bool CCreatureSet::hasStackAtSlot(const SlotID & slot) const return vstd::contains(stacks, slot); } -CCreatureSet & CCreatureSet::operator=(const CCreatureSet&cs) -{ - assert(0); - return *this; -} - -void CCreatureSet::armyChanged() -{ - -} +void CCreatureSet::armyChanged() {} void CCreatureSet::serializeJson(JsonSerializeFormat & handler, const std::string & armyFieldName, const std::optional fixedSize) { @@ -668,13 +647,12 @@ void CCreatureSet::serializeJson(JsonSerializeFormat & handler, const std::strin handler.serializeEnum("formation", formation, NArmyFormation::names); auto a = handler.enterArray(armyFieldName); - if(handler.saving) { size_t sz = 0; for(const auto & p : stacks) - vstd::amax(sz, p.first.getNum()+1); + vstd::amax(sz, p.first.getNum() + 1); if(fixedSize) vstd::amax(sz, fixedSize.value()); @@ -707,483 +685,4 @@ void CCreatureSet::serializeJson(JsonSerializeFormat & handler, const std::strin } } -CStackInstance::CStackInstance(IGameInfoCallback *cb) - : CStackInstance(cb, BonusNodeType::STACK_INSTANCE, false) -{} - -CStackInstance::CStackInstance(IGameInfoCallback *cb, BonusNodeType nodeType, bool isHypothetic) - : CBonusSystemNode(nodeType, isHypothetic) - , CStackBasicDescriptor(nullptr, 0) - , CArtifactSet(cb) - , GameCallbackHolder(cb) - , nativeTerrain(this, Selector::type()(BonusType::TERRAIN_NATIVE)) - , initiative(this, Selector::type()(BonusType::STACKS_SPEED)) - , totalExperience(0) -{} - -CStackInstance::CStackInstance(IGameInfoCallback *cb, const CreatureID & id, TQuantity Count, bool isHypothetic) - : CStackInstance(cb, BonusNodeType::STACK_INSTANCE, false) -{ - setType(id); - setCount(Count); -} - -CCreature::CreatureQuantityId CStackInstance::getQuantityID() const -{ - return CCreature::getQuantityID(getCount()); -} - -int CStackInstance::getExpRank() const -{ - if (!LIBRARY->engineSettings()->getBoolean(EGameSettings::MODULE_STACK_EXPERIENCE)) - return 0; - int tier = getType()->getLevel(); - if (vstd::iswithin(tier, 1, 7)) - { - for(int i = static_cast(LIBRARY->creh->expRanks[tier].size()) - 2; i > -1; --i) //sic! - { //exp values vary from 1st level to max exp at 11th level - if (getAverageExperience() >= LIBRARY->creh->expRanks[tier][i]) - return ++i; //faster, but confusing - 0 index mean 1st level of experience - } - return 0; - } - else //higher tier - { - for(int i = static_cast(LIBRARY->creh->expRanks[0].size()) - 2; i > -1; --i) - { - if (getAverageExperience() >= LIBRARY->creh->expRanks[0][i]) - return ++i; - } - return 0; - } -} - -int CStackInstance::getLevel() const -{ - return std::max(1, getType()->getLevel()); -} - -void CStackInstance::giveAverageStackExperience(TExpType desiredAmountPerUnit) -{ - if (!canGainExperience()) - return; - - int level = std::clamp(getLevel(), 1, 7); - TExpType maxAmountPerUnit = LIBRARY->creh->expRanks[level].back(); - TExpType actualAmountPerUnit = std::min(desiredAmountPerUnit, maxAmountPerUnit * LIBRARY->creh->maxExpPerBattle[level]/100); - TExpType maxExperience = maxAmountPerUnit * getCount(); - TExpType maxExperienceToGain = maxExperience - totalExperience; - TExpType actualGainedExperience = std::min(maxExperienceToGain, actualAmountPerUnit * getCount()); - - totalExperience += actualGainedExperience; -} - -void CStackInstance::giveTotalStackExperience(TExpType experienceToGive) -{ - if (!canGainExperience()) - return; - - totalExperience += experienceToGive; -} - -TExpType CStackInstance::getTotalExperience() const -{ - return totalExperience; -} - -TExpType CStackInstance::getAverageExperience() const -{ - return totalExperience / getCount(); -} - -bool CStackInstance::canGainExperience() const -{ - return cb->getSettings().getBoolean(EGameSettings::MODULE_STACK_EXPERIENCE); -} - -void CStackInstance::setType(const CreatureID & creID) -{ - if (creID == CreatureID::NONE) - setType(nullptr);//FIXME: unused branch? - else - setType(creID.toCreature()); -} - -void CStackInstance::setType(const CCreature *c) -{ - if(getCreature()) - { - detachFromSource(*getCreature()); - if (LIBRARY->engineSettings()->getBoolean(EGameSettings::MODULE_STACK_EXPERIENCE)) - totalExperience = totalExperience * LIBRARY->creh->expAfterUpgrade / 100; - } - - CStackBasicDescriptor::setType(c); - - if(getCreature()) - attachToSource(*getCreature()); -} - -void CStackInstance::setCount(TQuantity newCount) -{ - assert(newCount >= 0); - - if (newCount < getCount()) - { - TExpType averageExperience = totalExperience / getCount(); - totalExperience = averageExperience * newCount; - } - - CStackBasicDescriptor::setCount(newCount); - nodeHasChanged(); -} - -std::string CStackInstance::bonusToString(const std::shared_ptr& bonus) const -{ - if (!bonus->description.empty()) - return bonus->description.toString(); - else - return LIBRARY->getBth()->bonusToString(bonus, this); -} - -ImagePath CStackInstance::bonusToGraphics(const std::shared_ptr & bonus) const -{ - if (!bonus->customIconPath.empty()) - return bonus->customIconPath; - return LIBRARY->getBth()->bonusToGraphics(bonus); -} - -CArmedInstance * CStackInstance::getArmy() -{ - return armyInstance; -} - -const CArmedInstance * CStackInstance::getArmy() const -{ - return armyInstance; -} - -void CStackInstance::setArmy(CArmedInstance * ArmyObj) -{ - auto oldArmy = getArmy(); - - if(oldArmy) - { - detachFrom(*oldArmy); - armyInstance = nullptr; - } - - if(ArmyObj) - { - attachTo(const_cast(*ArmyObj)); - armyInstance = ArmyObj; - } -} - -std::string CStackInstance::getQuantityTXT(bool capitalized) const -{ - CCreature::CreatureQuantityId quantity = getQuantityID(); - - if ((int)quantity) - { - if(settings["gameTweaks"]["numericCreaturesQuantities"].Bool()) - return CCreature::getQuantityRangeStringForId(quantity); - - return LIBRARY->generaltexth->arraytxt[174 + (int)quantity*3 - 1 - capitalized]; - } - else - return ""; -} - -bool CStackInstance::valid(bool allowUnrandomized) const -{ - if(!randomStack) - { - return (getType() && getType() == getId().toEntity(LIBRARY)); - } - else - return allowUnrandomized; -} - -std::string CStackInstance::nodeName() const -{ - std::ostringstream oss; - oss << "Stack of " << getCount() << " of "; - if(getType()) - oss << getType()->getNamePluralTextID(); - else - oss << "[UNDEFINED TYPE]"; - - return oss.str(); -} - -PlayerColor CStackInstance::getOwner() const -{ - auto army = getArmy(); - return army ? army->getOwner() : PlayerColor::NEUTRAL; -} - -int32_t CStackInstance::getInitiative(int turn) const -{ - if (turn == 0) - return initiative.getValue(); - - return ACreature::getInitiative(turn); -} - -TerrainId CStackInstance::getNativeTerrain() const -{ - if (nativeTerrain.hasBonus()) - return TerrainId::ANY_TERRAIN; - - return getFactionID().toEntity(LIBRARY)->getNativeTerrain(); -} - -TerrainId CStackInstance::getCurrentTerrain() const -{ - assert(getArmy() != nullptr); - return getArmy()->getCurrentTerrain(); -} - -CreatureID CStackInstance::getCreatureID() const -{ - if(getType()) - return getType()->getId(); - else - return CreatureID::NONE; -} - -std::string CStackInstance::getName() const -{ - return (getCount() > 1) ? getType()->getNamePluralTranslated() : getType()->getNameSingularTranslated(); -} - -ui64 CStackInstance::getPower() const -{ - assert(getType()); - return static_cast(getType()->getAIValue()) * getCount(); -} - -ui64 CStackInstance::getMarketValue() const -{ - assert(getType()); - return getType()->getFullRecruitCost().marketValue() * getCount(); -} - -ArtBearer CStackInstance::bearerType() const -{ - return ArtBearer::CREATURE; -} - -CStackInstance::ArtPlacementMap CStackInstance::putArtifact(const ArtifactPosition & pos, const CArtifactInstance * art) -{ - assert(!getArt(pos)); - assert(art->canBePutAt(this, pos)); - - attachToSource(*art); - return CArtifactSet::putArtifact(pos, art); -} - -void CStackInstance::removeArtifact(const ArtifactPosition & pos) -{ - assert(getArt(pos)); - - detachFromSource(*getArt(pos)); - CArtifactSet::removeArtifact(pos); -} - -void CStackInstance::serializeJson(JsonSerializeFormat & handler) -{ - //todo: artifacts - CStackBasicDescriptor::serializeJson(handler);//must be first - - if(handler.saving) - { - if(randomStack) - { - int level = randomStack->level; - int upgrade = randomStack->upgrade; - - handler.serializeInt("level", level, 0); - handler.serializeInt("upgraded", upgrade, 0); - } - } - else - { - //type set by CStackBasicDescriptor::serializeJson - if(getType() == nullptr) - { - uint8_t level = 0; - uint8_t upgrade = 0; - - handler.serializeInt("level", level, 0); - handler.serializeInt("upgrade", upgrade, 0); - - randomStack = RandomStackInfo{ level, upgrade }; - } - } -} - -FactionID CStackInstance::getFactionID() const -{ - if(getType()) - return getType()->getFactionID(); - - return FactionID::NEUTRAL; -} - -const IBonusBearer* CStackInstance::getBonusBearer() const -{ - return this; -} - -CCommanderInstance::CCommanderInstance(IGameInfoCallback *cb) - :CStackInstance(cb) -{} - -CCommanderInstance::CCommanderInstance(IGameInfoCallback *cb, const CreatureID & id) - : CStackInstance(cb, BonusNodeType::COMMANDER, false) - , name("Commando") -{ - alive = true; - level = 1; - setCount(1); - setType(nullptr); - secondarySkills.resize (ECommander::SPELL_POWER + 1); - setType(id); - //TODO - parse them -} - -void CCommanderInstance::setAlive (bool Alive) -{ - //TODO: helm of immortality - alive = Alive; - if (!alive) - { - removeBonusesRecursive(Bonus::UntilCommanderKilled); - } -} - -bool CCommanderInstance::canGainExperience() const -{ - return alive; -} - -int CCommanderInstance::getExpRank() const -{ - return LIBRARY->heroh->level (getTotalExperience()); -} - -int CCommanderInstance::getLevel() const -{ - return std::max (1, getExpRank()); -} - -void CCommanderInstance::levelUp () -{ - level++; - for(const auto & bonus : LIBRARY->creh->commanderLevelPremy) - { //grant all regular level-up bonuses - accumulateBonus(bonus); - } -} - -ArtBearer CCommanderInstance::bearerType() const -{ - return ArtBearer::COMMANDER; -} - -bool CCommanderInstance::gainsLevel() const -{ - return getTotalExperience() >= LIBRARY->heroh->reqExp(level + 1); -} - -//This constructor should be placed here to avoid side effects -CStackBasicDescriptor::CStackBasicDescriptor() = default; - -CStackBasicDescriptor::CStackBasicDescriptor(const CreatureID & id, TQuantity Count): - typeID(id), - count(Count) -{ -} - -CStackBasicDescriptor::CStackBasicDescriptor(const CCreature *c, TQuantity Count) - : typeID(c ? c->getId() : CreatureID()), count(Count) -{ -} - -const CCreature * CStackBasicDescriptor::getCreature() const -{ - return typeID.hasValue() ? typeID.toCreature() : nullptr; -} - -const Creature * CStackBasicDescriptor::getType() const -{ - return typeID.hasValue() ? typeID.toEntity(LIBRARY) : nullptr; -} - -CreatureID CStackBasicDescriptor::getId() const -{ - return typeID; -} - -TQuantity CStackBasicDescriptor::getCount() const -{ - return count; -} - -void CStackBasicDescriptor::setType(const CCreature * c) -{ - typeID = c ? c->getId() : CreatureID(); -} - -void CStackBasicDescriptor::setCount(TQuantity newCount) -{ - assert(newCount >= 0); - count = newCount; -} - -bool operator== (const CStackBasicDescriptor & l, const CStackBasicDescriptor & r) -{ - return l.typeID == r.typeID && l.count == r.count; -} - -void CStackBasicDescriptor::serializeJson(JsonSerializeFormat & handler) -{ - handler.serializeInt("amount", count); - - if(handler.saving) - { - if(typeID.hasValue()) - { - std::string typeName = typeID.toEntity(LIBRARY)->getJsonKey(); - handler.serializeString("type", typeName); - } - } - else - { - std::string typeName; - handler.serializeString("type", typeName); - if(!typeName.empty()) - setType(CreatureID(CreatureID::decode(typeName)).toCreature()); - } -} - -void CSimpleArmy::clearSlots() -{ - army.clear(); -} - -CSimpleArmy::operator bool() const -{ - return !army.empty(); -} - -bool CSimpleArmy::setCreature(SlotID slot, CreatureID cre, TQuantity count) -{ - assert(!vstd::contains(army, slot)); - army[slot] = std::make_pair(cre, count); - return true; -} - VCMI_LIB_NAMESPACE_END diff --git a/lib/mapObjects/army/CCreatureSet.h b/lib/mapObjects/army/CCreatureSet.h new file mode 100644 index 000000000..d208a52b1 --- /dev/null +++ b/lib/mapObjects/army/CCreatureSet.h @@ -0,0 +1,160 @@ +/* + * CCreatureSet.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 "CSimpleArmy.h" +#include "CStackInstance.h" + +#include "serializer/Serializeable.h" + +VCMI_LIB_NAMESPACE_BEGIN + +class CStackInstance; +class CArmedInstance; +class CStackBasicDescriptor; +class JsonSerializeFormat; + +using TSlots = std::map>; + +using TPairCreatureSlot = std::pair; +using TMapCreatureSlot = std::map; + +struct DLL_LINKAGE CreatureSlotComparer +{ + bool operator()(const TPairCreatureSlot & lhs, const TPairCreatureSlot & rhs); +}; + +using TCreatureQueue = std::priority_queue, CreatureSlotComparer>; + +namespace NArmyFormation +{ +static const std::vector names{"wide", "tight"}; +} + +class DLL_LINKAGE CCreatureSet : public IArmyDescriptor, public virtual Serializeable, boost::noncopyable //seven combined creatures +{ + CCreatureSet(const CCreatureSet &) = delete; + CCreatureSet & operator=(const CCreatureSet &) = delete; + +public: + TSlots stacks; //slots[slot_id]->> pair(creature_id,creature_quantity) + EArmyFormation formation = EArmyFormation::LOOSE; //0 - wide, 1 - tight + + CCreatureSet() = default; //Should be here to avoid compile errors + virtual ~CCreatureSet(); + virtual void armyChanged(); + + const CStackInstance & operator[](const SlotID & slot) const; + + const TSlots &Slots() const {return stacks;} + + //Adds stack to slot. Slot must be empty or with same type creature + void addToSlot(const SlotID & slot, const CreatureID & cre, TQuantity count, bool allowMerging = true); + //Adds stack to slot. Slot must be empty or with same type creature + void addToSlot(const SlotID & slot, std::unique_ptr stack, bool allowMerging = true); + + void clearSlots() override; + void setFormation(EArmyFormation tight); + virtual CArmedInstance * getArmy() { return nullptr; } + virtual const CArmedInstance * getArmy() const { return nullptr; } + + //adds new stack to the army, slot must be empty + void putStack(const SlotID & slot, std::unique_ptr stack); + //stack must exist! + void setStackCount(const SlotID & slot, TQuantity count); + //removes stack from army but doesn't destroy it (so it can be moved somewhere else or safely deleted) + std::unique_ptr detachStack(const SlotID & slot); + + void setStackType(const SlotID & slot, const CreatureID & type); + + /// Give specified amount of experience to all units in army + /// Amount of granted experience is scaled by unit stack size + void giveAverageStackExperience(TExpType exp); + + /// Give specified amount of experience to unit in specified slot + /// Amount of granted experience is not scaled by unit stack size + void giveTotalStackExperience(const SlotID & slot, TExpType exp); + + /// Erased stack from specified slot. Slot must be non-empty + void eraseStack(const SlotID & slot); + + /// Joins stack into stack that occupies targeted slot. + /// Slot must be non-empty and contain same creature type + void joinStack(const SlotID & slot, std::unique_ptr stack); //adds new stack to the existing stack of the same type + + /// Splits off some units of specified stack and returns newly created stack + /// Slot must be non-empty and contain more units that split quantity + std::unique_ptr splitStack(const SlotID & slot, TQuantity toSplit); + + //stack must exist! + void changeStackCount(const SlotID & slot, TQuantity toAdd); + + //replaces creature in stack; slots 0 to 6, if quantity=0 erases stack + bool setCreature(SlotID slot, CreatureID type, TQuantity quantity) override; + + //erases all our army and moves stacks from src to us; src MUST NOT be an armed object! WARNING: use it wisely. Or better do not use at all. + void setToArmy(CSimpleArmy & src); + + const CStackInstance & getStack(const SlotID & slot) const; //stack must exist + CStackInstance * getStackPtr(const SlotID & slot) const; //if stack doesn't exist, returns nullptr + const CCreature * getCreature(const SlotID & slot) const; //workaround of map issue; + int getStackCount(const SlotID & slot) const; + TExpType getStackTotalExperience(const SlotID & slot) const; + TExpType getStackAverageExperience(const SlotID & slot) const; + SlotID findStack(const CStackInstance * stack) const; //-1 if none + SlotID getSlotFor(const CreatureID & creature, ui32 slotsAmount = GameConstants::ARMY_SIZE) const; //returns -1 if no slot available + SlotID getSlotFor(const CCreature * c, ui32 slotsAmount = GameConstants::ARMY_SIZE) const; //returns -1 if no slot available + bool hasCreatureSlots(const CCreature * c, const SlotID & exclude) const; + std::vector getCreatureSlots(const CCreature * c, const SlotID & exclude, TQuantity ignoreAmount = -1) const; + bool isCreatureBalanced(const CCreature * c, TQuantity ignoreAmount = 1) const; // Check if the creature is evenly distributed across slots + + SlotID getFreeSlot(ui32 slotsAmount = GameConstants::ARMY_SIZE) const; //returns first free slot + std::vector getFreeSlots(ui32 slotsAmount = GameConstants::ARMY_SIZE) const; + std::queue getFreeSlotsQueue(ui32 slotsAmount = GameConstants::ARMY_SIZE) const; + + TMapCreatureSlot getCreatureMap() const; + TCreatureQueue getCreatureQueue(const SlotID & exclude) const; + + bool mergeableStacks(std::pair & out, const SlotID & preferable = SlotID()) const; //looks for two same stacks, returns slot positions; + bool validTypes(bool allowUnrandomized = false) const; //checks if all types of creatures are set properly + bool slotEmpty(const SlotID & slot) const; + int stacksCount() const; + virtual bool needsLastStack() const; //true if last stack cannot be taken + ui64 getArmyStrength(int fortLevel = 0) const; //sum of AI values of creatures + ui64 getArmyCost() const; //sum of cost of creatures + ui64 getPower(const SlotID & slot) const; //value of specific stack + std::string getRoughAmount(const SlotID & slot, int mode = 0) const; //rough size of specific stack + std::string getArmyDescription() const; + bool hasStackAtSlot(const SlotID & slot) const; + + bool contains(const CStackInstance * stack) const; + bool canBeMergedWith(const CCreatureSet & cs, bool allowMergingStacks = true) const; + + /// Returns true if this creature set contains all listed units + /// If requireLastStack is true, then this function will also + /// require presence of any unit other than requested (or more units than requested) + bool hasUnits(const std::vector & units, bool requireLastStack = true) const; + + template + void serialize(Handler & h) + { + h & stacks; + h & formation; + } + + void serializeJson(JsonSerializeFormat & handler, const std::string & armyFieldName, const std::optional fixedSize = std::nullopt); + + operator bool() const + { + return !stacks.empty(); + } +}; + +VCMI_LIB_NAMESPACE_END diff --git a/lib/mapObjects/army/CSimpleArmy.h b/lib/mapObjects/army/CSimpleArmy.h new file mode 100644 index 000000000..8b4888c95 --- /dev/null +++ b/lib/mapObjects/army/CSimpleArmy.h @@ -0,0 +1,54 @@ +/* + * CSimpleArmy.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" + +VCMI_LIB_NAMESPACE_BEGIN + +class IArmyDescriptor +{ +public: + virtual void clearSlots() = 0; + virtual bool setCreature(SlotID slot, CreatureID cre, TQuantity count) = 0; +}; + +using TSimpleSlots = std::map>; + +//simplified version of CCreatureSet +class DLL_LINKAGE CSimpleArmy : public IArmyDescriptor +{ +public: + TSimpleSlots army; + void clearSlots() override + { + army.clear(); + } + + bool setCreature(SlotID slot, CreatureID cre, TQuantity count) override + { + assert(!vstd::contains(army, slot)); + army[slot] = std::make_pair(cre, count); + return true; + } + + operator bool() const + { + return !army.empty(); + } + + template + void serialize(Handler & h) + { + h & army; + } +}; + +VCMI_LIB_NAMESPACE_END diff --git a/lib/mapObjects/army/CStackBasicDescriptor.cpp b/lib/mapObjects/army/CStackBasicDescriptor.cpp new file mode 100644 index 000000000..5370c1aac --- /dev/null +++ b/lib/mapObjects/army/CStackBasicDescriptor.cpp @@ -0,0 +1,91 @@ +/* + * CStackBasicDescriptor.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 "CStackBasicDescriptor.h" + +#include "../../CCreatureHandler.h" +#include "../../GameLibrary.h" +#include "../../serializer/JsonSerializeFormat.h" + +VCMI_LIB_NAMESPACE_BEGIN + +//This constructor should be placed here to avoid side effects +CStackBasicDescriptor::CStackBasicDescriptor() = default; + +CStackBasicDescriptor::CStackBasicDescriptor(const CreatureID & id, TQuantity Count) + : typeID(id) + , count(Count) +{ +} + +CStackBasicDescriptor::CStackBasicDescriptor(const CCreature * c, TQuantity Count) + : typeID(c ? c->getId() : CreatureID()) + , count(Count) +{ +} + +const CCreature * CStackBasicDescriptor::getCreature() const +{ + return typeID.hasValue() ? typeID.toCreature() : nullptr; +} + +const Creature * CStackBasicDescriptor::getType() const +{ + return typeID.hasValue() ? typeID.toEntity(LIBRARY) : nullptr; +} + +CreatureID CStackBasicDescriptor::getId() const +{ + return typeID; +} + +TQuantity CStackBasicDescriptor::getCount() const +{ + return count; +} + +void CStackBasicDescriptor::setType(const CCreature * c) +{ + typeID = c ? c->getId() : CreatureID(); +} + +void CStackBasicDescriptor::setCount(TQuantity newCount) +{ + assert(newCount >= 0); + count = newCount; +} + +bool operator==(const CStackBasicDescriptor & l, const CStackBasicDescriptor & r) +{ + return l.typeID == r.typeID && l.count == r.count; +} + +void CStackBasicDescriptor::serializeJson(JsonSerializeFormat & handler) +{ + handler.serializeInt("amount", count); + + if(handler.saving) + { + if(typeID.hasValue()) + { + std::string typeName = typeID.toEntity(LIBRARY)->getJsonKey(); + handler.serializeString("type", typeName); + } + } + else + { + std::string typeName; + handler.serializeString("type", typeName); + if(!typeName.empty()) + setType(CreatureID(CreatureID::decode(typeName)).toCreature()); + } +} + +VCMI_LIB_NAMESPACE_END diff --git a/lib/mapObjects/army/CStackBasicDescriptor.h b/lib/mapObjects/army/CStackBasicDescriptor.h new file mode 100644 index 000000000..613ba8b9a --- /dev/null +++ b/lib/mapObjects/army/CStackBasicDescriptor.h @@ -0,0 +1,65 @@ +/* + * CStackBasicDescriptor.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" + +VCMI_LIB_NAMESPACE_BEGIN + +class JsonNode; +class CCreature; +class CGHeroInstance; +class CArmedInstance; +class CCreatureArtifactSet; +class JsonSerializeFormat; + +class DLL_LINKAGE CStackBasicDescriptor +{ + CreatureID typeID; + TQuantity count = -1; //exact quantity or quantity ID from CCreature::getQuantityID when getting info about enemy army + +public: + CStackBasicDescriptor(); + CStackBasicDescriptor(const CreatureID & id, TQuantity Count); + CStackBasicDescriptor(const CCreature * c, TQuantity Count); + virtual ~CStackBasicDescriptor() = default; + + const Creature * getType() const; + const CCreature * getCreature() const; + CreatureID getId() const; + TQuantity getCount() const; + + virtual void setType(const CCreature * c); + virtual void setCount(TQuantity amount); + + friend bool operator==(const CStackBasicDescriptor & l, const CStackBasicDescriptor & r); + + template + void serialize(Handler & h) + { + if(h.saving) + { + h & typeID; + } + else + { + CreatureID creatureID; + h & creatureID; + if(creatureID != CreatureID::NONE) + setType(creatureID.toCreature()); + } + + h & count; + } + + void serializeJson(JsonSerializeFormat & handler); +}; + +VCMI_LIB_NAMESPACE_END diff --git a/lib/mapObjects/army/CStackInstance.cpp b/lib/mapObjects/army/CStackInstance.cpp new file mode 100644 index 000000000..11f347e5c --- /dev/null +++ b/lib/mapObjects/army/CStackInstance.cpp @@ -0,0 +1,358 @@ +/* + * CStackInstance.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 "CStackInstance.h" + +#include "CArmedInstance.h" + +#include "../../CConfigHandler.h" +#include "../../GameLibrary.h" +#include "../../IGameSettings.h" +#include "../../callback/IGameInfoCallback.h" +#include "../../entities/faction/CFaction.h" +#include "../../texts/CGeneralTextHandler.h" +#include "../../IBonusTypeHandler.h" +#include "../../serializer/JsonSerializeFormat.h" + +VCMI_LIB_NAMESPACE_BEGIN + +CStackInstance::CStackInstance(IGameInfoCallback * cb) + : CStackInstance(cb, BonusNodeType::STACK_INSTANCE, false) +{ +} + +CStackInstance::CStackInstance(IGameInfoCallback * cb, BonusNodeType nodeType, bool isHypothetic) + : CBonusSystemNode(nodeType, isHypothetic) + , CStackBasicDescriptor(nullptr, 0) + , CArtifactSet(cb) + , GameCallbackHolder(cb) + , nativeTerrain(this, Selector::type()(BonusType::TERRAIN_NATIVE)) + , initiative(this, Selector::type()(BonusType::STACKS_SPEED)) + , totalExperience(0) +{ +} + +CStackInstance::CStackInstance(IGameInfoCallback * cb, const CreatureID & id, TQuantity Count, bool isHypothetic) + : CStackInstance(cb, BonusNodeType::STACK_INSTANCE, false) +{ + setType(id); + setCount(Count); +} + +CCreature::CreatureQuantityId CStackInstance::getQuantityID() const +{ + return CCreature::getQuantityID(getCount()); +} + +int CStackInstance::getExpRank() const +{ + if(!LIBRARY->engineSettings()->getBoolean(EGameSettings::MODULE_STACK_EXPERIENCE)) + return 0; + int tier = getType()->getLevel(); + if(vstd::iswithin(tier, 1, 7)) + { + for(int i = static_cast(LIBRARY->creh->expRanks[tier].size()) - 2; i > -1; --i) //sic! + { //exp values vary from 1st level to max exp at 11th level + if(getAverageExperience() >= LIBRARY->creh->expRanks[tier][i]) + return ++i; //faster, but confusing - 0 index mean 1st level of experience + } + return 0; + } + else //higher tier + { + for(int i = static_cast(LIBRARY->creh->expRanks[0].size()) - 2; i > -1; --i) + { + if(getAverageExperience() >= LIBRARY->creh->expRanks[0][i]) + return ++i; + } + return 0; + } +} + +int CStackInstance::getLevel() const +{ + return std::max(1, getType()->getLevel()); +} + +void CStackInstance::giveAverageStackExperience(TExpType desiredAmountPerUnit) +{ + if(!canGainExperience()) + return; + + int level = std::clamp(getLevel(), 1, 7); + TExpType maxAmountPerUnit = LIBRARY->creh->expRanks[level].back(); + TExpType actualAmountPerUnit = std::min(desiredAmountPerUnit, maxAmountPerUnit * LIBRARY->creh->maxExpPerBattle[level] / 100); + TExpType maxExperience = maxAmountPerUnit * getCount(); + TExpType maxExperienceToGain = maxExperience - totalExperience; + TExpType actualGainedExperience = std::min(maxExperienceToGain, actualAmountPerUnit * getCount()); + + totalExperience += actualGainedExperience; +} + +void CStackInstance::giveTotalStackExperience(TExpType experienceToGive) +{ + if(!canGainExperience()) + return; + + totalExperience += experienceToGive; +} + +TExpType CStackInstance::getTotalExperience() const +{ + return totalExperience; +} + +TExpType CStackInstance::getAverageExperience() const +{ + return totalExperience / getCount(); +} + +bool CStackInstance::canGainExperience() const +{ + return cb->getSettings().getBoolean(EGameSettings::MODULE_STACK_EXPERIENCE); +} + +void CStackInstance::setType(const CreatureID & creID) +{ + if(creID == CreatureID::NONE) + setType(nullptr); //FIXME: unused branch? + else + setType(creID.toCreature()); +} + +void CStackInstance::setType(const CCreature * c) +{ + if(getCreature()) + { + detachFromSource(*getCreature()); + if(LIBRARY->engineSettings()->getBoolean(EGameSettings::MODULE_STACK_EXPERIENCE)) + totalExperience = totalExperience * LIBRARY->creh->expAfterUpgrade / 100; + } + + CStackBasicDescriptor::setType(c); + + if(getCreature()) + attachToSource(*getCreature()); +} + +void CStackInstance::setCount(TQuantity newCount) +{ + assert(newCount >= 0); + + if(newCount < getCount()) + { + TExpType averageExperience = totalExperience / getCount(); + totalExperience = averageExperience * newCount; + } + + CStackBasicDescriptor::setCount(newCount); + nodeHasChanged(); +} + +std::string CStackInstance::bonusToString(const std::shared_ptr & bonus) const +{ + if(!bonus->description.empty()) + return bonus->description.toString(); + else + return LIBRARY->getBth()->bonusToString(bonus, this); +} + +ImagePath CStackInstance::bonusToGraphics(const std::shared_ptr & bonus) const +{ + if(!bonus->customIconPath.empty()) + return bonus->customIconPath; + return LIBRARY->getBth()->bonusToGraphics(bonus); +} + +CArmedInstance * CStackInstance::getArmy() +{ + return armyInstance; +} + +const CArmedInstance * CStackInstance::getArmy() const +{ + return armyInstance; +} + +void CStackInstance::setArmy(CArmedInstance * ArmyObj) +{ + auto oldArmy = getArmy(); + + if(oldArmy) + { + detachFrom(*oldArmy); + armyInstance = nullptr; + } + + if(ArmyObj) + { + attachTo(const_cast(*ArmyObj)); + armyInstance = ArmyObj; + } +} + +std::string CStackInstance::getQuantityTXT(bool capitalized) const +{ + CCreature::CreatureQuantityId quantity = getQuantityID(); + + if((int)quantity) + { + if(settings["gameTweaks"]["numericCreaturesQuantities"].Bool()) + return CCreature::getQuantityRangeStringForId(quantity); + + return LIBRARY->generaltexth->arraytxt[174 + (int)quantity * 3 - 1 - capitalized]; + } + else + return ""; +} + +bool CStackInstance::valid(bool allowUnrandomized) const +{ + if(!randomStack) + { + return (getType() && getType() == getId().toEntity(LIBRARY)); + } + else + return allowUnrandomized; +} + +std::string CStackInstance::nodeName() const +{ + std::ostringstream oss; + oss << "Stack of " << getCount() << " of "; + if(getType()) + oss << getType()->getNamePluralTextID(); + else + oss << "[UNDEFINED TYPE]"; + + return oss.str(); +} + +PlayerColor CStackInstance::getOwner() const +{ + auto army = getArmy(); + return army ? army->getOwner() : PlayerColor::NEUTRAL; +} + +int32_t CStackInstance::getInitiative(int turn) const +{ + if(turn == 0) + return initiative.getValue(); + + return ACreature::getInitiative(turn); +} + +TerrainId CStackInstance::getNativeTerrain() const +{ + if(nativeTerrain.hasBonus()) + return TerrainId::ANY_TERRAIN; + + return getFactionID().toEntity(LIBRARY)->getNativeTerrain(); +} + +TerrainId CStackInstance::getCurrentTerrain() const +{ + assert(getArmy() != nullptr); + return getArmy()->getCurrentTerrain(); +} + +CreatureID CStackInstance::getCreatureID() const +{ + if(getType()) + return getType()->getId(); + else + return CreatureID::NONE; +} + +std::string CStackInstance::getName() const +{ + return (getCount() > 1) ? getType()->getNamePluralTranslated() : getType()->getNameSingularTranslated(); +} + +ui64 CStackInstance::getPower() const +{ + assert(getType()); + return static_cast(getType()->getAIValue()) * getCount(); +} + +ui64 CStackInstance::getMarketValue() const +{ + assert(getType()); + return getType()->getFullRecruitCost().marketValue() * getCount(); +} + +ArtBearer CStackInstance::bearerType() const +{ + return ArtBearer::CREATURE; +} + +CStackInstance::ArtPlacementMap CStackInstance::putArtifact(const ArtifactPosition & pos, const CArtifactInstance * art) +{ + assert(!getArt(pos)); + assert(art->canBePutAt(this, pos)); + + attachToSource(*art); + return CArtifactSet::putArtifact(pos, art); +} + +void CStackInstance::removeArtifact(const ArtifactPosition & pos) +{ + assert(getArt(pos)); + + detachFromSource(*getArt(pos)); + CArtifactSet::removeArtifact(pos); +} + +void CStackInstance::serializeJson(JsonSerializeFormat & handler) +{ + //todo: artifacts + CStackBasicDescriptor::serializeJson(handler); //must be first + + if(handler.saving) + { + if(randomStack) + { + int level = randomStack->level; + int upgrade = randomStack->upgrade; + + handler.serializeInt("level", level, 0); + handler.serializeInt("upgraded", upgrade, 0); + } + } + else + { + //type set by CStackBasicDescriptor::serializeJson + if(getType() == nullptr) + { + uint8_t level = 0; + uint8_t upgrade = 0; + + handler.serializeInt("level", level, 0); + handler.serializeInt("upgrade", upgrade, 0); + + randomStack = RandomStackInfo{level, upgrade}; + } + } +} + +FactionID CStackInstance::getFactionID() const +{ + if(getType()) + return getType()->getFactionID(); + + return FactionID::NEUTRAL; +} + +const IBonusBearer * CStackInstance::getBonusBearer() const +{ + return this; +} + +VCMI_LIB_NAMESPACE_END diff --git a/lib/mapObjects/army/CStackInstance.h b/lib/mapObjects/army/CStackInstance.h new file mode 100644 index 000000000..8e9a93163 --- /dev/null +++ b/lib/mapObjects/army/CStackInstance.h @@ -0,0 +1,134 @@ +/* + * CStackInstance.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 "CStackBasicDescriptor.h" + +#include "CCreatureHandler.h" +#include "bonuses/BonusCache.h" +#include "bonuses/CBonusSystemNode.h" +#include "callback/GameCallbackHolder.h" +#include "entities/artifact/CArtifactSet.h" +#include "mapObjects/CGObjectInstance.h" + +VCMI_LIB_NAMESPACE_BEGIN + +class JsonNode; +class CCreature; +class CGHeroInstance; +class CArmedInstance; +class CCreatureArtifactSet; +class JsonSerializeFormat; + +class DLL_LINKAGE CStackInstance : public CBonusSystemNode, public CStackBasicDescriptor, public CArtifactSet, public ACreature, public GameCallbackHolder +{ + BonusValueCache nativeTerrain; + BonusValueCache initiative; + + CArmedInstance * armyInstance = nullptr; //stack must be part of some army, army must be part of some object + + IGameInfoCallback * getCallback() const final + { + return cb; + } + + TExpType totalExperience; //commander needs same amount of exp as hero +public: + struct RandomStackInfo + { + uint8_t level; + uint8_t upgrade; + }; + // helper variable used during loading map, when object (hero or town) have creatures that must have same alignment. + std::optional randomStack; + + CArmedInstance * getArmy(); + const CArmedInstance * getArmy() const; //stack must be part of some army, army must be part of some object + void setArmy(CArmedInstance * ArmyObj); + + TExpType getTotalExperience() const; + TExpType getAverageExperience() const; + virtual bool canGainExperience() const; + + template + void serialize(Handler & h) + { + h & static_cast(*this); + h & static_cast(*this); + h & static_cast(*this); + + if(h.hasFeature(Handler::Version::STACK_INSTANCE_ARMY_FIX)) + { + // no-op + } + if(h.hasFeature(Handler::Version::NO_RAW_POINTERS_IN_SERIALIZER)) + { + ObjectInstanceID dummyID; + h & dummyID; + } + else + { + std::shared_ptr army; + h & army; + } + + h & totalExperience; + if(!h.hasFeature(Handler::Version::STACK_INSTANCE_EXPERIENCE_FIX)) + { + totalExperience *= getCount(); + } + } + + void serializeJson(JsonSerializeFormat & handler); + + //overrides CBonusSystemNode + std::string bonusToString(const std::shared_ptr & bonus) const override; // how would bonus description look for this particular type of node + ImagePath bonusToGraphics(const std::shared_ptr & bonus) const; //file name of graphics from StackSkills , in future possibly others + + //IConstBonusProvider + const IBonusBearer * getBonusBearer() const override; + //INativeTerrainProvider + FactionID getFactionID() const override; + + virtual ui64 getPower() const; + /// Returns total market value of resources needed to recruit this unit + virtual ui64 getMarketValue() const; + CCreature::CreatureQuantityId getQuantityID() const; + std::string getQuantityTXT(bool capitalized = true) const; + virtual int getExpRank() const; + virtual int getLevel() const; //different for regular stack and commander + CreatureID getCreatureID() const; //-1 if not available + std::string getName() const; //plural or singular + CStackInstance(IGameInfoCallback * cb); + CStackInstance(IGameInfoCallback * cb, BonusNodeType nodeType, bool isHypothetic = false); + CStackInstance(IGameInfoCallback * cb, const CreatureID & id, TQuantity count, bool isHypothetic = false); + virtual ~CStackInstance() = default; + + void setType(const CreatureID & creID); + void setType(const CCreature * c) final; + void setCount(TQuantity amount) final; + + /// Gives specified amount of stack experience that will not be scaled by unit size + void giveAverageStackExperience(TExpType exp); + void giveTotalStackExperience(TExpType exp); + + bool valid(bool allowUnrandomized) const; + ArtPlacementMap putArtifact(const ArtifactPosition & pos, const CArtifactInstance * art) override; //from CArtifactSet + void removeArtifact(const ArtifactPosition & pos) override; + ArtBearer bearerType() const override; //from CArtifactSet + std::string nodeName() const override; //from CBonusSystemnode + PlayerColor getOwner() const override; + + int32_t getInitiative(int turn = 0) const final; + TerrainId getNativeTerrain() const final; + TerrainId getCurrentTerrain() const; +}; + +VCMI_LIB_NAMESPACE_END diff --git a/lib/mapping/CCastleEvent.h b/lib/mapping/CCastleEvent.h new file mode 100644 index 000000000..cc88a05de --- /dev/null +++ b/lib/mapping/CCastleEvent.h @@ -0,0 +1,37 @@ +/* + * CCastleEvent.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 "CMapEvent.h" + +VCMI_LIB_NAMESPACE_BEGIN + +/// The castle event builds/adds buildings/creatures for a specific town. +class DLL_LINKAGE CCastleEvent : public CMapEvent +{ +public: + CCastleEvent() = default; + + std::set buildings; + std::vector creatures; + + template + void serialize(Handler & h) + { + h & static_cast(*this); + h & buildings; + h & creatures; + } + + void serializeJson(JsonSerializeFormat & handler) override; +}; + +VCMI_LIB_NAMESPACE_END diff --git a/lib/mapping/CMap.cpp b/lib/mapping/CMap.cpp index fabc7b167..145f17d4d 100644 --- a/lib/mapping/CMap.cpp +++ b/lib/mapping/CMap.cpp @@ -12,6 +12,7 @@ #include "CMapEditManager.h" #include "CMapOperation.h" +#include "CCastleEvent.h" #include "../CCreatureHandler.h" #include "../CSkillHandler.h" diff --git a/lib/mapping/CMap.h b/lib/mapping/CMap.h index 21233f658..8544c82cb 100644 --- a/lib/mapping/CMap.h +++ b/lib/mapping/CMap.h @@ -10,8 +10,9 @@ #pragma once -#include "CMapDefines.h" +#include "CMapEvent.h" #include "CMapHeader.h" +#include "TerrainTile.h" #include "../mapObjects/CGObjectInstance.h" #include "../callback/GameCallbackHolder.h" diff --git a/lib/mapping/CMapEvent.h b/lib/mapping/CMapEvent.h new file mode 100644 index 000000000..6e9aff4ab --- /dev/null +++ b/lib/mapping/CMapEvent.h @@ -0,0 +1,59 @@ +/* + * CMapEvent.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 "../ResourceSet.h" +#include "../texts/MetaString.h" + +VCMI_LIB_NAMESPACE_BEGIN + +class JsonSerializeFormat; + +/// The map event is an event which e.g. gives or takes resources of a specific +/// amount to/from players and can appear regularly or once a time. +class DLL_LINKAGE CMapEvent +{ +public: + CMapEvent(); + virtual ~CMapEvent() = default; + + bool occursToday(int currentDay) const; + bool affectsPlayer(PlayerColor player, bool isHuman) const; + + std::string name; + MetaString message; + TResources resources; + std::set players; + bool humanAffected; + bool computerAffected; + ui32 firstOccurrence; + ui32 nextOccurrence; /// specifies after how many days the event will occur the next time; 0 if event occurs only one time + + std::vector deletedObjectsInstances; + + template + void serialize(Handler & h) + { + h & name; + h & message; + h & resources; + h & players; + h & humanAffected; + h & computerAffected; + h & firstOccurrence; + h & nextOccurrence; + h & deletedObjectsInstances; + } + + virtual void serializeJson(JsonSerializeFormat & handler); +}; + +VCMI_LIB_NAMESPACE_END diff --git a/lib/mapping/MapFormatH3M.cpp b/lib/mapping/MapFormatH3M.cpp index c4d785d14..2147651e6 100644 --- a/lib/mapping/MapFormatH3M.cpp +++ b/lib/mapping/MapFormatH3M.cpp @@ -11,6 +11,7 @@ #include "StdInc.h" #include "MapFormatH3M.h" +#include "CCastleEvent.h" #include "CMap.h" #include "MapReaderH3M.h" #include "MapFormatSettings.h" diff --git a/lib/mapping/CMapDefines.h b/lib/mapping/TerrainTile.h similarity index 70% rename from lib/mapping/CMapDefines.h rename to lib/mapping/TerrainTile.h index d2df9203c..092abb0ee 100644 --- a/lib/mapping/CMapDefines.h +++ b/lib/mapping/TerrainTile.h @@ -1,5 +1,5 @@ /* - * CMapDefines.h, part of VCMI engine + * TerrainTile.h, part of VCMI engine * * Authors: listed in file AUTHORS in main folder * @@ -10,8 +10,6 @@ #pragma once -#include "../ResourceSet.h" -#include "../texts/MetaString.h" #include "../GameLibrary.h" #include "../TerrainHandler.h" #include "../mapObjects/CGObjectInstance.h" @@ -22,67 +20,6 @@ class TerrainType; class RiverType; class RoadType; class CGObjectInstance; -class CGTownInstance; -class JsonSerializeFormat; - -/// The map event is an event which e.g. gives or takes resources of a specific -/// amount to/from players and can appear regularly or once a time. -class DLL_LINKAGE CMapEvent -{ -public: - CMapEvent(); - virtual ~CMapEvent() = default; - - bool occursToday(int currentDay) const; - bool affectsPlayer(PlayerColor player, bool isHuman) const; - - std::string name; - MetaString message; - TResources resources; - std::set players; - bool humanAffected; - bool computerAffected; - ui32 firstOccurrence; - ui32 nextOccurrence; /// specifies after how many days the event will occur the next time; 0 if event occurs only one time - - std::vector deletedObjectsInstances; - - template - void serialize(Handler & h) - { - h & name; - h & message; - h & resources; - h & players; - h & humanAffected; - h & computerAffected; - h & firstOccurrence; - h & nextOccurrence; - h & deletedObjectsInstances; - } - - virtual void serializeJson(JsonSerializeFormat & handler); -}; - -/// The castle event builds/adds buildings/creatures for a specific town. -class DLL_LINKAGE CCastleEvent: public CMapEvent -{ -public: - CCastleEvent() = default; - - std::set buildings; - std::vector creatures; - - template - void serialize(Handler & h) - { - h & static_cast(*this); - h & buildings; - h & creatures; - } - - void serializeJson(JsonSerializeFormat & handler) override; -}; /// The terrain tile describes the terrain type and the visual representation of the terrain. /// Furthermore the struct defines whether the tile is visitable or/and blocked and which objects reside in it. @@ -130,7 +67,7 @@ struct DLL_LINKAGE TerrainTile std::vector visitableObjects; std::vector blockingObjects; - template + template void serialize(Handler & h) { h & terrainType; @@ -141,7 +78,7 @@ struct DLL_LINKAGE TerrainTile h & roadDir; h & extTileFlags; - if (h.hasFeature(Handler::Version::NO_RAW_POINTERS_IN_SERIALIZER)) + if(h.hasFeature(Handler::Version::NO_RAW_POINTERS_IN_SERIALIZER)) { h & visitableObjects; h & blockingObjects; @@ -150,14 +87,12 @@ struct DLL_LINKAGE TerrainTile { std::vector> objectPtrs; h & objectPtrs; - for (const auto & ptr : objectPtrs) + for(const auto & ptr : objectPtrs) visitableObjects.push_back(ptr->id); h & objectPtrs; - for (const auto & ptr : objectPtrs) + for(const auto & ptr : objectPtrs) blockingObjects.push_back(ptr->id); } - - } }; diff --git a/lib/networkPacks/PacksForClient.h b/lib/networkPacks/PacksForClient.h index 7a1165c5a..94ad81c2d 100644 --- a/lib/networkPacks/PacksForClient.h +++ b/lib/networkPacks/PacksForClient.h @@ -17,16 +17,16 @@ #include "NetPacksBase.h" #include "ObjProperty.h" -#include "../CCreatureSet.h" #include "../ResourceSet.h" #include "../TurnTimerInfo.h" +#include "../bonuses/Bonus.h" #include "../gameState/EVictoryLossCheckResult.h" #include "../gameState/RumorState.h" #include "../gameState/QuestInfo.h" #include "../gameState/TavernSlot.h" #include "../gameState/GameStatistics.h" #include "../int3.h" -#include "../mapping/CMapDefines.h" +#include "../mapObjects/army/CSimpleArmy.h" #include "../spells/ViewSpellInt.h" class CClient; diff --git a/lib/networkPacks/PacksForClientBattle.h b/lib/networkPacks/PacksForClientBattle.h index cbe7fadbb..0af85b92f 100644 --- a/lib/networkPacks/PacksForClientBattle.h +++ b/lib/networkPacks/PacksForClientBattle.h @@ -16,6 +16,7 @@ #include "../battle/BattleInfo.h" #include "../battle/BattleHexArray.h" #include "../battle/BattleUnitTurnReason.h" +#include "../mapObjects/army/CStackBasicDescriptor.h" #include "../texts/MetaString.h" class CClient; diff --git a/lib/pathfinder/CGPathNode.cpp b/lib/pathfinder/CGPathNode.cpp index ca0e23366..755d47f32 100644 --- a/lib/pathfinder/CGPathNode.cpp +++ b/lib/pathfinder/CGPathNode.cpp @@ -14,7 +14,7 @@ #include "../callback/IGameInfoCallback.h" #include "../mapObjects/CGHeroInstance.h" -#include "../mapping/CMapDefines.h" +#include "../mapping/TerrainTile.h" VCMI_LIB_NAMESPACE_BEGIN diff --git a/lib/pathfinder/PathfinderUtil.h b/lib/pathfinder/PathfinderUtil.h index d1e9b1faa..a28f50c34 100644 --- a/lib/pathfinder/PathfinderUtil.h +++ b/lib/pathfinder/PathfinderUtil.h @@ -10,7 +10,7 @@ #pragma once #include "../mapObjects/CGObjectInstance.h" -#include "../mapping/CMapDefines.h" +#include "../mapping/TerrainTile.h" #include "../callback/IGameInfoCallback.h" #include "CGPathNode.h" diff --git a/lib/pathfinder/PathfindingRules.cpp b/lib/pathfinder/PathfindingRules.cpp index 8f90a3fca..8b599126a 100644 --- a/lib/pathfinder/PathfindingRules.cpp +++ b/lib/pathfinder/PathfindingRules.cpp @@ -17,7 +17,7 @@ #include "../mapObjects/CGHeroInstance.h" #include "../mapObjects/MiscObjects.h" -#include "../mapping/CMapDefines.h" +#include "../mapping/TerrainTile.h" VCMI_LIB_NAMESPACE_BEGIN diff --git a/lib/rewardable/Info.cpp b/lib/rewardable/Info.cpp index ebdf658fa..7f60b5041 100644 --- a/lib/rewardable/Info.cpp +++ b/lib/rewardable/Info.cpp @@ -15,12 +15,13 @@ #include "Limiter.h" #include "Reward.h" -#include "../callback/IGameRandomizer.h" -#include "../texts/CGeneralTextHandler.h" -#include "../json/JsonRandom.h" +#include "../CCreatureHandler.h" #include "../GameLibrary.h" +#include "../callback/IGameRandomizer.h" +#include "../json/JsonRandom.h" #include "../mapObjects/IObjectInterface.h" #include "../modding/IdentifierStorage.h" +#include "../texts/CGeneralTextHandler.h" #include diff --git a/lib/rewardable/Interface.cpp b/lib/rewardable/Interface.cpp index a0f69bbc3..b9c3c4103 100644 --- a/lib/rewardable/Interface.cpp +++ b/lib/rewardable/Interface.cpp @@ -22,7 +22,7 @@ #include "../spells/ISpellMechanics.h" #include "../mapObjects/CGHeroInstance.h" #include "../mapObjects/MiscObjects.h" -#include "../mapping/CMapDefines.h" +#include "../mapping/TerrainTile.h" #include "../networkPacks/StackLocation.h" #include "../networkPacks/PacksForClient.h" diff --git a/lib/rewardable/Interface.h b/lib/rewardable/Interface.h index eb54f2a3a..3cb329c3c 100644 --- a/lib/rewardable/Interface.h +++ b/lib/rewardable/Interface.h @@ -16,6 +16,8 @@ VCMI_LIB_NAMESPACE_BEGIN class IObjectInterface; +class IGameEventCallback; +class CArmedInstance; namespace Rewardable { diff --git a/lib/rewardable/Limiter.h b/lib/rewardable/Limiter.h index 9e1682aa6..e75b98ee2 100644 --- a/lib/rewardable/Limiter.h +++ b/lib/rewardable/Limiter.h @@ -12,6 +12,7 @@ #include "../GameConstants.h" #include "../ResourceSet.h" +#include "../mapObjects/army/CStackBasicDescriptor.h" #include "../serializer/Serializeable.h" VCMI_LIB_NAMESPACE_BEGIN diff --git a/lib/rewardable/Reward.h b/lib/rewardable/Reward.h index 83a70123a..2f9d896ca 100644 --- a/lib/rewardable/Reward.h +++ b/lib/rewardable/Reward.h @@ -12,7 +12,6 @@ #include "../ResourceSet.h" #include "../bonuses/Bonus.h" -#include "../CCreatureSet.h" #include "../networkPacks/Component.h" VCMI_LIB_NAMESPACE_BEGIN diff --git a/lib/rmg/modificators/ObjectManager.cpp b/lib/rmg/modificators/ObjectManager.cpp index 5d614eaad..f0c4a4521 100644 --- a/lib/rmg/modificators/ObjectManager.cpp +++ b/lib/rmg/modificators/ObjectManager.cpp @@ -23,6 +23,7 @@ #include "../../CCreatureHandler.h" #include "../../mapObjectConstructors/AObjectTypeHandler.h" #include "../../mapObjectConstructors/CObjectClassesHandler.h" +#include "../../mapObjects/army/CStackInstance.h" #include "../../mapObjects/CGCreature.h" #include "../../mapping/CMap.h" #include "../../mapping/CMapEditManager.h" diff --git a/lib/serializer/SerializerReflection.cpp b/lib/serializer/SerializerReflection.cpp index 9a4fc9c81..62d27c7b8 100644 --- a/lib/serializer/SerializerReflection.cpp +++ b/lib/serializer/SerializerReflection.cpp @@ -22,6 +22,7 @@ #include "../entities/hero/CHero.h" #include "../mapObjects/ObjectTemplate.h" #include "../mapping/CMapInfo.h" +#include "../mapping/CCastleEvent.h" #include "../rmg/CMapGenOptions.h" VCMI_LIB_NAMESPACE_BEGIN diff --git a/lib/spells/AdventureSpellMechanics.cpp b/lib/spells/AdventureSpellMechanics.cpp deleted file mode 100644 index e57f7c3b0..000000000 --- a/lib/spells/AdventureSpellMechanics.cpp +++ /dev/null @@ -1,782 +0,0 @@ -/* - * AdventureSpellMechanics.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 "AdventureSpellMechanics.h" - -#include "CSpellHandler.h" -#include "Problem.h" - -#include "../CPlayerState.h" -#include "../IGameSettings.h" -#include "../callback/IGameInfoCallback.h" -#include "../mapObjects/CGHeroInstance.h" -#include "../mapObjects/CGTownInstance.h" -#include "../mapObjects/MiscObjects.h" -#include "../mapping/CMap.h" -#include "../networkPacks/PacksForClient.h" - -#include - -VCMI_LIB_NAMESPACE_BEGIN - -///AdventureSpellMechanics -AdventureSpellMechanics::AdventureSpellMechanics(const CSpell * s): - IAdventureSpellMechanics(s) -{ -} - -bool AdventureSpellMechanics::canBeCast(spells::Problem & problem, const IGameInfoCallback * cb, const spells::Caster * caster) const -{ - if(!owner->isAdventure()) - return false; - - const auto * heroCaster = dynamic_cast(caster); - - if (heroCaster) - { - if(heroCaster->isGarrisoned()) - return false; - - const auto level = heroCaster->getSpellSchoolLevel(owner); - const auto cost = owner->getCost(level); - - if(!heroCaster->canCastThisSpell(owner)) - return false; - - if(heroCaster->mana < cost) - return false; - } - - return canBeCastImpl(problem, cb, caster); -} - -bool AdventureSpellMechanics::canBeCastAt(spells::Problem & problem, const IGameInfoCallback * cb, const spells::Caster * caster, const int3 & pos) const -{ - return canBeCast(problem, cb, caster) && canBeCastAtImpl(problem, cb, caster, pos); -} - -bool AdventureSpellMechanics::canBeCastImpl(spells::Problem & problem, const IGameInfoCallback * cb, const spells::Caster * caster) const -{ - return true; -} - -bool AdventureSpellMechanics::canBeCastAtImpl(spells::Problem & problem, const IGameInfoCallback * cb, const spells::Caster * caster, const int3 & pos) const -{ - return true; -} - -bool AdventureSpellMechanics::adventureCast(SpellCastEnvironment * env, const AdventureSpellCastParameters & parameters) const -{ - spells::detail::ProblemImpl problem; - - if (!canBeCastAt(problem, env->getCb(), parameters.caster, parameters.pos)) - return false; - - ESpellCastResult result = beginCast(env, parameters); - - if(result == ESpellCastResult::OK) - performCast(env, parameters); - - return result != ESpellCastResult::ERROR; -} - -ESpellCastResult AdventureSpellMechanics::applyAdventureEffects(SpellCastEnvironment * env, const AdventureSpellCastParameters & parameters) const -{ - if(owner->hasEffects()) - { - //todo: cumulative effects support - const auto schoolLevel = parameters.caster->getSpellSchoolLevel(owner); - - std::vector bonuses; - - owner->getEffects(bonuses, schoolLevel, false, parameters.caster->getEnchantPower(owner)); - - for(const Bonus & b : bonuses) - { - GiveBonus gb; - gb.id = ObjectInstanceID(parameters.caster->getCasterUnitId()); - gb.bonus = b; - env->apply(gb); - } - - return ESpellCastResult::OK; - } - else - { - //There is no generic algorithm of adventure cast - env->complain("Unimplemented adventure spell"); - return ESpellCastResult::ERROR; - } -} - -ESpellCastResult AdventureSpellMechanics::beginCast(SpellCastEnvironment * env, const AdventureSpellCastParameters & parameters) const -{ - return ESpellCastResult::OK; -} - -void AdventureSpellMechanics::endCast(SpellCastEnvironment * env, const AdventureSpellCastParameters & parameters) const -{ - // no-op, only for implementation in derived classes -} - -void AdventureSpellMechanics::performCast(SpellCastEnvironment * env, const AdventureSpellCastParameters & parameters) const -{ - const auto level = parameters.caster->getSpellSchoolLevel(owner); - const auto cost = owner->getCost(level); - - AdvmapSpellCast asc; - asc.casterID = ObjectInstanceID(parameters.caster->getCasterUnitId()); - asc.spellID = owner->id; - env->apply(asc); - - ESpellCastResult result = applyAdventureEffects(env, parameters); - - switch(result) - { - case ESpellCastResult::OK: - parameters.caster->spendMana(env, cost); - endCast(env, parameters); - break; - default: - break; - } -} - -///SummonBoatMechanics -SummonBoatMechanics::SummonBoatMechanics(const CSpell * s): - AdventureSpellMechanics(s) -{ -} - -bool SummonBoatMechanics::canBeCastImpl(spells::Problem & problem, const IGameInfoCallback * cb, const spells::Caster * caster) const -{ - if(!caster->getHeroCaster()) - return false; - - if(caster->getHeroCaster()->inBoat()) - { - MetaString message = MetaString::createFromTextID("core.genrltxt.333"); - caster->getCasterName(message); - problem.add(std::move(message)); - return false; - } - - int3 summonPos = caster->getHeroCaster()->bestLocation(); - - if(summonPos.x < 0) - { - MetaString message = MetaString::createFromTextID("core.genrltxt.334"); - caster->getCasterName(message); - problem.add(std::move(message)); - return false; - } - - return true; -} - -ESpellCastResult SummonBoatMechanics::applyAdventureEffects(SpellCastEnvironment * env, const AdventureSpellCastParameters & parameters) const -{ - const auto schoolLevel = parameters.caster->getSpellSchoolLevel(owner); - - //check if spell works at all - if(env->getRNG()->nextInt(0, 99) >= owner->getLevelPower(schoolLevel)) //power is % chance of success - { - InfoWindow iw; - iw.player = parameters.caster->getCasterOwner(); - iw.text.appendLocalString(EMetaText::GENERAL_TXT, 336); //%s tried to summon a boat, but failed. - parameters.caster->getCasterName(iw.text); - env->apply(iw); - return ESpellCastResult::OK; - } - - //try to find unoccupied boat to summon - const CGBoat * nearest = nullptr; - double dist = 0; - for(const auto & b : env->getMap()->getObjects()) - { - if(b->getBoardedHero() || b->layer != EPathfindingLayer::SAIL) - continue; //we're looking for unoccupied boat - - double nDist = b->visitablePos().dist2d(parameters.caster->getHeroCaster()->visitablePos()); - if(!nearest || nDist < dist) //it's first boat or closer than previous - { - nearest = b; - dist = nDist; - } - } - - int3 summonPos = parameters.caster->getHeroCaster()->bestLocation(); - - if(nullptr != nearest) //we found boat to summon - { - ChangeObjPos cop; - cop.objid = nearest->id; - cop.nPos = summonPos; - cop.initiator = parameters.caster->getCasterOwner(); - env->apply(cop); - } - else if(schoolLevel < 2) //none or basic level -> cannot create boat :( - { - InfoWindow iw; - iw.player = parameters.caster->getCasterOwner(); - iw.text.appendLocalString(EMetaText::GENERAL_TXT, 335); //There are no boats to summon. - env->apply(iw); - return ESpellCastResult::ERROR; - } - else //create boat - { - env->createBoat(summonPos, BoatId::NECROPOLIS, parameters.caster->getCasterOwner()); - } - return ESpellCastResult::OK; -} - -///ScuttleBoatMechanics -ScuttleBoatMechanics::ScuttleBoatMechanics(const CSpell * s): - AdventureSpellMechanics(s) -{ -} - -bool ScuttleBoatMechanics::canBeCastAtImpl(spells::Problem & problem, const IGameInfoCallback * cb, const spells::Caster * caster, const int3 & pos) const -{ - if(!cb->isInTheMap(pos)) - return false; - - if (caster->getHeroCaster()) - { - int3 casterPosition = caster->getHeroCaster()->getSightCenter(); - - if(!isInScreenRange(casterPosition, pos)) - return false; - } - - if(!cb->isVisibleFor(pos, caster->getCasterOwner())) - return false; - - const TerrainTile * t = cb->getTile(pos); - if(!t || t->visitableObjects.empty()) - return false; - - const CGObjectInstance * topObject = cb->getObj(t->visitableObjects.back()); - if (topObject->ID != Obj::BOAT) - return false; - - return true; -} - -ESpellCastResult ScuttleBoatMechanics::applyAdventureEffects(SpellCastEnvironment * env, const AdventureSpellCastParameters & parameters) const -{ - const auto schoolLevel = parameters.caster->getSpellSchoolLevel(owner); - //check if spell works at all - if(env->getRNG()->nextInt(0, 99) >= owner->getLevelPower(schoolLevel)) //power is % chance of success - { - InfoWindow iw; - iw.player = parameters.caster->getCasterOwner(); - iw.text.appendLocalString(EMetaText::GENERAL_TXT, 337); //%s tried to scuttle the boat, but failed - parameters.caster->getCasterName(iw.text); - env->apply(iw); - return ESpellCastResult::OK; - } - - const TerrainTile & t = env->getMap()->getTile(parameters.pos); - - RemoveObject ro; - ro.initiator = parameters.caster->getCasterOwner(); - ro.objectID = t.visitableObjects.back(); - env->apply(ro); - return ESpellCastResult::OK; -} - -///DimensionDoorMechanics -DimensionDoorMechanics::DimensionDoorMechanics(const CSpell * s): - AdventureSpellMechanics(s) -{ -} - -bool DimensionDoorMechanics::canBeCastImpl(spells::Problem & problem, const IGameInfoCallback * cb, const spells::Caster * caster) const -{ - if(!caster->getHeroCaster()) - return false; - - if(caster->getHeroCaster()->movementPointsRemaining() <= 0) //unlike town portal non-zero MP is enough - { - problem.add(MetaString::createFromTextID("core.genrltxt.125")); - return false; - } - - const auto schoolLevel = caster->getSpellSchoolLevel(owner); - - std::stringstream cachingStr; - cachingStr << "source_" << vstd::to_underlying(BonusSource::SPELL_EFFECT) << "id_" << owner->id.num; - - int castsAlreadyPerformedThisTurn = caster->getHeroCaster()->getBonuses(Selector::source(BonusSource::SPELL_EFFECT, BonusSourceID(owner->id)), cachingStr.str())->size(); - int castsLimit = owner->getLevelPower(schoolLevel); - - bool isTournamentRulesLimitEnabled = cb->getSettings().getBoolean(EGameSettings::DIMENSION_DOOR_TOURNAMENT_RULES_LIMIT); - if(isTournamentRulesLimitEnabled) - { - int3 mapSize = cb->getMapSize(); - - bool meetsTournamentRulesTwoCastsRequirements = mapSize.x * mapSize.y * mapSize.z >= GameConstants::TOURNAMENT_RULES_DD_MAP_TILES_THRESHOLD - && schoolLevel == MasteryLevel::EXPERT; - - castsLimit = meetsTournamentRulesTwoCastsRequirements ? 2 : 1; - } - - if(castsAlreadyPerformedThisTurn >= castsLimit) //limit casts per turn - { - MetaString message = MetaString::createFromTextID("core.genrltxt.338"); - caster->getCasterName(message); - problem.add(std::move(message)); - return false; - } - - return true; -} - -bool DimensionDoorMechanics::canBeCastAtImpl(spells::Problem & problem, const IGameInfoCallback * cb, const spells::Caster * caster, const int3 & pos) const -{ - if(!cb->isInTheMap(pos)) - return false; - - if(cb->getSettings().getBoolean(EGameSettings::DIMENSION_DOOR_ONLY_TO_UNCOVERED_TILES)) - { - if(!cb->isVisibleFor(pos, caster->getCasterOwner())) - return false; - } - - int3 casterPosition = caster->getHeroCaster()->getSightCenter(); - - const TerrainTile * dest = cb->getTileUnchecked(pos); - const TerrainTile * curr = cb->getTileUnchecked(casterPosition); - - if(!dest) - return false; - - if(!curr) - return false; - - if(!isInScreenRange(casterPosition, pos)) - return false; - - if(cb->getSettings().getBoolean(EGameSettings::DIMENSION_DOOR_EXPOSES_TERRAIN_TYPE)) - { - if(!dest->isClear(curr)) - return false; - } - else - { - if (dest->blocked()) - return false; - } - - return true; -} - -ESpellCastResult DimensionDoorMechanics::applyAdventureEffects(SpellCastEnvironment * env, const AdventureSpellCastParameters & parameters) const -{ - const auto schoolLevel = parameters.caster->getSpellSchoolLevel(owner); - const int baseCost = env->getCb()->getSettings().getInteger(EGameSettings::HEROES_MOVEMENT_COST_BASE); - const int movementCost = baseCost * ((schoolLevel >= 3) ? 2 : 3); - - int3 casterPosition = parameters.caster->getHeroCaster()->getSightCenter(); - const TerrainTile * dest = env->getCb()->getTile(parameters.pos); - const TerrainTile * curr = env->getCb()->getTile(casterPosition); - - if(!dest->isClear(curr)) - { - InfoWindow iw; - iw.player = parameters.caster->getCasterOwner(); - - // tile is either blocked or not possible to move (e.g. water <-> land) - if(env->getCb()->getSettings().getBoolean(EGameSettings::DIMENSION_DOOR_FAILURE_SPENDS_POINTS)) - { - // SOD: DD to such "wrong" terrain results in mana and move points spending, but fails to move hero - iw.text = MetaString::createFromTextID("core.genrltxt.70"); // Dimension Door failed! - env->apply(iw); - // no return - resources will be spent - } - else - { - // HotA: game will show error message without taking mana or move points, even when DD into terra incognita - iw.text = MetaString::createFromTextID("vcmi.dimensionDoor.seaToLandError"); - env->apply(iw); - return ESpellCastResult::CANCEL; - } - } - - GiveBonus gb; - gb.id = ObjectInstanceID(parameters.caster->getCasterUnitId()); - gb.bonus = Bonus(BonusDuration::ONE_DAY, BonusType::NONE, BonusSource::SPELL_EFFECT, 0, BonusSourceID(owner->id)); - env->apply(gb); - - SetMovePoints smp; - smp.hid = ObjectInstanceID(parameters.caster->getCasterUnitId()); - if(movementCost < static_cast(parameters.caster->getHeroCaster()->movementPointsRemaining())) - smp.val = parameters.caster->getHeroCaster()->movementPointsRemaining() - movementCost; - else - smp.val = 0; - env->apply(smp); - - return ESpellCastResult::OK; -} - -void DimensionDoorMechanics::endCast(SpellCastEnvironment * env, const AdventureSpellCastParameters & parameters) const -{ - int3 casterPosition = parameters.caster->getHeroCaster()->getSightCenter(); - const TerrainTile * dest = env->getCb()->getTile(parameters.pos); - const TerrainTile * curr = env->getCb()->getTile(casterPosition); - - if(dest->isClear(curr)) - env->moveHero(ObjectInstanceID(parameters.caster->getCasterUnitId()), parameters.caster->getHeroCaster()->convertFromVisitablePos(parameters.pos), EMovementMode::DIMENSION_DOOR); -} - -///TownPortalMechanics -TownPortalMechanics::TownPortalMechanics(const CSpell * s): - AdventureSpellMechanics(s) -{ -} - -ESpellCastResult TownPortalMechanics::applyAdventureEffects(SpellCastEnvironment * env, const AdventureSpellCastParameters & parameters) const -{ - const CGTownInstance * destination = nullptr; - const int moveCost = movementCost(env, parameters); - - if(!parameters.caster->getHeroCaster()) - { - env->complain("Not a hero caster!"); - return ESpellCastResult::ERROR; - } - - if(parameters.caster->getSpellSchoolLevel(owner) < 2) - { - std::vector pool = getPossibleTowns(env, parameters); - destination = findNearestTown(env, parameters, pool); - - if(nullptr == destination) - return ESpellCastResult::ERROR; - - if(static_cast(parameters.caster->getHeroCaster()->movementPointsRemaining()) < moveCost) - return ESpellCastResult::ERROR; - - if(destination->getVisitingHero()) - { - InfoWindow iw; - iw.player = parameters.caster->getCasterOwner(); - iw.text.appendLocalString(EMetaText::GENERAL_TXT, 123); - env->apply(iw); - return ESpellCastResult::CANCEL; - } - } - else if(env->getMap()->isInTheMap(parameters.pos)) - { - const TerrainTile & tile = env->getMap()->getTile(parameters.pos); - - ObjectInstanceID topObjID = tile.topVisitableObj(false); - const CGObjectInstance * topObj = env->getMap()->getObject(topObjID); - - if(!topObj) - { - env->complain("Destination tile is not visitable" + parameters.pos.toString()); - return ESpellCastResult::ERROR; - } - else if(topObj->ID == Obj::HERO) - { - env->complain("Can't teleport to occupied town at " + parameters.pos.toString()); - return ESpellCastResult::ERROR; - } - else if(topObj->ID != Obj::TOWN) - { - env->complain("No town at destination tile " + parameters.pos.toString()); - return ESpellCastResult::ERROR; - } - - destination = dynamic_cast(topObj); - - if(nullptr == destination) - { - env->complain("[Internal error] invalid town object at " + parameters.pos.toString()); - return ESpellCastResult::ERROR; - } - - const auto relations = env->getCb()->getPlayerRelations(destination->tempOwner, parameters.caster->getCasterOwner()); - - if(relations == PlayerRelations::ENEMIES) - { - env->complain("Can't teleport to enemy!"); - return ESpellCastResult::ERROR; - } - - if(static_cast(parameters.caster->getHeroCaster()->movementPointsRemaining()) < moveCost) - { - env->complain("This hero has not enough movement points!"); - return ESpellCastResult::ERROR; - } - - if(destination->getVisitingHero()) - { - env->complain("[Internal error] Can't teleport to occupied town"); - return ESpellCastResult::ERROR; - } - } - else - { - env->complain("Invalid destination tile"); - return ESpellCastResult::ERROR; - } - - const TerrainTile & from = env->getMap()->getTile(parameters.caster->getHeroCaster()->visitablePos()); - const TerrainTile & dest = env->getMap()->getTile(destination->visitablePos()); - - if(!dest.entrableTerrain(&from)) - { - InfoWindow iw; - iw.player = parameters.caster->getCasterOwner(); - iw.text.appendLocalString(EMetaText::GENERAL_TXT, 135); - env->apply(iw); - return ESpellCastResult::ERROR; - } - - return ESpellCastResult::OK; -} - -void TownPortalMechanics::endCast(SpellCastEnvironment * env, const AdventureSpellCastParameters & parameters) const -{ - const int moveCost = movementCost(env, parameters); - const CGTownInstance * destination = nullptr; - - if(parameters.caster->getSpellSchoolLevel(owner) < 2) - { - std::vector pool = getPossibleTowns(env, parameters); - destination = findNearestTown(env, parameters, pool); - } - else - { - const TerrainTile & tile = env->getMap()->getTile(parameters.pos); - ObjectInstanceID topObjID = tile.topVisitableObj(false); - const CGObjectInstance * topObj = env->getMap()->getObject(topObjID); - - destination = dynamic_cast(topObj); - } - - if(env->moveHero(ObjectInstanceID(parameters.caster->getCasterUnitId()), parameters.caster->getHeroCaster()->convertFromVisitablePos(destination->visitablePos()), EMovementMode::TOWN_PORTAL)) - { - SetMovePoints smp; - smp.hid = ObjectInstanceID(parameters.caster->getCasterUnitId()); - smp.val = std::max(0, parameters.caster->getHeroCaster()->movementPointsRemaining() - moveCost); - env->apply(smp); - } -} - -ESpellCastResult TownPortalMechanics::beginCast(SpellCastEnvironment * env, const AdventureSpellCastParameters & parameters) const -{ - std::vector towns = getPossibleTowns(env, parameters); - - if(!parameters.caster->getHeroCaster()) - { - env->complain("Not a hero caster!"); - return ESpellCastResult::ERROR; - } - - if(towns.empty()) - { - InfoWindow iw; - iw.player = parameters.caster->getCasterOwner(); - iw.text.appendLocalString(EMetaText::GENERAL_TXT, 124); - env->apply(iw); - return ESpellCastResult::CANCEL; - } - - const int moveCost = movementCost(env, parameters); - - if(static_cast(parameters.caster->getHeroCaster()->movementPointsRemaining()) < moveCost) - { - InfoWindow iw; - iw.player = parameters.caster->getCasterOwner(); - iw.text.appendLocalString(EMetaText::GENERAL_TXT, 125); - env->apply(iw); - return ESpellCastResult::CANCEL; - } - - if(!parameters.pos.isValid() && parameters.caster->getSpellSchoolLevel(owner) >= 2) - { - auto queryCallback = [this, env, parameters](std::optional reply) -> void - { - if(reply.has_value()) - { - ObjectInstanceID townId(*reply); - - const CGObjectInstance * o = env->getCb()->getObj(townId, true); - if(o == nullptr) - { - env->complain("Invalid object instance selected"); - return; - } - - if(!dynamic_cast(o)) - { - env->complain("Object instance is not town"); - return; - } - - AdventureSpellCastParameters p; - p.caster = parameters.caster; - p.pos = o->visitablePos(); - performCast(env, p); - } - }; - - MapObjectSelectDialog request; - - for(const auto * t : towns) - { - if(t->getVisitingHero() == nullptr) //empty town - request.objects.push_back(t->id); - } - - if(request.objects.empty()) - { - InfoWindow iw; - iw.player = parameters.caster->getCasterOwner(); - iw.text.appendLocalString(EMetaText::GENERAL_TXT, 124); - env->apply(iw); - return ESpellCastResult::CANCEL; - } - - request.player = parameters.caster->getCasterOwner(); - request.title.appendLocalString(EMetaText::JK_TXT, 40); - request.description.appendLocalString(EMetaText::JK_TXT, 41); - request.icon = Component(ComponentType::SPELL, owner->id); - - env->genericQuery(&request, request.player, queryCallback); - - return ESpellCastResult::PENDING; - } - - return ESpellCastResult::OK; -} - -const CGTownInstance * TownPortalMechanics::findNearestTown(SpellCastEnvironment * env, const AdventureSpellCastParameters & parameters, const std::vector & pool) const -{ - if(pool.empty()) - return nullptr; - - if(!parameters.caster->getHeroCaster()) - return nullptr; - - auto nearest = pool.cbegin(); //nearest town's iterator - si32 dist = (*nearest)->visitablePos().dist2dSQ(parameters.caster->getHeroCaster()->visitablePos()); - - for(auto i = nearest + 1; i != pool.cend(); ++i) - { - si32 curDist = (*i)->visitablePos().dist2dSQ(parameters.caster->getHeroCaster()->visitablePos()); - - if(curDist < dist) - { - nearest = i; - dist = curDist; - } - } - return *nearest; -} - -std::vector TownPortalMechanics::getPossibleTowns(SpellCastEnvironment * env, const AdventureSpellCastParameters & parameters) const -{ - std::vector ret; - - const TeamState * team = env->getCb()->getPlayerTeam(parameters.caster->getCasterOwner()); - - for(const auto & color : team->players) - { - for(auto currTown : env->getCb()->getPlayerState(color)->getTowns()) - { - ret.push_back(currTown); - } - } - return ret; -} - -int32_t TownPortalMechanics::movementCost(SpellCastEnvironment * env, const AdventureSpellCastParameters & parameters) const -{ - if(parameters.caster != parameters.caster->getHeroCaster()) //if caster is not hero - return 0; - - int baseMovementCost = env->getCb()->getSettings().getInteger(EGameSettings::HEROES_MOVEMENT_COST_BASE); - return baseMovementCost * ((parameters.caster->getSpellSchoolLevel(owner) >= 3) ? 2 : 3); -} - -///ViewMechanics -ViewMechanics::ViewMechanics(const CSpell * s): - AdventureSpellMechanics(s) -{ -} - -ESpellCastResult ViewMechanics::applyAdventureEffects(SpellCastEnvironment * env, const AdventureSpellCastParameters & parameters) const -{ - ShowWorldViewEx pack; - - pack.player = parameters.caster->getCasterOwner(); - - const auto spellLevel = parameters.caster->getSpellSchoolLevel(owner); - - const auto & fowMap = env->getCb()->getPlayerTeam(parameters.caster->getCasterOwner())->fogOfWarMap; - - for(const auto & obj : env->getMap()->getObjects()) - { - //deleted object remain as empty pointer - if(obj && filterObject(obj, spellLevel)) - { - ObjectPosInfo posInfo(obj); - - if(fowMap[posInfo.pos.z][posInfo.pos.x][posInfo.pos.y] == 0) - pack.objectPositions.push_back(posInfo); - } - } - pack.showTerrain = showTerrain(spellLevel); - - env->apply(pack); - - return ESpellCastResult::OK; -} - -///ViewAirMechanics -ViewAirMechanics::ViewAirMechanics(const CSpell * s): - ViewMechanics(s) -{ -} - -bool ViewAirMechanics::filterObject(const CGObjectInstance * obj, const int32_t spellLevel) const -{ - return (obj->ID == Obj::ARTIFACT) || (spellLevel > 1 && obj->ID == Obj::HERO) || (spellLevel > 2 && obj->ID == Obj::TOWN); -} - -bool ViewAirMechanics::showTerrain(const int32_t spellLevel) const -{ - return false; -} - -///ViewEarthMechanics -ViewEarthMechanics::ViewEarthMechanics(const CSpell * s): - ViewMechanics(s) -{ -} - -bool ViewEarthMechanics::filterObject(const CGObjectInstance * obj, const int32_t spellLevel) const -{ - return (obj->ID == Obj::RESOURCE) || (spellLevel > 1 && obj->ID == Obj::MINE); -} - -bool ViewEarthMechanics::showTerrain(const int32_t spellLevel) const -{ - return spellLevel > 2; -} - -VCMI_LIB_NAMESPACE_END diff --git a/lib/spells/AdventureSpellMechanics.h b/lib/spells/AdventureSpellMechanics.h deleted file mode 100644 index 247dcd9f4..000000000 --- a/lib/spells/AdventureSpellMechanics.h +++ /dev/null @@ -1,123 +0,0 @@ -/* - * AdventureSpellMechanics.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 "ISpellMechanics.h" - -VCMI_LIB_NAMESPACE_BEGIN - -class CGTownInstance; - -enum class ESpellCastResult -{ - OK, // cast successful - CANCEL, // cast failed but it is not an error, no mana has been spent - PENDING, - ERROR// error occurred, for example invalid request from player -}; - -class AdventureSpellMechanics : public IAdventureSpellMechanics -{ -public: - AdventureSpellMechanics(const CSpell * s); - - bool canBeCast(spells::Problem & problem, const IGameInfoCallback * cb, const spells::Caster * caster) const final; - bool canBeCastAt(spells::Problem & problem, const IGameInfoCallback * cb, const spells::Caster * caster, const int3 & pos) const final; - - bool adventureCast(SpellCastEnvironment * env, const AdventureSpellCastParameters & parameters) const override final; -protected: - ///actual adventure cast implementation - virtual ESpellCastResult applyAdventureEffects(SpellCastEnvironment * env, const AdventureSpellCastParameters & parameters) const; - virtual ESpellCastResult beginCast(SpellCastEnvironment * env, const AdventureSpellCastParameters & parameters) const; - virtual void endCast(SpellCastEnvironment * env, const AdventureSpellCastParameters & parameters) const; - virtual bool canBeCastImpl(spells::Problem & problem, const IGameInfoCallback * cb, const spells::Caster * caster) const; - virtual bool canBeCastAtImpl(spells::Problem & problem, const IGameInfoCallback * cb, const spells::Caster * caster, const int3 & pos) const; - - void performCast(SpellCastEnvironment * env, const AdventureSpellCastParameters & parameters) const; -}; - -class SummonBoatMechanics final : public AdventureSpellMechanics -{ -public: - SummonBoatMechanics(const CSpell * s); -protected: - bool canBeCastImpl(spells::Problem & problem, const IGameInfoCallback * cb, const spells::Caster * caster) const override; - - ESpellCastResult applyAdventureEffects(SpellCastEnvironment * env, const AdventureSpellCastParameters & parameters) const override; -}; - -class ScuttleBoatMechanics final : public AdventureSpellMechanics -{ -public: - ScuttleBoatMechanics(const CSpell * s); -protected: - bool canBeCastAtImpl(spells::Problem & problem, const IGameInfoCallback * cb, const spells::Caster * caster, const int3 & pos) const override; - - ESpellCastResult applyAdventureEffects(SpellCastEnvironment * env, const AdventureSpellCastParameters & parameters) const override; -}; - -class DimensionDoorMechanics final : public AdventureSpellMechanics -{ -public: - DimensionDoorMechanics(const CSpell * s); -protected: - bool canBeCastImpl(spells::Problem & problem, const IGameInfoCallback * cb, const spells::Caster * caster) const override; - bool canBeCastAtImpl(spells::Problem & problem, const IGameInfoCallback * cb, const spells::Caster * caster, const int3 & pos) const override; - - ESpellCastResult applyAdventureEffects(SpellCastEnvironment * env, const AdventureSpellCastParameters & parameters) const override; - void endCast(SpellCastEnvironment * env, const AdventureSpellCastParameters & parameters) const override; -}; - -class TownPortalMechanics final : public AdventureSpellMechanics -{ -public: - TownPortalMechanics(const CSpell * s); -protected: - ESpellCastResult applyAdventureEffects(SpellCastEnvironment * env, const AdventureSpellCastParameters & parameters) const override; - ESpellCastResult beginCast(SpellCastEnvironment * env, const AdventureSpellCastParameters & parameters) const override; - void endCast(SpellCastEnvironment * env, const AdventureSpellCastParameters & parameters) const override; -private: - const CGTownInstance * findNearestTown(SpellCastEnvironment * env, const AdventureSpellCastParameters & parameters, const std::vector & pool) const; - int32_t movementCost(SpellCastEnvironment * env, const AdventureSpellCastParameters & parameters) const; - std::vector getPossibleTowns(SpellCastEnvironment * env, const AdventureSpellCastParameters & parameters) const; -}; - -class ViewMechanics : public AdventureSpellMechanics -{ -public: - ViewMechanics(const CSpell * s); -protected: - ESpellCastResult applyAdventureEffects(SpellCastEnvironment * env, const AdventureSpellCastParameters & parameters) const override; - virtual bool filterObject(const CGObjectInstance * obj, const int32_t spellLevel) const = 0; - virtual bool showTerrain(const int32_t spellLevel) const = 0; -}; - -class ViewAirMechanics final : public ViewMechanics -{ -public: - ViewAirMechanics(const CSpell * s); -protected: - bool filterObject(const CGObjectInstance * obj, const int32_t spellLevel) const override; - bool showTerrain(const int32_t spellLevel) const override; -}; - -class ViewEarthMechanics final : public ViewMechanics -{ -public: - ViewEarthMechanics(const CSpell * s); -protected: - bool filterObject(const CGObjectInstance * obj, const int32_t spellLevel) const override; - bool showTerrain(const int32_t spellLevel) const override; -}; - - - -VCMI_LIB_NAMESPACE_END diff --git a/lib/spells/ISpellMechanics.cpp b/lib/spells/ISpellMechanics.cpp index d337de05e..192cbe064 100644 --- a/lib/spells/ISpellMechanics.cpp +++ b/lib/spells/ISpellMechanics.cpp @@ -26,7 +26,13 @@ #include "TargetCondition.h" #include "Problem.h" -#include "AdventureSpellMechanics.h" +#include "adventure/AdventureSpellMechanics.h" +#include "adventure/DimensionDoorMechanics.h" +#include "adventure/ScuttleBoatMechanics.h" +#include "adventure/SummonBoatMechanics.h" +#include "adventure/TownPortalMechanics.h" +#include "adventure/ViewWorldMechanics.h" + #include "BattleSpellMechanics.h" #include "effects/Effects.h" diff --git a/lib/spells/adventure/AdventureSpellMechanics.cpp b/lib/spells/adventure/AdventureSpellMechanics.cpp new file mode 100644 index 000000000..f5d5f6a46 --- /dev/null +++ b/lib/spells/adventure/AdventureSpellMechanics.cpp @@ -0,0 +1,144 @@ +/* + * AdventureSpellMechanics.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 "AdventureSpellMechanics.h" + +#include "../CSpellHandler.h" +#include "../Problem.h" + +#include "../../mapObjects/CGHeroInstance.h" +#include "../../networkPacks/PacksForClient.h" + +VCMI_LIB_NAMESPACE_BEGIN + +AdventureSpellMechanics::AdventureSpellMechanics(const CSpell * s) + : IAdventureSpellMechanics(s) +{ +} + +bool AdventureSpellMechanics::canBeCast(spells::Problem & problem, const IGameInfoCallback * cb, const spells::Caster * caster) const +{ + if(!owner->isAdventure()) + return false; + + const auto * heroCaster = dynamic_cast(caster); + + if(heroCaster) + { + if(heroCaster->isGarrisoned()) + return false; + + const auto level = heroCaster->getSpellSchoolLevel(owner); + const auto cost = owner->getCost(level); + + if(!heroCaster->canCastThisSpell(owner)) + return false; + + if(heroCaster->mana < cost) + return false; + } + + return canBeCastImpl(problem, cb, caster); +} + +bool AdventureSpellMechanics::canBeCastAt(spells::Problem & problem, const IGameInfoCallback * cb, const spells::Caster * caster, const int3 & pos) const +{ + return canBeCast(problem, cb, caster) && canBeCastAtImpl(problem, cb, caster, pos); +} + +bool AdventureSpellMechanics::canBeCastImpl(spells::Problem & problem, const IGameInfoCallback * cb, const spells::Caster * caster) const +{ + return true; +} + +bool AdventureSpellMechanics::canBeCastAtImpl(spells::Problem & problem, const IGameInfoCallback * cb, const spells::Caster * caster, const int3 & pos) const +{ + return true; +} + +bool AdventureSpellMechanics::adventureCast(SpellCastEnvironment * env, const AdventureSpellCastParameters & parameters) const +{ + spells::detail::ProblemImpl problem; + + if(!canBeCastAt(problem, env->getCb(), parameters.caster, parameters.pos)) + return false; + + ESpellCastResult result = beginCast(env, parameters); + + if(result == ESpellCastResult::OK) + performCast(env, parameters); + + return result != ESpellCastResult::ERROR; +} + +ESpellCastResult AdventureSpellMechanics::applyAdventureEffects(SpellCastEnvironment * env, const AdventureSpellCastParameters & parameters) const +{ + if(owner->hasEffects()) + { + //todo: cumulative effects support + const auto schoolLevel = parameters.caster->getSpellSchoolLevel(owner); + + std::vector bonuses; + + owner->getEffects(bonuses, schoolLevel, false, parameters.caster->getEnchantPower(owner)); + + for(const Bonus & b : bonuses) + { + GiveBonus gb; + gb.id = ObjectInstanceID(parameters.caster->getCasterUnitId()); + gb.bonus = b; + env->apply(gb); + } + + return ESpellCastResult::OK; + } + else + { + //There is no generic algorithm of adventure cast + env->complain("Unimplemented adventure spell"); + return ESpellCastResult::ERROR; + } +} + +ESpellCastResult AdventureSpellMechanics::beginCast(SpellCastEnvironment * env, const AdventureSpellCastParameters & parameters) const +{ + return ESpellCastResult::OK; +} + +void AdventureSpellMechanics::endCast(SpellCastEnvironment * env, const AdventureSpellCastParameters & parameters) const +{ + // no-op, only for implementation in derived classes +} + +void AdventureSpellMechanics::performCast(SpellCastEnvironment * env, const AdventureSpellCastParameters & parameters) const +{ + const auto level = parameters.caster->getSpellSchoolLevel(owner); + const auto cost = owner->getCost(level); + + AdvmapSpellCast asc; + asc.casterID = ObjectInstanceID(parameters.caster->getCasterUnitId()); + asc.spellID = owner->id; + env->apply(asc); + + ESpellCastResult result = applyAdventureEffects(env, parameters); + + switch(result) + { + case ESpellCastResult::OK: + parameters.caster->spendMana(env, cost); + endCast(env, parameters); + break; + default: + break; + } +} + +VCMI_LIB_NAMESPACE_END diff --git a/lib/spells/adventure/AdventureSpellMechanics.h b/lib/spells/adventure/AdventureSpellMechanics.h new file mode 100644 index 000000000..ae352272e --- /dev/null +++ b/lib/spells/adventure/AdventureSpellMechanics.h @@ -0,0 +1,46 @@ +/* + * AdventureSpellMechanics.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 "../ISpellMechanics.h" + +VCMI_LIB_NAMESPACE_BEGIN + +enum class ESpellCastResult +{ + OK, // cast successful + CANCEL, // cast failed but it is not an error, no mana has been spent + PENDING, + ERROR // error occurred, for example invalid request from player +}; + +class AdventureSpellMechanics : public IAdventureSpellMechanics +{ +public: + AdventureSpellMechanics(const CSpell * s); + + bool canBeCast(spells::Problem & problem, const IGameInfoCallback * cb, const spells::Caster * caster) const final; + bool canBeCastAt(spells::Problem & problem, const IGameInfoCallback * cb, const spells::Caster * caster, const int3 & pos) const final; + + bool adventureCast(SpellCastEnvironment * env, const AdventureSpellCastParameters & parameters) const override final; + +protected: + ///actual adventure cast implementation + virtual ESpellCastResult applyAdventureEffects(SpellCastEnvironment * env, const AdventureSpellCastParameters & parameters) const; + virtual ESpellCastResult beginCast(SpellCastEnvironment * env, const AdventureSpellCastParameters & parameters) const; + virtual void endCast(SpellCastEnvironment * env, const AdventureSpellCastParameters & parameters) const; + virtual bool canBeCastImpl(spells::Problem & problem, const IGameInfoCallback * cb, const spells::Caster * caster) const; + virtual bool canBeCastAtImpl(spells::Problem & problem, const IGameInfoCallback * cb, const spells::Caster * caster, const int3 & pos) const; + + void performCast(SpellCastEnvironment * env, const AdventureSpellCastParameters & parameters) const; +}; + +VCMI_LIB_NAMESPACE_END diff --git a/lib/spells/adventure/DimensionDoorMechanics.cpp b/lib/spells/adventure/DimensionDoorMechanics.cpp new file mode 100644 index 000000000..72046d965 --- /dev/null +++ b/lib/spells/adventure/DimensionDoorMechanics.cpp @@ -0,0 +1,166 @@ +/* + * DimensionDoorMechanics.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 "DimensionDoorMechanics.h" + +#include "../CSpellHandler.h" + +#include "../../IGameSettings.h" +#include "../../callback/IGameInfoCallback.h" +#include "../../mapObjects/CGHeroInstance.h" +#include "../../mapping/TerrainTile.h" +#include "../../networkPacks/PacksForClient.h" + +VCMI_LIB_NAMESPACE_BEGIN + +DimensionDoorMechanics::DimensionDoorMechanics(const CSpell * s) + : AdventureSpellMechanics(s) +{ +} + +bool DimensionDoorMechanics::canBeCastImpl(spells::Problem & problem, const IGameInfoCallback * cb, const spells::Caster * caster) const +{ + if(!caster->getHeroCaster()) + return false; + + if(caster->getHeroCaster()->movementPointsRemaining() <= 0) //unlike town portal non-zero MP is enough + { + problem.add(MetaString::createFromTextID("core.genrltxt.125")); + return false; + } + + const auto schoolLevel = caster->getSpellSchoolLevel(owner); + + std::stringstream cachingStr; + cachingStr << "source_" << vstd::to_underlying(BonusSource::SPELL_EFFECT) << "id_" << owner->id.num; + + int castsAlreadyPerformedThisTurn = caster->getHeroCaster()->getBonuses(Selector::source(BonusSource::SPELL_EFFECT, BonusSourceID(owner->id)), cachingStr.str())->size(); + int castsLimit = owner->getLevelPower(schoolLevel); + + bool isTournamentRulesLimitEnabled = cb->getSettings().getBoolean(EGameSettings::DIMENSION_DOOR_TOURNAMENT_RULES_LIMIT); + if(isTournamentRulesLimitEnabled) + { + int3 mapSize = cb->getMapSize(); + + bool meetsTournamentRulesTwoCastsRequirements = mapSize.x * mapSize.y * mapSize.z >= GameConstants::TOURNAMENT_RULES_DD_MAP_TILES_THRESHOLD && schoolLevel == MasteryLevel::EXPERT; + + castsLimit = meetsTournamentRulesTwoCastsRequirements ? 2 : 1; + } + + if(castsAlreadyPerformedThisTurn >= castsLimit) //limit casts per turn + { + MetaString message = MetaString::createFromTextID("core.genrltxt.338"); + caster->getCasterName(message); + problem.add(std::move(message)); + return false; + } + + return true; +} + +bool DimensionDoorMechanics::canBeCastAtImpl(spells::Problem & problem, const IGameInfoCallback * cb, const spells::Caster * caster, const int3 & pos) const +{ + if(!cb->isInTheMap(pos)) + return false; + + if(cb->getSettings().getBoolean(EGameSettings::DIMENSION_DOOR_ONLY_TO_UNCOVERED_TILES)) + { + if(!cb->isVisibleFor(pos, caster->getCasterOwner())) + return false; + } + + int3 casterPosition = caster->getHeroCaster()->getSightCenter(); + + const TerrainTile * dest = cb->getTileUnchecked(pos); + const TerrainTile * curr = cb->getTileUnchecked(casterPosition); + + if(!dest) + return false; + + if(!curr) + return false; + + if(!isInScreenRange(casterPosition, pos)) + return false; + + if(cb->getSettings().getBoolean(EGameSettings::DIMENSION_DOOR_EXPOSES_TERRAIN_TYPE)) + { + if(!dest->isClear(curr)) + return false; + } + else + { + if(dest->blocked()) + return false; + } + + return true; +} + +ESpellCastResult DimensionDoorMechanics::applyAdventureEffects(SpellCastEnvironment * env, const AdventureSpellCastParameters & parameters) const +{ + const auto schoolLevel = parameters.caster->getSpellSchoolLevel(owner); + const int baseCost = env->getCb()->getSettings().getInteger(EGameSettings::HEROES_MOVEMENT_COST_BASE); + const int movementCost = baseCost * ((schoolLevel >= 3) ? 2 : 3); + + int3 casterPosition = parameters.caster->getHeroCaster()->getSightCenter(); + const TerrainTile * dest = env->getCb()->getTile(parameters.pos); + const TerrainTile * curr = env->getCb()->getTile(casterPosition); + + if(!dest->isClear(curr)) + { + InfoWindow iw; + iw.player = parameters.caster->getCasterOwner(); + + // tile is either blocked or not possible to move (e.g. water <-> land) + if(env->getCb()->getSettings().getBoolean(EGameSettings::DIMENSION_DOOR_FAILURE_SPENDS_POINTS)) + { + // SOD: DD to such "wrong" terrain results in mana and move points spending, but fails to move hero + iw.text = MetaString::createFromTextID("core.genrltxt.70"); // Dimension Door failed! + env->apply(iw); + // no return - resources will be spent + } + else + { + // HotA: game will show error message without taking mana or move points, even when DD into terra incognita + iw.text = MetaString::createFromTextID("vcmi.dimensionDoor.seaToLandError"); + env->apply(iw); + return ESpellCastResult::CANCEL; + } + } + + GiveBonus gb; + gb.id = ObjectInstanceID(parameters.caster->getCasterUnitId()); + gb.bonus = Bonus(BonusDuration::ONE_DAY, BonusType::NONE, BonusSource::SPELL_EFFECT, 0, BonusSourceID(owner->id)); + env->apply(gb); + + SetMovePoints smp; + smp.hid = ObjectInstanceID(parameters.caster->getCasterUnitId()); + if(movementCost < static_cast(parameters.caster->getHeroCaster()->movementPointsRemaining())) + smp.val = parameters.caster->getHeroCaster()->movementPointsRemaining() - movementCost; + else + smp.val = 0; + env->apply(smp); + + return ESpellCastResult::OK; +} + +void DimensionDoorMechanics::endCast(SpellCastEnvironment * env, const AdventureSpellCastParameters & parameters) const +{ + int3 casterPosition = parameters.caster->getHeroCaster()->getSightCenter(); + const TerrainTile * dest = env->getCb()->getTile(parameters.pos); + const TerrainTile * curr = env->getCb()->getTile(casterPosition); + + if(dest->isClear(curr)) + env->moveHero(ObjectInstanceID(parameters.caster->getCasterUnitId()), parameters.caster->getHeroCaster()->convertFromVisitablePos(parameters.pos), EMovementMode::DIMENSION_DOOR); +} + +VCMI_LIB_NAMESPACE_END diff --git a/lib/spells/adventure/DimensionDoorMechanics.h b/lib/spells/adventure/DimensionDoorMechanics.h new file mode 100644 index 000000000..cf5e32b6f --- /dev/null +++ b/lib/spells/adventure/DimensionDoorMechanics.h @@ -0,0 +1,30 @@ +/* + * DimensionDoorMechanics.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 "AdventureSpellMechanics.h" + +VCMI_LIB_NAMESPACE_BEGIN + +class DimensionDoorMechanics final : public AdventureSpellMechanics +{ +public: + DimensionDoorMechanics(const CSpell * s); + +protected: + bool canBeCastImpl(spells::Problem & problem, const IGameInfoCallback * cb, const spells::Caster * caster) const override; + bool canBeCastAtImpl(spells::Problem & problem, const IGameInfoCallback * cb, const spells::Caster * caster, const int3 & pos) const override; + + ESpellCastResult applyAdventureEffects(SpellCastEnvironment * env, const AdventureSpellCastParameters & parameters) const override; + void endCast(SpellCastEnvironment * env, const AdventureSpellCastParameters & parameters) const override; +}; + +VCMI_LIB_NAMESPACE_END diff --git a/lib/spells/adventure/ScuttleBoatMechanics.cpp b/lib/spells/adventure/ScuttleBoatMechanics.cpp new file mode 100644 index 000000000..24b8bfe8b --- /dev/null +++ b/lib/spells/adventure/ScuttleBoatMechanics.cpp @@ -0,0 +1,78 @@ +/* + * ScuttleBoatMechanics.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 "ScuttleBoatMechanics.h" + +#include "../CSpellHandler.h" + +#include "../../callback/IGameInfoCallback.h" +#include "../../mapObjects/CGHeroInstance.h" +#include "../../mapping/CMap.h" +#include "../../networkPacks/PacksForClient.h" + +VCMI_LIB_NAMESPACE_BEGIN + +ScuttleBoatMechanics::ScuttleBoatMechanics(const CSpell * s) + : AdventureSpellMechanics(s) +{ +} + +bool ScuttleBoatMechanics::canBeCastAtImpl(spells::Problem & problem, const IGameInfoCallback * cb, const spells::Caster * caster, const int3 & pos) const +{ + if(!cb->isInTheMap(pos)) + return false; + + if(caster->getHeroCaster()) + { + int3 casterPosition = caster->getHeroCaster()->getSightCenter(); + + if(!isInScreenRange(casterPosition, pos)) + return false; + } + + if(!cb->isVisibleFor(pos, caster->getCasterOwner())) + return false; + + const TerrainTile * t = cb->getTile(pos); + if(!t || t->visitableObjects.empty()) + return false; + + const CGObjectInstance * topObject = cb->getObj(t->visitableObjects.back()); + if(topObject->ID != Obj::BOAT) + return false; + + return true; +} + +ESpellCastResult ScuttleBoatMechanics::applyAdventureEffects(SpellCastEnvironment * env, const AdventureSpellCastParameters & parameters) const +{ + const auto schoolLevel = parameters.caster->getSpellSchoolLevel(owner); + //check if spell works at all + if(env->getRNG()->nextInt(0, 99) >= owner->getLevelPower(schoolLevel)) //power is % chance of success + { + InfoWindow iw; + iw.player = parameters.caster->getCasterOwner(); + iw.text.appendLocalString(EMetaText::GENERAL_TXT, 337); //%s tried to scuttle the boat, but failed + parameters.caster->getCasterName(iw.text); + env->apply(iw); + return ESpellCastResult::OK; + } + + const TerrainTile & t = env->getMap()->getTile(parameters.pos); + + RemoveObject ro; + ro.initiator = parameters.caster->getCasterOwner(); + ro.objectID = t.visitableObjects.back(); + env->apply(ro); + return ESpellCastResult::OK; +} + +VCMI_LIB_NAMESPACE_END diff --git a/lib/spells/adventure/ScuttleBoatMechanics.h b/lib/spells/adventure/ScuttleBoatMechanics.h new file mode 100644 index 000000000..dec0b4037 --- /dev/null +++ b/lib/spells/adventure/ScuttleBoatMechanics.h @@ -0,0 +1,28 @@ +/* + * ScuttleBoatMechanics.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 "AdventureSpellMechanics.h" + +VCMI_LIB_NAMESPACE_BEGIN + +class ScuttleBoatMechanics final : public AdventureSpellMechanics +{ +public: + ScuttleBoatMechanics(const CSpell * s); + +protected: + bool canBeCastAtImpl(spells::Problem & problem, const IGameInfoCallback * cb, const spells::Caster * caster, const int3 & pos) const override; + + ESpellCastResult applyAdventureEffects(SpellCastEnvironment * env, const AdventureSpellCastParameters & parameters) const override; +}; + +VCMI_LIB_NAMESPACE_END diff --git a/lib/spells/adventure/SummonBoatMechanics.cpp b/lib/spells/adventure/SummonBoatMechanics.cpp new file mode 100644 index 000000000..6d4a0341d --- /dev/null +++ b/lib/spells/adventure/SummonBoatMechanics.cpp @@ -0,0 +1,111 @@ +/* + * SummonBoatMechanics.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 "SummonBoatMechanics.h" + +#include "../CSpellHandler.h" + +#include "../../mapObjects/CGHeroInstance.h" +#include "../../mapObjects/MiscObjects.h" +#include "../../mapping/CMap.h" +#include "../../networkPacks/PacksForClient.h" + +VCMI_LIB_NAMESPACE_BEGIN + +SummonBoatMechanics::SummonBoatMechanics(const CSpell * s) + : AdventureSpellMechanics(s) +{ +} + +bool SummonBoatMechanics::canBeCastImpl(spells::Problem & problem, const IGameInfoCallback * cb, const spells::Caster * caster) const +{ + if(!caster->getHeroCaster()) + return false; + + if(caster->getHeroCaster()->inBoat()) + { + MetaString message = MetaString::createFromTextID("core.genrltxt.333"); + caster->getCasterName(message); + problem.add(std::move(message)); + return false; + } + + int3 summonPos = caster->getHeroCaster()->bestLocation(); + + if(summonPos.x < 0) + { + MetaString message = MetaString::createFromTextID("core.genrltxt.334"); + caster->getCasterName(message); + problem.add(std::move(message)); + return false; + } + + return true; +} + +ESpellCastResult SummonBoatMechanics::applyAdventureEffects(SpellCastEnvironment * env, const AdventureSpellCastParameters & parameters) const +{ + const auto schoolLevel = parameters.caster->getSpellSchoolLevel(owner); + + //check if spell works at all + if(env->getRNG()->nextInt(0, 99) >= owner->getLevelPower(schoolLevel)) //power is % chance of success + { + InfoWindow iw; + iw.player = parameters.caster->getCasterOwner(); + iw.text.appendLocalString(EMetaText::GENERAL_TXT, 336); //%s tried to summon a boat, but failed. + parameters.caster->getCasterName(iw.text); + env->apply(iw); + return ESpellCastResult::OK; + } + + //try to find unoccupied boat to summon + const CGBoat * nearest = nullptr; + double dist = 0; + for(const auto & b : env->getMap()->getObjects()) + { + if(b->getBoardedHero() || b->layer != EPathfindingLayer::SAIL) + continue; //we're looking for unoccupied boat + + double nDist = b->visitablePos().dist2d(parameters.caster->getHeroCaster()->visitablePos()); + if(!nearest || nDist < dist) //it's first boat or closer than previous + { + nearest = b; + dist = nDist; + } + } + + int3 summonPos = parameters.caster->getHeroCaster()->bestLocation(); + + if(nullptr != nearest) //we found boat to summon + { + ChangeObjPos cop; + cop.objid = nearest->id; + cop.nPos = summonPos; + cop.initiator = parameters.caster->getCasterOwner(); + env->apply(cop); + } + else if(schoolLevel < 2) //none or basic level -> cannot create boat :( + { + InfoWindow iw; + iw.player = parameters.caster->getCasterOwner(); + iw.text.appendLocalString(EMetaText::GENERAL_TXT, 335); //There are no boats to summon. + env->apply(iw); + return ESpellCastResult::ERROR; + } + else //create boat + { + env->createBoat(summonPos, BoatId::NECROPOLIS, parameters.caster->getCasterOwner()); + } + return ESpellCastResult::OK; +} + +VCMI_LIB_NAMESPACE_END diff --git a/lib/spells/adventure/SummonBoatMechanics.h b/lib/spells/adventure/SummonBoatMechanics.h new file mode 100644 index 000000000..553abaa9c --- /dev/null +++ b/lib/spells/adventure/SummonBoatMechanics.h @@ -0,0 +1,28 @@ +/* + * SummonBoatMechanics.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 "AdventureSpellMechanics.h" + +VCMI_LIB_NAMESPACE_BEGIN + +class SummonBoatMechanics final : public AdventureSpellMechanics +{ +public: + SummonBoatMechanics(const CSpell * s); + +protected: + bool canBeCastImpl(spells::Problem & problem, const IGameInfoCallback * cb, const spells::Caster * caster) const override; + + ESpellCastResult applyAdventureEffects(SpellCastEnvironment * env, const AdventureSpellCastParameters & parameters) const override; +}; + +VCMI_LIB_NAMESPACE_END diff --git a/lib/spells/adventure/TownPortalMechanics.cpp b/lib/spells/adventure/TownPortalMechanics.cpp new file mode 100644 index 000000000..e67df29d7 --- /dev/null +++ b/lib/spells/adventure/TownPortalMechanics.cpp @@ -0,0 +1,299 @@ +/* + * TownPortalMechanics.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 "TownPortalMechanics.h" + +#include "../CSpellHandler.h" + +#include "../../CPlayerState.h" +#include "../../IGameSettings.h" +#include "../../callback/IGameInfoCallback.h" +#include "../../mapObjects/CGHeroInstance.h" +#include "../../mapObjects/CGTownInstance.h" +#include "../../mapping/CMap.h" +#include "../../networkPacks/PacksForClient.h" + +VCMI_LIB_NAMESPACE_BEGIN + +TownPortalMechanics::TownPortalMechanics(const CSpell * s): + AdventureSpellMechanics(s) +{ +} + +ESpellCastResult TownPortalMechanics::applyAdventureEffects(SpellCastEnvironment * env, const AdventureSpellCastParameters & parameters) const +{ + const CGTownInstance * destination = nullptr; + const int moveCost = movementCost(env, parameters); + + if(!parameters.caster->getHeroCaster()) + { + env->complain("Not a hero caster!"); + return ESpellCastResult::ERROR; + } + + if(parameters.caster->getSpellSchoolLevel(owner) < 2) + { + std::vector pool = getPossibleTowns(env, parameters); + destination = findNearestTown(env, parameters, pool); + + if(nullptr == destination) + return ESpellCastResult::ERROR; + + if(static_cast(parameters.caster->getHeroCaster()->movementPointsRemaining()) < moveCost) + return ESpellCastResult::ERROR; + + if(destination->getVisitingHero()) + { + InfoWindow iw; + iw.player = parameters.caster->getCasterOwner(); + iw.text.appendLocalString(EMetaText::GENERAL_TXT, 123); + env->apply(iw); + return ESpellCastResult::CANCEL; + } + } + else if(env->getMap()->isInTheMap(parameters.pos)) + { + const TerrainTile & tile = env->getMap()->getTile(parameters.pos); + + ObjectInstanceID topObjID = tile.topVisitableObj(false); + const CGObjectInstance * topObj = env->getMap()->getObject(topObjID); + + if(!topObj) + { + env->complain("Destination tile is not visitable" + parameters.pos.toString()); + return ESpellCastResult::ERROR; + } + else if(topObj->ID == Obj::HERO) + { + env->complain("Can't teleport to occupied town at " + parameters.pos.toString()); + return ESpellCastResult::ERROR; + } + else if(topObj->ID != Obj::TOWN) + { + env->complain("No town at destination tile " + parameters.pos.toString()); + return ESpellCastResult::ERROR; + } + + destination = dynamic_cast(topObj); + + if(nullptr == destination) + { + env->complain("[Internal error] invalid town object at " + parameters.pos.toString()); + return ESpellCastResult::ERROR; + } + + const auto relations = env->getCb()->getPlayerRelations(destination->tempOwner, parameters.caster->getCasterOwner()); + + if(relations == PlayerRelations::ENEMIES) + { + env->complain("Can't teleport to enemy!"); + return ESpellCastResult::ERROR; + } + + if(static_cast(parameters.caster->getHeroCaster()->movementPointsRemaining()) < moveCost) + { + env->complain("This hero has not enough movement points!"); + return ESpellCastResult::ERROR; + } + + if(destination->getVisitingHero()) + { + env->complain("[Internal error] Can't teleport to occupied town"); + return ESpellCastResult::ERROR; + } + } + else + { + env->complain("Invalid destination tile"); + return ESpellCastResult::ERROR; + } + + const TerrainTile & from = env->getMap()->getTile(parameters.caster->getHeroCaster()->visitablePos()); + const TerrainTile & dest = env->getMap()->getTile(destination->visitablePos()); + + if(!dest.entrableTerrain(&from)) + { + InfoWindow iw; + iw.player = parameters.caster->getCasterOwner(); + iw.text.appendLocalString(EMetaText::GENERAL_TXT, 135); + env->apply(iw); + return ESpellCastResult::ERROR; + } + + return ESpellCastResult::OK; +} + +void TownPortalMechanics::endCast(SpellCastEnvironment * env, const AdventureSpellCastParameters & parameters) const +{ + const int moveCost = movementCost(env, parameters); + const CGTownInstance * destination = nullptr; + + if(parameters.caster->getSpellSchoolLevel(owner) < 2) + { + std::vector pool = getPossibleTowns(env, parameters); + destination = findNearestTown(env, parameters, pool); + } + else + { + const TerrainTile & tile = env->getMap()->getTile(parameters.pos); + ObjectInstanceID topObjID = tile.topVisitableObj(false); + const CGObjectInstance * topObj = env->getMap()->getObject(topObjID); + + destination = dynamic_cast(topObj); + } + + if(env->moveHero(ObjectInstanceID(parameters.caster->getCasterUnitId()), parameters.caster->getHeroCaster()->convertFromVisitablePos(destination->visitablePos()), EMovementMode::TOWN_PORTAL)) + { + SetMovePoints smp; + smp.hid = ObjectInstanceID(parameters.caster->getCasterUnitId()); + smp.val = std::max(0, parameters.caster->getHeroCaster()->movementPointsRemaining() - moveCost); + env->apply(smp); + } +} + +ESpellCastResult TownPortalMechanics::beginCast(SpellCastEnvironment * env, const AdventureSpellCastParameters & parameters) const +{ + std::vector towns = getPossibleTowns(env, parameters); + + if(!parameters.caster->getHeroCaster()) + { + env->complain("Not a hero caster!"); + return ESpellCastResult::ERROR; + } + + if(towns.empty()) + { + InfoWindow iw; + iw.player = parameters.caster->getCasterOwner(); + iw.text.appendLocalString(EMetaText::GENERAL_TXT, 124); + env->apply(iw); + return ESpellCastResult::CANCEL; + } + + const int moveCost = movementCost(env, parameters); + + if(static_cast(parameters.caster->getHeroCaster()->movementPointsRemaining()) < moveCost) + { + InfoWindow iw; + iw.player = parameters.caster->getCasterOwner(); + iw.text.appendLocalString(EMetaText::GENERAL_TXT, 125); + env->apply(iw); + return ESpellCastResult::CANCEL; + } + + if(!parameters.pos.isValid() && parameters.caster->getSpellSchoolLevel(owner) >= 2) + { + auto queryCallback = [this, env, parameters](std::optional reply) -> void + { + if(reply.has_value()) + { + ObjectInstanceID townId(*reply); + + const CGObjectInstance * o = env->getCb()->getObj(townId, true); + if(o == nullptr) + { + env->complain("Invalid object instance selected"); + return; + } + + if(!dynamic_cast(o)) + { + env->complain("Object instance is not town"); + return; + } + + AdventureSpellCastParameters p; + p.caster = parameters.caster; + p.pos = o->visitablePos(); + performCast(env, p); + } + }; + + MapObjectSelectDialog request; + + for(const auto * t : towns) + { + if(t->getVisitingHero() == nullptr) //empty town + request.objects.push_back(t->id); + } + + if(request.objects.empty()) + { + InfoWindow iw; + iw.player = parameters.caster->getCasterOwner(); + iw.text.appendLocalString(EMetaText::GENERAL_TXT, 124); + env->apply(iw); + return ESpellCastResult::CANCEL; + } + + request.player = parameters.caster->getCasterOwner(); + request.title.appendLocalString(EMetaText::JK_TXT, 40); + request.description.appendLocalString(EMetaText::JK_TXT, 41); + request.icon = Component(ComponentType::SPELL, owner->id); + + env->genericQuery(&request, request.player, queryCallback); + + return ESpellCastResult::PENDING; + } + + return ESpellCastResult::OK; +} + +const CGTownInstance * TownPortalMechanics::findNearestTown(SpellCastEnvironment * env, const AdventureSpellCastParameters & parameters, const std::vector & pool) const +{ + if(pool.empty()) + return nullptr; + + if(!parameters.caster->getHeroCaster()) + return nullptr; + + auto nearest = pool.cbegin(); //nearest town's iterator + si32 dist = (*nearest)->visitablePos().dist2dSQ(parameters.caster->getHeroCaster()->visitablePos()); + + for(auto i = nearest + 1; i != pool.cend(); ++i) + { + si32 curDist = (*i)->visitablePos().dist2dSQ(parameters.caster->getHeroCaster()->visitablePos()); + + if(curDist < dist) + { + nearest = i; + dist = curDist; + } + } + return *nearest; +} + +std::vector TownPortalMechanics::getPossibleTowns(SpellCastEnvironment * env, const AdventureSpellCastParameters & parameters) const +{ + std::vector ret; + + const TeamState * team = env->getCb()->getPlayerTeam(parameters.caster->getCasterOwner()); + + for(const auto & color : team->players) + { + for(auto currTown : env->getCb()->getPlayerState(color)->getTowns()) + { + ret.push_back(currTown); + } + } + return ret; +} + +int32_t TownPortalMechanics::movementCost(SpellCastEnvironment * env, const AdventureSpellCastParameters & parameters) const +{ + if(parameters.caster != parameters.caster->getHeroCaster()) //if caster is not hero + return 0; + + int baseMovementCost = env->getCb()->getSettings().getInteger(EGameSettings::HEROES_MOVEMENT_COST_BASE); + return baseMovementCost * ((parameters.caster->getSpellSchoolLevel(owner) >= 3) ? 2 : 3); +} + +VCMI_LIB_NAMESPACE_END diff --git a/lib/spells/adventure/TownPortalMechanics.h b/lib/spells/adventure/TownPortalMechanics.h new file mode 100644 index 000000000..e0d227678 --- /dev/null +++ b/lib/spells/adventure/TownPortalMechanics.h @@ -0,0 +1,35 @@ +/* + * TownPortalMechanics.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 "AdventureSpellMechanics.h" + +VCMI_LIB_NAMESPACE_BEGIN + +class CGTownInstance; + +class TownPortalMechanics final : public AdventureSpellMechanics +{ +public: + TownPortalMechanics(const CSpell * s); + +protected: + ESpellCastResult applyAdventureEffects(SpellCastEnvironment * env, const AdventureSpellCastParameters & parameters) const override; + ESpellCastResult beginCast(SpellCastEnvironment * env, const AdventureSpellCastParameters & parameters) const override; + void endCast(SpellCastEnvironment * env, const AdventureSpellCastParameters & parameters) const override; + +private: + const CGTownInstance * findNearestTown(SpellCastEnvironment * env, const AdventureSpellCastParameters & parameters, const std::vector & pool) const; + int32_t movementCost(SpellCastEnvironment * env, const AdventureSpellCastParameters & parameters) const; + std::vector getPossibleTowns(SpellCastEnvironment * env, const AdventureSpellCastParameters & parameters) const; +}; + +VCMI_LIB_NAMESPACE_END diff --git a/lib/spells/adventure/ViewWorldMechanics.cpp b/lib/spells/adventure/ViewWorldMechanics.cpp new file mode 100644 index 000000000..7d7c45aa9 --- /dev/null +++ b/lib/spells/adventure/ViewWorldMechanics.cpp @@ -0,0 +1,90 @@ +/* + * ViewWorldMechanics.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 "ViewWorldMechanics.h" + +#include "../CSpellHandler.h" + +#include "../../CPlayerState.h" +#include "../../callback/IGameInfoCallback.h" +#include "../../mapObjects/CGHeroInstance.h" +#include "../../mapping/CMap.h" +#include "../../networkPacks/PacksForClient.h" + +VCMI_LIB_NAMESPACE_BEGIN + +ViewMechanics::ViewMechanics(const CSpell * s): + AdventureSpellMechanics(s) +{ +} + +ESpellCastResult ViewMechanics::applyAdventureEffects(SpellCastEnvironment * env, const AdventureSpellCastParameters & parameters) const +{ + ShowWorldViewEx pack; + + pack.player = parameters.caster->getCasterOwner(); + + const auto spellLevel = parameters.caster->getSpellSchoolLevel(owner); + + const auto & fowMap = env->getCb()->getPlayerTeam(parameters.caster->getCasterOwner())->fogOfWarMap; + + for(const auto & obj : env->getMap()->getObjects()) + { + //deleted object remain as empty pointer + if(obj && filterObject(obj, spellLevel)) + { + ObjectPosInfo posInfo(obj); + + if(fowMap[posInfo.pos.z][posInfo.pos.x][posInfo.pos.y] == 0) + pack.objectPositions.push_back(posInfo); + } + } + pack.showTerrain = showTerrain(spellLevel); + + env->apply(pack); + + return ESpellCastResult::OK; +} + +///ViewAirMechanics +ViewAirMechanics::ViewAirMechanics(const CSpell * s) + : ViewMechanics(s) +{ +} + +bool ViewAirMechanics::filterObject(const CGObjectInstance * obj, const int32_t spellLevel) const +{ + return (obj->ID == Obj::ARTIFACT) || (spellLevel > 1 && obj->ID == Obj::HERO) || (spellLevel > 2 && obj->ID == Obj::TOWN); +} + +bool ViewAirMechanics::showTerrain(const int32_t spellLevel) const +{ + return false; +} + +///ViewEarthMechanics +ViewEarthMechanics::ViewEarthMechanics(const CSpell * s) + : ViewMechanics(s) +{ +} + +bool ViewEarthMechanics::filterObject(const CGObjectInstance * obj, const int32_t spellLevel) const +{ + return (obj->ID == Obj::RESOURCE) || (spellLevel > 1 && obj->ID == Obj::MINE); +} + +bool ViewEarthMechanics::showTerrain(const int32_t spellLevel) const +{ + return spellLevel > 2; +} + +VCMI_LIB_NAMESPACE_END diff --git a/lib/spells/adventure/ViewWorldMechanics.h b/lib/spells/adventure/ViewWorldMechanics.h new file mode 100644 index 000000000..2e82babf2 --- /dev/null +++ b/lib/spells/adventure/ViewWorldMechanics.h @@ -0,0 +1,48 @@ +/* + * ViewWorldMechanics.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 "AdventureSpellMechanics.h" + +VCMI_LIB_NAMESPACE_BEGIN + +class ViewMechanics : public AdventureSpellMechanics +{ +public: + ViewMechanics(const CSpell * s); + +protected: + ESpellCastResult applyAdventureEffects(SpellCastEnvironment * env, const AdventureSpellCastParameters & parameters) const override; + virtual bool filterObject(const CGObjectInstance * obj, const int32_t spellLevel) const = 0; + virtual bool showTerrain(const int32_t spellLevel) const = 0; +}; + +class ViewAirMechanics final : public ViewMechanics +{ +public: + ViewAirMechanics(const CSpell * s); + +protected: + bool filterObject(const CGObjectInstance * obj, const int32_t spellLevel) const override; + bool showTerrain(const int32_t spellLevel) const override; +}; + +class ViewEarthMechanics final : public ViewMechanics +{ +public: + ViewEarthMechanics(const CSpell * s); + +protected: + bool filterObject(const CGObjectInstance * obj, const int32_t spellLevel) const override; + bool showTerrain(const int32_t spellLevel) const override; +}; + +VCMI_LIB_NAMESPACE_END diff --git a/lib/texts/MetaString.cpp b/lib/texts/MetaString.cpp index e05af1cc0..60a3375b4 100644 --- a/lib/texts/MetaString.cpp +++ b/lib/texts/MetaString.cpp @@ -11,7 +11,6 @@ #include "MetaString.h" #include "CCreatureHandler.h" -#include "CCreatureSet.h" #include "entities/artifact/CArtifact.h" #include "entities/faction/CFaction.h" #include "entities/hero/CHero.h" @@ -19,6 +18,7 @@ #include "CSkillHandler.h" #include "GameConstants.h" #include "GameLibrary.h" +#include "mapObjects/army/CStackBasicDescriptor.h" #include "mapObjectConstructors/CObjectClassesHandler.h" #include "spells/CSpellHandler.h" #include "serializer/JsonSerializeFormat.h" diff --git a/mapeditor/inspector/armywidget.cpp b/mapeditor/inspector/armywidget.cpp index 174109ead..c44402b2f 100644 --- a/mapeditor/inspector/armywidget.cpp +++ b/mapeditor/inspector/armywidget.cpp @@ -14,6 +14,7 @@ #include "CCreatureHandler.h" #include "../../lib/GameLibrary.h" +#include "../../lib/mapObjects/army/CStackInstance.h" ArmyWidget::ArmyWidget(CArmedInstance & a, QWidget *parent) : QDialog(parent), diff --git a/mapeditor/inspector/armywidget.h b/mapeditor/inspector/armywidget.h index f127f25c8..c3d316b60 100644 --- a/mapeditor/inspector/armywidget.h +++ b/mapeditor/inspector/armywidget.h @@ -12,7 +12,7 @@ #include "../StdInc.h" #include #include "baseinspectoritemdelegate.h" -#include "../lib/mapObjects/CArmedInstance.h" +#include "../lib/mapObjects/army/CArmedInstance.h" const int TOTAL_SLOTS = 7; diff --git a/mapeditor/inspector/towneventswidget.cpp b/mapeditor/inspector/towneventswidget.cpp index 823c0525e..c6e303934 100644 --- a/mapeditor/inspector/towneventswidget.cpp +++ b/mapeditor/inspector/towneventswidget.cpp @@ -16,6 +16,7 @@ #include "mapsettings/eventsettings.h" #include "../../lib/constants/NumericConstants.h" #include "../../lib/constants/StringConstants.h" +#include "../../lib/mapping/CCastleEvent.h" TownEventsWidget::TownEventsWidget(CGTownInstance & town, QWidget * parent) : QDialog(parent), diff --git a/mapeditor/inspector/towneventswidget.h b/mapeditor/inspector/towneventswidget.h index 54e7e17b3..3bc1913a3 100644 --- a/mapeditor/inspector/towneventswidget.h +++ b/mapeditor/inspector/towneventswidget.h @@ -12,7 +12,6 @@ #include "../StdInc.h" #include #include "baseinspectoritemdelegate.h" -#include "../lib/mapping/CMapDefines.h" #include "../lib/mapObjects/CGTownInstance.h" #include "../mapcontroller.h" diff --git a/mapeditor/mapsettings/eventsettings.cpp b/mapeditor/mapsettings/eventsettings.cpp index a74cf5a21..7eae1b493 100644 --- a/mapeditor/mapsettings/eventsettings.cpp +++ b/mapeditor/mapsettings/eventsettings.cpp @@ -12,7 +12,6 @@ #include "timedevent.h" #include "ui_eventsettings.h" #include "../mapcontroller.h" -#include "../../lib/mapping/CMapDefines.h" #include "../../lib/constants/NumericConstants.h" #include "../../lib/constants/StringConstants.h" diff --git a/scripting/lua/api/StackInstance.h b/scripting/lua/api/StackInstance.h index 47e0f5ea1..0b9c12ea6 100644 --- a/scripting/lua/api/StackInstance.h +++ b/scripting/lua/api/StackInstance.h @@ -14,7 +14,7 @@ #include "../LuaWrapper.h" -#include "../../../lib/CCreatureSet.h" +#include "../../../lib/mapObjects/army/CStackInstance.h" VCMI_LIB_NAMESPACE_BEGIN diff --git a/server/CGameHandler.cpp b/server/CGameHandler.cpp index e96e826c5..4313222e0 100644 --- a/server/CGameHandler.cpp +++ b/server/CGameHandler.cpp @@ -25,7 +25,6 @@ #include "../lib/CConfigHandler.h" #include "../lib/CCreatureHandler.h" -#include "../lib/CCreatureSet.h" #include "../lib/CPlayerState.h" #include "../lib/CSoundBase.h" #include "../lib/GameConstants.h" diff --git a/server/CVCMIServer.cpp b/server/CVCMIServer.cpp index 802b5007b..4567ea2ce 100644 --- a/server/CVCMIServer.cpp +++ b/server/CVCMIServer.cpp @@ -16,6 +16,7 @@ #include "processors/PlayerMessageProcessor.h" #include "../lib/CThreadHelper.h" +#include "../lib/GameLibrary.h" #include "../lib/campaign/CampaignState.h" #include "../lib/entities/hero/CHeroHandler.h" #include "../lib/entities/hero/CHeroClass.h" diff --git a/server/NetPacksLobbyServer.cpp b/server/NetPacksLobbyServer.cpp index c1028aa5f..dafa74827 100644 --- a/server/NetPacksLobbyServer.cpp +++ b/server/NetPacksLobbyServer.cpp @@ -14,6 +14,7 @@ #include "CGameHandler.h" #include "../lib/StartInfo.h" +#include "../lib/GameLibrary.h" #include "../lib/CRandomGenerator.h" #include "../lib/campaign/CampaignState.h" diff --git a/server/battles/BattleResultProcessor.cpp b/server/battles/BattleResultProcessor.cpp index 16e638cca..e99b86858 100644 --- a/server/battles/BattleResultProcessor.cpp +++ b/server/battles/BattleResultProcessor.cpp @@ -17,6 +17,7 @@ #include "../queries/QueriesProcessor.h" #include "../queries/BattleQueries.h" +#include "../../lib/GameLibrary.h" #include "../../lib/CStack.h" #include "../../lib/CPlayerState.h" #include "../../lib/IGameSettings.h" diff --git a/server/processors/HeroPoolProcessor.cpp b/server/processors/HeroPoolProcessor.cpp index bdab46efb..04a6fa107 100644 --- a/server/processors/HeroPoolProcessor.cpp +++ b/server/processors/HeroPoolProcessor.cpp @@ -21,6 +21,7 @@ #include "../../lib/entities/hero/CHero.h" #include "../../lib/mapObjects/CGTownInstance.h" #include "../../lib/mapObjects/CGHeroInstance.h" +#include "../../lib/mapping/TerrainTile.h" #include "../../lib/networkPacks/PacksForClient.h" #include "../../lib/gameState/CGameState.h" #include "../../lib/gameState/TavernHeroesPool.h" diff --git a/server/processors/NewTurnProcessor.cpp b/server/processors/NewTurnProcessor.cpp index 33bb8eb03..846884fe0 100644 --- a/server/processors/NewTurnProcessor.cpp +++ b/server/processors/NewTurnProcessor.cpp @@ -27,6 +27,7 @@ #include "../../lib/mapObjects/CGTownInstance.h" #include "../../lib/mapObjects/IOwnableObject.h" #include "../../lib/mapping/CMap.h" +#include "../../lib/mapping/CCastleEvent.h" #include "../../lib/networkPacks/PacksForClient.h" #include "../../lib/networkPacks/StackLocation.h" #include "../../lib/pathfinder/TurnInfo.h" diff --git a/server/queries/MapQueries.h b/server/queries/MapQueries.h index f202e11b7..77108413c 100644 --- a/server/queries/MapQueries.h +++ b/server/queries/MapQueries.h @@ -16,6 +16,7 @@ VCMI_LIB_NAMESPACE_BEGIN class CGHeroInstance; class CGObjectInstance; class IObjectInterface; +class CArmedInstance; VCMI_LIB_NAMESPACE_END //Created when player starts turn or when player puts game on [ause