|
@@ -10,22 +10,11 @@
|
|
|
#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 <vcmi/FactionService.h>
|
|
|
-#include <vcmi/Faction.h>
|
|
|
+#include "../CGHeroInstance.h"
|
|
|
+
|
|
|
+#include "../../CConfigHandler.h"
|
|
|
+#include "../../texts/CGeneralTextHandler.h"
|
|
|
+#include "../../serializer/JsonSerializeFormat.h"
|
|
|
|
|
|
VCMI_LIB_NAMESPACE_BEGIN
|
|
|
|
|
@@ -707,483 +696,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
|