1
0
mirror of https://github.com/vcmi/vcmi.git synced 2025-11-23 22:37:55 +02:00
Files
vcmi/lib/mapObjects/army/CStackInstance.cpp
2025-07-09 17:52:51 +03:00

359 lines
8.2 KiB
C++

/*
* 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(static_cast<int>(quantity))
{
if(settings["gameTweaks"]["numericCreaturesQuantities"].Bool())
return CCreature::getQuantityRangeStringForId(quantity);
return LIBRARY->generaltexth->arraytxt[174 + static_cast<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