1
0
mirror of https://github.com/vcmi/vcmi.git synced 2025-07-15 01:24:45 +02:00

Merge pull request #5896 from IvanSavenko/code_reorganize

Split large source files into smaller files per 1 class
This commit is contained in:
Ivan Savenko
2025-07-08 10:47:13 +03:00
committed by GitHub
101 changed files with 2406 additions and 1974 deletions

View File

@ -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"

View File

@ -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"

View File

@ -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"

View File

@ -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"

View File

@ -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"

View File

@ -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;

View File

@ -10,7 +10,8 @@
#include "StdInc.h"
#include "AIPathfinder.h"
#include "AIPathfinderConfig.h"
#include "../../../lib/mapping/CMapDefines.h"
#include "../../../lib/mapping/TerrainTile.h"
#include <tbb/task_group.h>

View File

@ -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)

View File

@ -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"

View File

@ -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"

View File

@ -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"

View File

@ -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"

View File

@ -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"

View File

@ -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"

View File

@ -16,7 +16,6 @@
#include "../lib/mapObjects/CGHeroInstance.h"
#include "../lib/networkPacks/ArtifactLocation.h"
#include "../lib/CRandomGenerator.h"
#include "../lib/CCreatureSet.h"
std::vector<Component> UIHelper::getArtifactsComponents(const CArtifactSet & artSet, const std::vector<MoveArtifactInfo> & movedPack)
{

View File

@ -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"

View File

@ -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()

View File

@ -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

View File

@ -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"

View File

@ -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

View File

@ -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/Entity.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 <typename Handler> 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<RandomStackInfo> 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 <typename Handler> void serialize(Handler &h)
{
h & static_cast<CBonusSystemNode&>(*this);
h & static_cast<CStackBasicDescriptor&>(*this);
h & static_cast<CArtifactSet&>(*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<CGObjectInstance> 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>& bonus) const override; // how would bonus description look for this particular type of node
ImagePath bonusToGraphics(const std::shared_ptr<Bonus> & 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 <ui8> secondarySkills; //ID -> level
std::set <ui8> specialSkills;
//std::vector <CArtifactInstance *> 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 <typename Handler> void serialize(Handler &h)
{
h & static_cast<CStackInstance&>(*this);
h & alive;
h & level;
h & name;
h & secondarySkills;
h & specialSkills;
}
};
using TSlots = std::map<SlotID, std::unique_ptr<CStackInstance>>;
using TSimpleSlots = std::map<SlotID, std::pair<CreatureID, TQuantity>>;
using TPairCreatureSlot = std::pair<const CCreature *, SlotID>;
using TMapCreatureSlot = std::map<const CCreature *, SlotID>;
struct DLL_LINKAGE CreatureSlotComparer
{
bool operator()(const TPairCreatureSlot & lhs, const TPairCreatureSlot & rhs);
};
using TCreatureQueue = std::priority_queue<TPairCreatureSlot, std::vector<TPairCreatureSlot>, 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 <typename Handler> void serialize(Handler &h)
{
h & army;
}
};
namespace NArmyFormation
{
static const std::vector<std::string> 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<CStackInstance> 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<CStackInstance> stack); //adds new stack to the army, slot must be empty
void setStackCount(const SlotID & slot, TQuantity count); //stack must exist!
std::unique_ptr<CStackInstance> 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<CStackInstance> 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<CStackInstance> 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<SlotID> 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<SlotID> getFreeSlots(ui32 slotsAmount = GameConstants::ARMY_SIZE) const;
std::queue<SlotID> getFreeSlotsQueue(ui32 slotsAmount = GameConstants::ARMY_SIZE) const;
TMapCreatureSlot getCreatureMap() const;
TCreatureQueue getCreatureQueue(const SlotID & exclude) const;
bool mergeableStacks(std::pair<SlotID, SlotID> & 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<CStackBasicDescriptor> & units, bool requireLastStack = true) const;
template <typename Handler> void serialize(Handler &h)
{
h & stacks;
h & formation;
}
void serializeJson(JsonSerializeFormat & handler, const std::string & armyFieldName, const std::optional<int> fixedSize = std::nullopt);
operator bool() const
{
return !stacks.empty();
}
};
VCMI_LIB_NAMESPACE_END

View File

@ -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

View File

@ -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

View File

@ -12,7 +12,6 @@
#include "BattleStateInfoForRetreat.h"
#include "Unit.h"
#include "CBattleInfoCallback.h"
#include "../CCreatureSet.h"
#include "../mapObjects/CGHeroInstance.h"
VCMI_LIB_NAMESPACE_BEGIN

View File

@ -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"

View File

@ -16,7 +16,6 @@
#include "../CBonusTypeHandler.h"
#include "../CCreatureHandler.h"
#include "../CCreatureSet.h"
#include "../CSkillHandler.h"
#include "../TerrainHandler.h"
#include "../GameLibrary.h"

View File

@ -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"

View File

@ -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"

View File

@ -9,6 +9,8 @@
*/
#pragma once
#include "../texts/MetaString.h"
VCMI_LIB_NAMESPACE_BEGIN
class DLL_LINKAGE EVictoryLossCheckResult

View File

@ -9,7 +9,7 @@
*/
#pragma once
#include "CCreatureSet.h"
#include "../mapObjects/army/CStackBasicDescriptor.h"
VCMI_LIB_NAMESPACE_BEGIN

View File

@ -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"

View File

@ -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

View File

@ -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"

View File

@ -9,7 +9,7 @@
*/
#pragma once
#include "CArmedInstance.h"
#include "army/CArmedInstance.h"
#include "../ResourceSet.h"
VCMI_LIB_NAMESPACE_BEGIN

View File

@ -10,9 +10,10 @@
#pragma once
#include "CArmedInstance.h"
#include "IOwnableObject.h"
#include "army/CArmedInstance.h"
VCMI_LIB_NAMESPACE_BEGIN
class CGDwelling;

View File

@ -11,9 +11,11 @@
#include <vcmi/spells/Caster.h>
#include "CArmedInstance.h"
#include "IOwnableObject.h"
#include "army/CArmedInstance.h"
#include "army/CCommanderInstance.h"
#include "../bonuses/BonusCache.h"
#include "../entities/hero/EHeroGender.h"

View File

@ -9,7 +9,7 @@
*/
#pragma once
#include "CArmedInstance.h"
#include "army/CArmedInstance.h"
VCMI_LIB_NAMESPACE_BEGIN

View File

@ -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"

View File

@ -167,7 +167,7 @@ public:
void removeAllBuildings();
std::set<BuildingID> getBuildings() const;
TResources getBuildingCost(const BuildingID & buildingID) const;
ResourceSet getBuildingCost(const BuildingID & buildingID) const;
ResourceSet dailyIncome() const override;
std::vector<CreatureID> providedCreatures() const override;

View File

@ -9,7 +9,8 @@
*/
#pragma once
#include "CArmedInstance.h"
#include "army/CArmedInstance.h"
#include "../rewardable/Interface.h"
VCMI_LIB_NAMESPACE_BEGIN

View File

@ -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

View File

@ -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"

View File

@ -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

View File

@ -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<Bonus>(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<si32>(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;

View File

@ -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/Entity.h>
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 <typename Handler> void serialize(Handler &h)
template<typename Handler>
void serialize(Handler & h)
{
h & static_cast<CGObjectInstance&>(*this);
h & static_cast<CBonusSystemNode&>(*this);
h & static_cast<CCreatureSet&>(*this);
h & static_cast<CGObjectInstance &>(*this);
h & static_cast<CBonusSystemNode &>(*this);
h & static_cast<CCreatureSet &>(*this);
if(!h.saving && h.loadingGamestate)
attachUnitsToArmy();

View File

@ -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

View File

@ -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<ui8> secondarySkills; //ID -> level
std::set<ui8> specialSkills;
//std::vector <CArtifactInstance *> 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<typename Handler>
void serialize(Handler & h)
{
h & static_cast<CStackInstance &>(*this);
h & alive;
h & level;
h & name;
h & secondarySkills;
h & specialSkills;
}
};
VCMI_LIB_NAMESPACE_END

View File

@ -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 <vcmi/FactionService.h>
#include <vcmi/Faction.h>
#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<slotsAmount; i++)
for(ui32 i = 0; i < slotsAmount; i++)
{
if(!vstd::contains(stacks, SlotID(i)))
{
@ -190,7 +177,7 @@ std::queue<SlotID> CCreatureSet::getFreeSlotsQueue(ui32 slotsAmount) const
{
std::queue<SlotID> 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<SlotID, SlotID> & 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<SlotID, SlotID> & 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<const CGHeroInstance *>(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<CStackInstance>
void CCreatureSet::joinStack(const SlotID & slot, std::unique_ptr<CStackInstance> 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<const CCreature*> cresToAdd;
std::set<const CCreature *> 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<CStackBasicDescriptor> & 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<int> 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<int>(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<int>(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>& 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> & 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<CArmedInstance&>(*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<ui64>(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

View File

@ -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<SlotID, std::unique_ptr<CStackInstance>>;
using TPairCreatureSlot = std::pair<const CCreature *, SlotID>;
using TMapCreatureSlot = std::map<const CCreature *, SlotID>;
struct DLL_LINKAGE CreatureSlotComparer
{
bool operator()(const TPairCreatureSlot & lhs, const TPairCreatureSlot & rhs);
};
using TCreatureQueue = std::priority_queue<TPairCreatureSlot, std::vector<TPairCreatureSlot>, CreatureSlotComparer>;
namespace NArmyFormation
{
static const std::vector<std::string> 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<CStackInstance> 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<CStackInstance> 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<CStackInstance> 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<CStackInstance> 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<CStackInstance> 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<SlotID> 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<SlotID> getFreeSlots(ui32 slotsAmount = GameConstants::ARMY_SIZE) const;
std::queue<SlotID> getFreeSlotsQueue(ui32 slotsAmount = GameConstants::ARMY_SIZE) const;
TMapCreatureSlot getCreatureMap() const;
TCreatureQueue getCreatureQueue(const SlotID & exclude) const;
bool mergeableStacks(std::pair<SlotID, SlotID> & 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<CStackBasicDescriptor> & units, bool requireLastStack = true) const;
template<typename Handler>
void serialize(Handler & h)
{
h & stacks;
h & formation;
}
void serializeJson(JsonSerializeFormat & handler, const std::string & armyFieldName, const std::optional<int> fixedSize = std::nullopt);
operator bool() const
{
return !stacks.empty();
}
};
VCMI_LIB_NAMESPACE_END

View File

@ -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<SlotID, std::pair<CreatureID, TQuantity>>;
//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<typename Handler>
void serialize(Handler & h)
{
h & army;
}
};
VCMI_LIB_NAMESPACE_END

View File

@ -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

View File

@ -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<typename Handler>
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

View File

@ -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<int>(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<int>(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> & 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> & 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<CArmedInstance &>(*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<ui64>(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

View File

@ -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<RandomStackInfo> 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<typename Handler>
void serialize(Handler & h)
{
h & static_cast<CBonusSystemNode &>(*this);
h & static_cast<CStackBasicDescriptor &>(*this);
h & static_cast<CArtifactSet &>(*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<CGObjectInstance> 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> & bonus) const override; // how would bonus description look for this particular type of node
ImagePath bonusToGraphics(const std::shared_ptr<Bonus> & 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

View File

@ -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<BuildingID> buildings;
std::vector<si32> creatures;
template<typename Handler>
void serialize(Handler & h)
{
h & static_cast<CMapEvent &>(*this);
h & buildings;
h & creatures;
}
void serializeJson(JsonSerializeFormat & handler) override;
};
VCMI_LIB_NAMESPACE_END

View File

@ -12,6 +12,7 @@
#include "CMapEditManager.h"
#include "CMapOperation.h"
#include "CCastleEvent.h"
#include "../CCreatureHandler.h"
#include "../CSkillHandler.h"

View File

@ -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"

59
lib/mapping/CMapEvent.h Normal file
View File

@ -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<PlayerColor> 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<ObjectInstanceID> deletedObjectsInstances;
template<typename Handler>
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

View File

@ -11,6 +11,7 @@
#include "StdInc.h"
#include "MapFormatH3M.h"
#include "CCastleEvent.h"
#include "CMap.h"
#include "MapReaderH3M.h"
#include "MapFormatSettings.h"

View File

@ -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<PlayerColor> 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<ObjectInstanceID> deletedObjectsInstances;
template <typename Handler>
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<BuildingID> buildings;
std::vector<si32> creatures;
template <typename Handler>
void serialize(Handler & h)
{
h & static_cast<CMapEvent &>(*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<ObjectInstanceID> visitableObjects;
std::vector<ObjectInstanceID> blockingObjects;
template <typename Handler>
template<typename Handler>
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<std::shared_ptr<CGObjectInstance>> 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);
}
}
};

View File

@ -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;

View File

@ -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;

View File

@ -14,7 +14,7 @@
#include "../callback/IGameInfoCallback.h"
#include "../mapObjects/CGHeroInstance.h"
#include "../mapping/CMapDefines.h"
#include "../mapping/TerrainTile.h"
VCMI_LIB_NAMESPACE_BEGIN

View File

@ -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"

View File

@ -17,7 +17,7 @@
#include "../mapObjects/CGHeroInstance.h"
#include "../mapObjects/MiscObjects.h"
#include "../mapping/CMapDefines.h"
#include "../mapping/TerrainTile.h"
VCMI_LIB_NAMESPACE_BEGIN

View File

@ -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 <vstd/RNG.h>

View File

@ -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"

View File

@ -16,6 +16,8 @@
VCMI_LIB_NAMESPACE_BEGIN
class IObjectInterface;
class IGameEventCallback;
class CArmedInstance;
namespace Rewardable
{

View File

@ -12,6 +12,7 @@
#include "../GameConstants.h"
#include "../ResourceSet.h"
#include "../mapObjects/army/CStackBasicDescriptor.h"
#include "../serializer/Serializeable.h"
VCMI_LIB_NAMESPACE_BEGIN

View File

@ -12,7 +12,6 @@
#include "../ResourceSet.h"
#include "../bonuses/Bonus.h"
#include "../CCreatureSet.h"
#include "../networkPacks/Component.h"
VCMI_LIB_NAMESPACE_BEGIN

View File

@ -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"

View File

@ -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

View File

@ -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 <vstd/RNG.h>
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<const CGHeroInstance *>(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<Bonus> 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<CGBoat>())
{
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<int>(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 <const CGTownInstance*> pool = getPossibleTowns(env, parameters);
destination = findNearestTown(env, parameters, pool);
if(nullptr == destination)
return ESpellCastResult::ERROR;
if(static_cast<int>(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<const CGTownInstance*>(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<int>(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 <const CGTownInstance*> 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<const CGTownInstance*>(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<ui32>(0, parameters.caster->getHeroCaster()->movementPointsRemaining() - moveCost);
env->apply(smp);
}
}
ESpellCastResult TownPortalMechanics::beginCast(SpellCastEnvironment * env, const AdventureSpellCastParameters & parameters) const
{
std::vector<const CGTownInstance *> 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<int>(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<int32_t> 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<const CGTownInstance *>(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 <const CGTownInstance *> & 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 <const CGTownInstance*> TownPortalMechanics::getPossibleTowns(SpellCastEnvironment * env, const AdventureSpellCastParameters & parameters) const
{
std::vector <const CGTownInstance*> 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

View File

@ -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 <const CGTownInstance*> & pool) const;
int32_t movementCost(SpellCastEnvironment * env, const AdventureSpellCastParameters & parameters) const;
std::vector <const CGTownInstance*> 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

View File

@ -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"

View File

@ -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<const CGHeroInstance *>(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<Bonus> 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

View File

@ -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

View File

@ -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<int>(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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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<CGBoat>())
{
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

View File

@ -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

View File

@ -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<const CGTownInstance *> pool = getPossibleTowns(env, parameters);
destination = findNearestTown(env, parameters, pool);
if(nullptr == destination)
return ESpellCastResult::ERROR;
if(static_cast<int>(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<const CGTownInstance *>(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<int>(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<const CGTownInstance *> 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<const CGTownInstance *>(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<ui32>(0, parameters.caster->getHeroCaster()->movementPointsRemaining() - moveCost);
env->apply(smp);
}
}
ESpellCastResult TownPortalMechanics::beginCast(SpellCastEnvironment * env, const AdventureSpellCastParameters & parameters) const
{
std::vector<const CGTownInstance *> 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<int>(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<int32_t> 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<const CGTownInstance *>(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 <const CGTownInstance *> & 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<const CGTownInstance *> TownPortalMechanics::getPossibleTowns(SpellCastEnvironment * env, const AdventureSpellCastParameters & parameters) const
{
std::vector<const CGTownInstance *> 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

View File

@ -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<const CGTownInstance *> & pool) const;
int32_t movementCost(SpellCastEnvironment * env, const AdventureSpellCastParameters & parameters) const;
std::vector<const CGTownInstance *> getPossibleTowns(SpellCastEnvironment * env, const AdventureSpellCastParameters & parameters) const;
};
VCMI_LIB_NAMESPACE_END

View File

@ -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

View File

@ -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

View File

@ -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"

View File

@ -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),

View File

@ -12,7 +12,7 @@
#include "../StdInc.h"
#include <QDialog>
#include "baseinspectoritemdelegate.h"
#include "../lib/mapObjects/CArmedInstance.h"
#include "../lib/mapObjects/army/CArmedInstance.h"
const int TOTAL_SLOTS = 7;

View File

@ -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),

View File

@ -12,7 +12,6 @@
#include "../StdInc.h"
#include <QDialog>
#include "baseinspectoritemdelegate.h"
#include "../lib/mapping/CMapDefines.h"
#include "../lib/mapObjects/CGTownInstance.h"
#include "../mapcontroller.h"

View File

@ -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"

View File

@ -14,7 +14,7 @@
#include "../LuaWrapper.h"
#include "../../../lib/CCreatureSet.h"
#include "../../../lib/mapObjects/army/CStackInstance.h"
VCMI_LIB_NAMESPACE_BEGIN

View File

@ -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"

View File

@ -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"

View File

@ -14,6 +14,7 @@
#include "CGameHandler.h"
#include "../lib/StartInfo.h"
#include "../lib/GameLibrary.h"
#include "../lib/CRandomGenerator.h"
#include "../lib/campaign/CampaignState.h"

View File

@ -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"

View File

@ -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"

View File

@ -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"

Some files were not shown because too many files have changed in this diff Show More