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:
@ -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"
|
||||
|
@ -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"
|
||||
|
@ -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"
|
||||
|
@ -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"
|
||||
|
@ -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"
|
||||
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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>
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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"
|
||||
|
@ -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"
|
||||
|
@ -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"
|
||||
|
@ -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"
|
||||
|
||||
|
@ -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"
|
||||
|
@ -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"
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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"
|
||||
|
@ -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()
|
||||
|
@ -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
|
||||
|
@ -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"
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
@ -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
|
||||
|
@ -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
|
||||
|
||||
|
@ -12,7 +12,6 @@
|
||||
#include "BattleStateInfoForRetreat.h"
|
||||
#include "Unit.h"
|
||||
#include "CBattleInfoCallback.h"
|
||||
#include "../CCreatureSet.h"
|
||||
#include "../mapObjects/CGHeroInstance.h"
|
||||
|
||||
VCMI_LIB_NAMESPACE_BEGIN
|
||||
|
@ -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"
|
||||
|
@ -16,7 +16,6 @@
|
||||
|
||||
#include "../CBonusTypeHandler.h"
|
||||
#include "../CCreatureHandler.h"
|
||||
#include "../CCreatureSet.h"
|
||||
#include "../CSkillHandler.h"
|
||||
#include "../TerrainHandler.h"
|
||||
#include "../GameLibrary.h"
|
||||
|
@ -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"
|
||||
|
@ -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"
|
||||
|
@ -9,6 +9,8 @@
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "../texts/MetaString.h"
|
||||
|
||||
VCMI_LIB_NAMESPACE_BEGIN
|
||||
|
||||
class DLL_LINKAGE EVictoryLossCheckResult
|
||||
|
@ -9,7 +9,7 @@
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "CCreatureSet.h"
|
||||
#include "../mapObjects/army/CStackBasicDescriptor.h"
|
||||
|
||||
VCMI_LIB_NAMESPACE_BEGIN
|
||||
|
||||
|
@ -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"
|
||||
|
@ -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
|
||||
|
||||
|
@ -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"
|
||||
|
@ -9,7 +9,7 @@
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "CArmedInstance.h"
|
||||
#include "army/CArmedInstance.h"
|
||||
#include "../ResourceSet.h"
|
||||
|
||||
VCMI_LIB_NAMESPACE_BEGIN
|
||||
|
@ -10,9 +10,10 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CArmedInstance.h"
|
||||
#include "IOwnableObject.h"
|
||||
|
||||
#include "army/CArmedInstance.h"
|
||||
|
||||
VCMI_LIB_NAMESPACE_BEGIN
|
||||
|
||||
class CGDwelling;
|
||||
|
@ -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"
|
||||
|
||||
|
@ -9,7 +9,7 @@
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "CArmedInstance.h"
|
||||
#include "army/CArmedInstance.h"
|
||||
|
||||
VCMI_LIB_NAMESPACE_BEGIN
|
||||
|
||||
|
@ -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"
|
||||
|
@ -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;
|
||||
|
||||
|
@ -9,7 +9,8 @@
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "CArmedInstance.h"
|
||||
#include "army/CArmedInstance.h"
|
||||
|
||||
#include "../rewardable/Interface.h"
|
||||
|
||||
VCMI_LIB_NAMESPACE_BEGIN
|
||||
|
@ -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
|
||||
|
@ -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"
|
||||
|
@ -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
|
||||
|
@ -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;
|
@ -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();
|
80
lib/mapObjects/army/CCommanderInstance.cpp
Normal file
80
lib/mapObjects/army/CCommanderInstance.cpp
Normal 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
|
55
lib/mapObjects/army/CCommanderInstance.h
Normal file
55
lib/mapObjects/army/CCommanderInstance.h
Normal 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
|
@ -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
|
160
lib/mapObjects/army/CCreatureSet.h
Normal file
160
lib/mapObjects/army/CCreatureSet.h
Normal 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
|
54
lib/mapObjects/army/CSimpleArmy.h
Normal file
54
lib/mapObjects/army/CSimpleArmy.h
Normal 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
|
91
lib/mapObjects/army/CStackBasicDescriptor.cpp
Normal file
91
lib/mapObjects/army/CStackBasicDescriptor.cpp
Normal 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
|
65
lib/mapObjects/army/CStackBasicDescriptor.h
Normal file
65
lib/mapObjects/army/CStackBasicDescriptor.h
Normal 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
|
358
lib/mapObjects/army/CStackInstance.cpp
Normal file
358
lib/mapObjects/army/CStackInstance.cpp
Normal 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
|
134
lib/mapObjects/army/CStackInstance.h
Normal file
134
lib/mapObjects/army/CStackInstance.h
Normal 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
|
37
lib/mapping/CCastleEvent.h
Normal file
37
lib/mapping/CCastleEvent.h
Normal 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
|
@ -12,6 +12,7 @@
|
||||
|
||||
#include "CMapEditManager.h"
|
||||
#include "CMapOperation.h"
|
||||
#include "CCastleEvent.h"
|
||||
|
||||
#include "../CCreatureHandler.h"
|
||||
#include "../CSkillHandler.h"
|
||||
|
@ -10,8 +10,9 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CMapDefines.h"
|
||||
#include "CMapEvent.h"
|
||||
#include "CMapHeader.h"
|
||||
#include "TerrainTile.h"
|
||||
|
||||
#include "../mapObjects/CGObjectInstance.h"
|
||||
#include "../callback/GameCallbackHolder.h"
|
||||
|
59
lib/mapping/CMapEvent.h
Normal file
59
lib/mapping/CMapEvent.h
Normal 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
|
@ -11,6 +11,7 @@
|
||||
#include "StdInc.h"
|
||||
#include "MapFormatH3M.h"
|
||||
|
||||
#include "CCastleEvent.h"
|
||||
#include "CMap.h"
|
||||
#include "MapReaderH3M.h"
|
||||
#include "MapFormatSettings.h"
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* CMapDefines.h, part of VCMI engine
|
||||
* TerrainTile.h, part of VCMI engine
|
||||
*
|
||||
* Authors: listed in file AUTHORS in main folder
|
||||
*
|
||||
@ -10,8 +10,6 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../ResourceSet.h"
|
||||
#include "../texts/MetaString.h"
|
||||
#include "../GameLibrary.h"
|
||||
#include "../TerrainHandler.h"
|
||||
#include "../mapObjects/CGObjectInstance.h"
|
||||
@ -22,67 +20,6 @@ class TerrainType;
|
||||
class RiverType;
|
||||
class RoadType;
|
||||
class CGObjectInstance;
|
||||
class CGTownInstance;
|
||||
class JsonSerializeFormat;
|
||||
|
||||
/// The map event is an event which e.g. gives or takes resources of a specific
|
||||
/// amount to/from players and can appear regularly or once a time.
|
||||
class DLL_LINKAGE CMapEvent
|
||||
{
|
||||
public:
|
||||
CMapEvent();
|
||||
virtual ~CMapEvent() = default;
|
||||
|
||||
bool occursToday(int currentDay) const;
|
||||
bool affectsPlayer(PlayerColor player, bool isHuman) const;
|
||||
|
||||
std::string name;
|
||||
MetaString message;
|
||||
TResources resources;
|
||||
std::set<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);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
};
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -14,7 +14,7 @@
|
||||
|
||||
#include "../callback/IGameInfoCallback.h"
|
||||
#include "../mapObjects/CGHeroInstance.h"
|
||||
#include "../mapping/CMapDefines.h"
|
||||
#include "../mapping/TerrainTile.h"
|
||||
|
||||
VCMI_LIB_NAMESPACE_BEGIN
|
||||
|
||||
|
@ -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"
|
||||
|
||||
|
@ -17,7 +17,7 @@
|
||||
|
||||
#include "../mapObjects/CGHeroInstance.h"
|
||||
#include "../mapObjects/MiscObjects.h"
|
||||
#include "../mapping/CMapDefines.h"
|
||||
#include "../mapping/TerrainTile.h"
|
||||
|
||||
VCMI_LIB_NAMESPACE_BEGIN
|
||||
|
||||
|
@ -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>
|
||||
|
||||
|
@ -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"
|
||||
|
||||
|
@ -16,6 +16,8 @@
|
||||
VCMI_LIB_NAMESPACE_BEGIN
|
||||
|
||||
class IObjectInterface;
|
||||
class IGameEventCallback;
|
||||
class CArmedInstance;
|
||||
|
||||
namespace Rewardable
|
||||
{
|
||||
|
@ -12,6 +12,7 @@
|
||||
|
||||
#include "../GameConstants.h"
|
||||
#include "../ResourceSet.h"
|
||||
#include "../mapObjects/army/CStackBasicDescriptor.h"
|
||||
#include "../serializer/Serializeable.h"
|
||||
|
||||
VCMI_LIB_NAMESPACE_BEGIN
|
||||
|
@ -12,7 +12,6 @@
|
||||
|
||||
#include "../ResourceSet.h"
|
||||
#include "../bonuses/Bonus.h"
|
||||
#include "../CCreatureSet.h"
|
||||
#include "../networkPacks/Component.h"
|
||||
|
||||
VCMI_LIB_NAMESPACE_BEGIN
|
||||
|
@ -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"
|
||||
|
@ -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
|
||||
|
@ -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
|
@ -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
|
@ -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"
|
||||
|
144
lib/spells/adventure/AdventureSpellMechanics.cpp
Normal file
144
lib/spells/adventure/AdventureSpellMechanics.cpp
Normal 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
|
46
lib/spells/adventure/AdventureSpellMechanics.h
Normal file
46
lib/spells/adventure/AdventureSpellMechanics.h
Normal 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
|
166
lib/spells/adventure/DimensionDoorMechanics.cpp
Normal file
166
lib/spells/adventure/DimensionDoorMechanics.cpp
Normal 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
|
30
lib/spells/adventure/DimensionDoorMechanics.h
Normal file
30
lib/spells/adventure/DimensionDoorMechanics.h
Normal 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
|
78
lib/spells/adventure/ScuttleBoatMechanics.cpp
Normal file
78
lib/spells/adventure/ScuttleBoatMechanics.cpp
Normal 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
|
28
lib/spells/adventure/ScuttleBoatMechanics.h
Normal file
28
lib/spells/adventure/ScuttleBoatMechanics.h
Normal 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
|
111
lib/spells/adventure/SummonBoatMechanics.cpp
Normal file
111
lib/spells/adventure/SummonBoatMechanics.cpp
Normal 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
|
28
lib/spells/adventure/SummonBoatMechanics.h
Normal file
28
lib/spells/adventure/SummonBoatMechanics.h
Normal 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
|
299
lib/spells/adventure/TownPortalMechanics.cpp
Normal file
299
lib/spells/adventure/TownPortalMechanics.cpp
Normal 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
|
35
lib/spells/adventure/TownPortalMechanics.h
Normal file
35
lib/spells/adventure/TownPortalMechanics.h
Normal 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
|
90
lib/spells/adventure/ViewWorldMechanics.cpp
Normal file
90
lib/spells/adventure/ViewWorldMechanics.cpp
Normal 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
|
48
lib/spells/adventure/ViewWorldMechanics.h
Normal file
48
lib/spells/adventure/ViewWorldMechanics.h
Normal 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
|
@ -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"
|
||||
|
@ -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),
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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),
|
||||
|
@ -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"
|
||||
|
||||
|
@ -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"
|
||||
|
||||
|
@ -14,7 +14,7 @@
|
||||
|
||||
#include "../LuaWrapper.h"
|
||||
|
||||
#include "../../../lib/CCreatureSet.h"
|
||||
#include "../../../lib/mapObjects/army/CStackInstance.h"
|
||||
|
||||
VCMI_LIB_NAMESPACE_BEGIN
|
||||
|
||||
|
@ -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"
|
||||
|
@ -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"
|
||||
|
@ -14,6 +14,7 @@
|
||||
#include "CGameHandler.h"
|
||||
|
||||
#include "../lib/StartInfo.h"
|
||||
#include "../lib/GameLibrary.h"
|
||||
|
||||
#include "../lib/CRandomGenerator.h"
|
||||
#include "../lib/campaign/CampaignState.h"
|
||||
|
@ -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"
|
||||
|
@ -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"
|
||||
|
@ -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
Reference in New Issue
Block a user