1
0
mirror of https://github.com/vcmi/vcmi.git synced 2025-11-25 22:42:04 +02:00

(lib) Bonus subtype is now stored as metaidentifier that can store any

other identifier inside it
This commit is contained in:
Ivan Savenko
2023-10-05 16:13:52 +03:00
parent 454ba44ac5
commit 0a10fc30b8
54 changed files with 455 additions and 395 deletions

View File

@@ -95,7 +95,7 @@ ui32 CGHeroInstance::getTileMovementCost(const TerrainTile & dest, const Terrain
}
else if(ti->nativeTerrain != from.terType->getId() &&//the terrain is not native
ti->nativeTerrain != ETerrainId::ANY_TERRAIN && //no special creature bonus
!ti->hasBonusOfType(BonusType::NO_TERRAIN_PENALTY, from.terType->getIndex())) //no special movement bonus
!ti->hasBonusOfType(BonusType::NO_TERRAIN_PENALTY, TBonusSubtype(from.terType->getId()))) //no special movement bonus
{
ret = VLC->terrainTypeHandler->getById(from.terType->getId())->moveCost;
@@ -249,14 +249,14 @@ void CGHeroInstance::updateArmyMovementBonus(bool onLand, const TurnInfo * ti) c
lowestCreatureSpeed = realLowestSpeed;
//Let updaters run again
treeHasChanged();
ti->updateHeroBonuses(BonusType::MOVEMENT, Selector::subtype()(!!onLand));
ti->updateHeroBonuses(BonusType::MOVEMENT, Selector::subtype()(onLand ? BonusSubtypes::heroMovementLand : BonusSubtypes::heroMovementSea));
}
}
int CGHeroInstance::movementPointsLimitCached(bool onLand, const TurnInfo * ti) const
{
updateArmyMovementBonus(onLand, ti);
return ti->valOfBonuses(BonusType::MOVEMENT, !!onLand);
return ti->valOfBonuses(BonusType::MOVEMENT, onLand ? BonusSubtypes::heroMovementLand : BonusSubtypes::heroMovementSea);
}
CGHeroInstance::CGHeroInstance():
@@ -638,7 +638,7 @@ int32_t CGHeroInstance::getSpellSchoolLevel(const spells::Spell * spell, int32_t
spell->forEachSchool([&, this](const SpellSchool & cnf, bool & stop)
{
int32_t thisSchool = valOfBonuses(BonusType::MAGIC_SCHOOL_SKILL, cnf); //FIXME: Bonus shouldn't be additive (Witchking Artifacts : Crown of Skies)
int32_t thisSchool = valOfBonuses(BonusType::MAGIC_SCHOOL_SKILL, TBonusSubtype(cnf)); //FIXME: Bonus shouldn't be additive (Witchking Artifacts : Crown of Skies)
if(thisSchool > skill)
{
skill = thisSchool;
@@ -647,8 +647,8 @@ int32_t CGHeroInstance::getSpellSchoolLevel(const spells::Spell * spell, int32_t
}
});
vstd::amax(skill, valOfBonuses(BonusType::MAGIC_SCHOOL_SKILL, SpellSchool(ESpellSchool::ANY))); //any school bonus
vstd::amax(skill, valOfBonuses(BonusType::SPELL, spell->getIndex())); //given by artifact or other effect
vstd::amax(skill, valOfBonuses(BonusType::MAGIC_SCHOOL_SKILL, TBonusSubtype(SpellSchool::ANY))); //any school bonus
vstd::amax(skill, valOfBonuses(BonusType::SPELL, TBonusSubtype(spell->getId()))); //given by artifact or other effect
vstd::amax(skill, 0); //in case we don't know any school
vstd::amin(skill, 3);
@@ -660,28 +660,28 @@ int64_t CGHeroInstance::getSpellBonus(const spells::Spell * spell, int64_t base,
//applying sorcery secondary skill
if(spell->isMagical())
base = static_cast<int64_t>(base * (valOfBonuses(BonusType::SPELL_DAMAGE, SpellSchool(ESpellSchool::ANY))) / 100.0);
base = static_cast<int64_t>(base * (valOfBonuses(BonusType::SPELL_DAMAGE, TBonusSubtype(SpellSchool::ANY))) / 100.0);
base = static_cast<int64_t>(base * (100 + valOfBonuses(BonusType::SPECIFIC_SPELL_DAMAGE, spell->getIndex())) / 100.0);
base = static_cast<int64_t>(base * (100 + valOfBonuses(BonusType::SPECIFIC_SPELL_DAMAGE, TBonusSubtype(spell->getId()))) / 100.0);
int maxSchoolBonus = 0;
spell->forEachSchool([&maxSchoolBonus, this](const SpellSchool & cnf, bool & stop)
{
vstd::amax(maxSchoolBonus, valOfBonuses(BonusType::SPELL_DAMAGE, cnf));
vstd::amax(maxSchoolBonus, valOfBonuses(BonusType::SPELL_DAMAGE, TBonusSubtype(cnf)));
});
base = static_cast<int64_t>(base * (100 + maxSchoolBonus) / 100.0);
if(affectedStack && affectedStack->creatureLevel() > 0) //Hero specials like Solmyr, Deemer
base = static_cast<int64_t>(base * static_cast<double>(100 + valOfBonuses(BonusType::SPECIAL_SPELL_LEV, spell->getIndex()) / affectedStack->creatureLevel()) / 100.0);
base = static_cast<int64_t>(base * static_cast<double>(100 + valOfBonuses(BonusType::SPECIAL_SPELL_LEV, TBonusSubtype(spell->getId())) / affectedStack->creatureLevel()) / 100.0);
return base;
}
int64_t CGHeroInstance::getSpecificSpellBonus(const spells::Spell * spell, int64_t base) const
{
base = static_cast<int64_t>(base * (100 + valOfBonuses(BonusType::SPECIFIC_SPELL_DAMAGE, spell->getIndex())) / 100.0);
base = static_cast<int64_t>(base * (100 + valOfBonuses(BonusType::SPECIFIC_SPELL_DAMAGE, TBonusSubtype(spell->getId()))) / 100.0);
return base;
}
@@ -751,19 +751,19 @@ bool CGHeroInstance::canCastThisSpell(const spells::Spell * spell) const
const bool isAllowed = IObjectInterface::cb->isAllowed(0, spell->getIndex());
const bool inSpellBook = vstd::contains(spells, spell->getId()) && hasSpellbook();
const bool specificBonus = hasBonusOfType(BonusType::SPELL, spell->getIndex());
const bool specificBonus = hasBonusOfType(BonusType::SPELL, TBonusSubtype(spell->getId()));
bool schoolBonus = false;
spell->forEachSchool([this, &schoolBonus](const SpellSchool & cnf, bool & stop)
{
if(hasBonusOfType(BonusType::SPELLS_OF_SCHOOL, cnf))
if(hasBonusOfType(BonusType::SPELLS_OF_SCHOOL, TBonusSubtype(cnf)))
{
schoolBonus = stop = true;
}
});
const bool levelBonus = hasBonusOfType(BonusType::SPELLS_OF_LEVEL, spell->getLevel());
const bool levelBonus = hasBonusOfType(BonusType::SPELLS_OF_LEVEL, BonusSubtypes::spellLevel(spell->getLevel()));
if(spell->isSpecial())
{
@@ -845,13 +845,6 @@ CStackBasicDescriptor CGHeroInstance::calculateNecromancy (const BattleResult &b
TConstBonusListPtr improvedNecromancy = getBonuses(Selector::type()(BonusType::IMPROVED_NECROMANCY));
if(!improvedNecromancy->empty())
{
auto getCreatureID = [](const std::shared_ptr<Bonus> & bonus) -> CreatureID
{
assert(bonus->subtype >=0);
if(bonus->subtype >= 0)
return CreatureID(bonus->subtype);
return CreatureID::NONE;
};
int maxCasualtyLevel = 1;
for(const auto & casualty : casualties)
vstd::amax(maxCasualtyLevel, VLC->creatures()->getByIndex(casualty.first)->getLevel());
@@ -868,9 +861,9 @@ CStackBasicDescriptor CGHeroInstance::calculateNecromancy (const BattleResult &b
}
else
{
auto quality = [getCreatureID](const std::shared_ptr<Bonus> & pick) -> std::tuple<int, int, int>
auto quality = [](const std::shared_ptr<Bonus> & pick) -> std::tuple<int, int, int>
{
const auto * c = getCreatureID(pick).toCreature();
const auto * c = pick->subtype.as<CreatureID>().toCreature();
return std::tuple<int, int, int> {c->getLevel(), static_cast<int>(c->getFullRecruitCost().marketValue()), -pick->additionalInfo[1]};
};
if(quality(topPick) < quality(newPick))
@@ -879,7 +872,7 @@ CStackBasicDescriptor CGHeroInstance::calculateNecromancy (const BattleResult &b
}
if(topPick)
{
creatureTypeRaised = getCreatureID(topPick);
creatureTypeRaised = topPick->subtype.as<CreatureID>();
requiredCasualtyLevel = std::max(topPick->additionalInfo[1], 1);
}
}
@@ -1015,12 +1008,12 @@ int32_t CGHeroInstance::getSpellCost(const spells::Spell * sp) const
void CGHeroInstance::pushPrimSkill( PrimarySkill which, int val )
{
auto sel = Selector::typeSubtype(BonusType::PRIMARY_SKILL, static_cast<int>(which))
auto sel = Selector::typeSubtype(BonusType::PRIMARY_SKILL, TBonusSubtype(which))
.And(Selector::sourceType()(BonusSource::HERO_BASE_SKILL));
if(hasBonus(sel))
removeBonuses(sel);
addNewBonus(std::make_shared<Bonus>(BonusDuration::PERMANENT, BonusType::PRIMARY_SKILL, BonusSource::HERO_BASE_SKILL, val, id.getNum(), static_cast<int>(which)));
addNewBonus(std::make_shared<Bonus>(BonusDuration::PERMANENT, BonusType::PRIMARY_SKILL, BonusSource::HERO_BASE_SKILL, val, id.getNum(), TBonusSubtype(which)));
}
EAlignment CGHeroInstance::getAlignment() const
@@ -1283,7 +1276,7 @@ std::vector<SecondarySkill> CGHeroInstance::getLevelUpProposedSecondarySkills()
for(const auto & elem : secSkills)
{
if(elem.second < SecSkillLevel::EXPERT)
if(elem.second < MasteryLevel::EXPERT)
basicAndAdv.insert(elem.first);
else
expert.insert(elem.first);
@@ -1403,7 +1396,7 @@ void CGHeroInstance::setPrimarySkill(PrimarySkill primarySkill, si64 value, ui8
if(primarySkill < PrimarySkill::EXPERIENCE)
{
auto skill = getBonusLocalFirst(Selector::type()(BonusType::PRIMARY_SKILL)
.And(Selector::subtype()(static_cast<int>(primarySkill)))
.And(Selector::subtype()(TBonusSubtype(primarySkill)))
.And(Selector::sourceType()(BonusSource::HERO_BASE_SKILL)));
assert(skill);
@@ -1474,13 +1467,10 @@ void CGHeroInstance::levelUpAutomatically(CRandomGenerator & rand)
}
}
bool CGHeroInstance::hasVisions(const CGObjectInstance * target, const int subtype) const
bool CGHeroInstance::hasVisions(const CGObjectInstance * target, TBonusSubtype subtype) const
{
//VISIONS spell support
const auto cached = "type_" + std::to_string(vstd::to_underlying(BonusType::VISIONS)) + "__subtype_" + std::to_string(subtype);
const int visionsMultiplier = valOfBonuses(Selector::typeSubtype(BonusType::VISIONS,subtype), cached);
const int visionsMultiplier = valOfBonuses(BonusType::VISIONS, subtype);
int visionsRange = visionsMultiplier * getPrimSkillLevel(PrimarySkill::SPELL_POWER);
@@ -1567,11 +1557,11 @@ void CGHeroInstance::serializeCommonOptions(JsonSerializeFormat & handler)
{
auto primarySkills = handler.enterStruct("primarySkills");
for(int i = 0; i < GameConstants::PRIMARY_SKILLS; ++i)
for(auto i = PrimarySkill::BEGIN; i < PrimarySkill::END; ++i)
{
int value = valOfBonuses(Selector::typeSubtype(BonusType::PRIMARY_SKILL, i).And(Selector::sourceType()(BonusSource::HERO_BASE_SKILL)));
int value = valOfBonuses(Selector::typeSubtype(BonusType::PRIMARY_SKILL, TBonusSubtype(i)).And(Selector::sourceType()(BonusSource::HERO_BASE_SKILL)));
handler.serializeInt(NPrimarySkill::names[i], value, 0);
handler.serializeInt(NPrimarySkill::names[i.getNum()], value, 0);
}
}
}
@@ -1762,7 +1752,7 @@ bool CGHeroInstance::isMissionCritical() const
void CGHeroInstance::fillUpgradeInfo(UpgradeInfo & info, const CStackInstance &stack) const
{
TConstBonusListPtr lista = getBonuses(Selector::typeSubtype(BonusType::SPECIAL_UPGRADE, stack.type->getId()));
TConstBonusListPtr lista = getBonuses(Selector::typeSubtype(BonusType::SPECIAL_UPGRADE, TBonusSubtype(stack.type->getId())));
for(const auto & it : *lista)
{
auto nid = CreatureID(it->additionalInfo[0]);