1
0
mirror of https://github.com/vcmi/vcmi.git synced 2025-03-19 21:10:12 +02:00

Optimized getHeroStrength method

- replaced 4x access to bonus system with single access
- fixed formula for Diplomacy
- fxied formula for hero transfer in campaigns
- removed pointless sqrt(pow()) construct
This commit is contained in:
Ivan Savenko 2024-12-22 14:49:35 +00:00
parent 5caf12f22f
commit e035cf9e63
7 changed files with 89 additions and 29 deletions

View File

@ -339,7 +339,7 @@ void CampaignState::setCurrentMapAsConquered(std::vector<CGHeroInstance *> heroe
{ {
range::sort(heroes, [](const CGHeroInstance * a, const CGHeroInstance * b) range::sort(heroes, [](const CGHeroInstance * a, const CGHeroInstance * b)
{ {
return a->getHeroStrengthForCampaign() > b->getHeroStrengthForCampaign(); return a->getValueForCampaign() > b->getValueForCampaign();
}); });
logGlobal->info("Scenario %d of campaign %s (%s) has been completed", currentMap->getNum(), getFilename(), getNameTranslated()); logGlobal->info("Scenario %d of campaign %s (%s) has been completed", currentMap->getNum(), getFilename(), getNameTranslated());

View File

@ -97,8 +97,6 @@ const PrimarySkill PrimarySkill::ATTACK(0);
const PrimarySkill PrimarySkill::DEFENSE(1); const PrimarySkill PrimarySkill::DEFENSE(1);
const PrimarySkill PrimarySkill::SPELL_POWER(2); const PrimarySkill PrimarySkill::SPELL_POWER(2);
const PrimarySkill PrimarySkill::KNOWLEDGE(3); const PrimarySkill PrimarySkill::KNOWLEDGE(3);
const PrimarySkill PrimarySkill::BEGIN(0);
const PrimarySkill PrimarySkill::END(4);
const PrimarySkill PrimarySkill::EXPERIENCE(4); const PrimarySkill PrimarySkill::EXPERIENCE(4);
const BoatId BoatId::NONE(-1); const BoatId BoatId::NONE(-1);
@ -630,6 +628,18 @@ std::string GameResID::entityType()
return "resource"; return "resource";
} }
const std::array<PrimarySkill, 4> & PrimarySkill::ALL_SKILLS()
{
static const std::array allSkills = {
PrimarySkill(ATTACK),
PrimarySkill(DEFENSE),
PrimarySkill(SPELL_POWER),
PrimarySkill(KNOWLEDGE)
};
return allSkills;
}
const std::array<GameResID, 7> & GameResID::ALL_RESOURCES() const std::array<GameResID, 7> & GameResID::ALL_RESOURCES()
{ {
static const std::array allResources = { static const std::array allResources = {

View File

@ -230,8 +230,7 @@ public:
static const PrimarySkill SPELL_POWER; static const PrimarySkill SPELL_POWER;
static const PrimarySkill KNOWLEDGE; static const PrimarySkill KNOWLEDGE;
static const PrimarySkill BEGIN; static const std::array<PrimarySkill, 4> & ALL_SKILLS();
static const PrimarySkill END;
static const PrimarySkill EXPERIENCE; static const PrimarySkill EXPERIENCE;

View File

@ -82,13 +82,13 @@ void CGameStateCampaign::trimCrossoverHeroesParameters(const CampaignTravel & tr
//trimming prim skills //trimming prim skills
for(auto & hero : campaignHeroReplacements) for(auto & hero : campaignHeroReplacements)
{ {
for(auto g = PrimarySkill::BEGIN; g < PrimarySkill::END; ++g) for(auto skill : PrimarySkill::ALL_SKILLS())
{ {
auto sel = Selector::type()(BonusType::PRIMARY_SKILL) auto sel = Selector::type()(BonusType::PRIMARY_SKILL)
.And(Selector::subtype()(BonusSubtypeID(g))) .And(Selector::subtype()(BonusSubtypeID(skill)))
.And(Selector::sourceType()(BonusSource::HERO_BASE_SKILL)); .And(Selector::sourceType()(BonusSource::HERO_BASE_SKILL));
hero.hero->getLocalBonus(sel)->val = hero.hero->getHeroClass()->primarySkillInitial[g.getNum()]; hero.hero->getLocalBonus(sel)->val = hero.hero->getHeroClass()->primarySkillInitial[skill.getNum()];
} }
} }
} }
@ -337,14 +337,14 @@ void CGameStateCampaign::giveCampaignBonusToHero(CGHeroInstance * hero)
case CampaignBonusType::PRIMARY_SKILL: case CampaignBonusType::PRIMARY_SKILL:
{ {
const ui8 * ptr = reinterpret_cast<const ui8 *>(&curBonus->info2); const ui8 * ptr = reinterpret_cast<const ui8 *>(&curBonus->info2);
for(auto g = PrimarySkill::BEGIN; g < PrimarySkill::END; ++g) for(auto skill : PrimarySkill::ALL_SKILLS())
{ {
int val = ptr[g.getNum()]; int val = ptr[skill.getNum()];
if(val == 0) if(val == 0)
continue; continue;
auto currentScenario = *gameState->scenarioOps->campState->currentScenario(); auto currentScenario = *gameState->scenarioOps->campState->currentScenario();
auto bb = std::make_shared<Bonus>( BonusDuration::PERMANENT, BonusType::PRIMARY_SKILL, BonusSource::CAMPAIGN_BONUS, val, BonusSourceID(currentScenario), BonusSubtypeID(g) ); auto bb = std::make_shared<Bonus>( BonusDuration::PERMANENT, BonusType::PRIMARY_SKILL, BonusSource::CAMPAIGN_BONUS, val, BonusSourceID(currentScenario), BonusSubtypeID(skill) );
hero->addNewBonus(bb); hero->addNewBonus(bb);
} }
break; break;
@ -551,7 +551,7 @@ void CGameStateCampaign::initHeroes()
int maxB = -1; int maxB = -1;
for (int b=0; b<heroes.size(); ++b) for (int b=0; b<heroes.size(); ++b)
{ {
if (maxB == -1 || heroes[b]->getTotalStrength() > heroes[maxB]->getTotalStrength()) if (maxB == -1 || heroes[b]->getValueForCampaign() > heroes[maxB]->getValueForCampaign())
{ {
maxB = b; maxB = b;
} }

View File

@ -336,7 +336,7 @@ void CGCreature::setPropertyDer(ObjProperty what, ObjPropertyID identifier)
int CGCreature::takenAction(const CGHeroInstance *h, bool allowJoin) const int CGCreature::takenAction(const CGHeroInstance *h, bool allowJoin) const
{ {
//calculate relative strength of hero and creatures armies //calculate relative strength of hero and creatures armies
double relStrength = static_cast<double>(h->getTotalStrength()) / getArmyStrength(); double relStrength = static_cast<double>(h->getValueForDiplomacy()) / getArmyStrength();
int powerFactor; int powerFactor;
if(relStrength >= 7) if(relStrength >= 7)

View File

@ -704,19 +704,46 @@ void CGHeroInstance::setPropertyDer(ObjProperty what, ObjPropertyID identifier)
setStackCount(SlotID(0), identifier.getNum()); setStackCount(SlotID(0), identifier.getNum());
} }
std::array<int, 4> CGHeroInstance::getPrimarySkills() const
{
std::array<int, 4> result;
auto allSkills = getBonusBearer()->getBonusesOfType(BonusType::PRIMARY_SKILL);
for (auto skill : PrimarySkill::ALL_SKILLS())
{
int ret = allSkills->valOfBonuses(Selector::subtype()(BonusSubtypeID(skill)));
int minSkillValue = VLC->engineSettings()->getVectorValue(EGameSettings::HEROES_MINIMAL_PRIMARY_SKILLS, skill.getNum());
result[skill] = std::max(ret, minSkillValue); //otherwise, some artifacts may cause negative skill value effect
}
return result;
}
double CGHeroInstance::getFightingStrength() const double CGHeroInstance::getFightingStrength() const
{ {
return sqrt((1.0 + 0.05*getPrimSkillLevel(PrimarySkill::ATTACK)) * (1.0 + 0.05*getPrimSkillLevel(PrimarySkill::DEFENSE))); const auto & primarySkills = getPrimarySkills();
return getFightingStrengthImpl(primarySkills);
}
double CGHeroInstance::getFightingStrengthImpl(const std::array<int, 4> & primarySkills) const
{
return sqrt((1.0 + 0.05*primarySkills[PrimarySkill::ATTACK]) * (1.0 + 0.05*primarySkills[PrimarySkill::DEFENSE]));
} }
double CGHeroInstance::getMagicStrength() const double CGHeroInstance::getMagicStrength() const
{
const auto & primarySkills = getPrimarySkills();
return getMagicStrengthImpl(primarySkills);
}
double CGHeroInstance::getMagicStrengthImpl(const std::array<int, 4> & primarySkills) const
{ {
if (!hasSpellbook()) if (!hasSpellbook())
return 1; return 1;
bool atLeastOneCombatSpell = false; bool atLeastOneCombatSpell = false;
for (auto spell : spells) for (auto spell : spells)
{ {
if (spellbookContainsSpell(spell) && spell.toSpell()->isCombat()) if (spell.toSpell()->isCombat())
{ {
atLeastOneCombatSpell = true; atLeastOneCombatSpell = true;
break; break;
@ -724,22 +751,40 @@ double CGHeroInstance::getMagicStrength() const
} }
if (!atLeastOneCombatSpell) if (!atLeastOneCombatSpell)
return 1; return 1;
return sqrt((1.0 + 0.05*getPrimSkillLevel(PrimarySkill::KNOWLEDGE) * mana / manaLimit()) * (1.0 + 0.05*getPrimSkillLevel(PrimarySkill::SPELL_POWER) * mana / manaLimit())); return sqrt((1.0 + 0.05*primarySkills[PrimarySkill::KNOWLEDGE] * mana / manaLimit()) * (1.0 + 0.05*primarySkills[PrimarySkill::SPELL_POWER] * mana / manaLimit()));
}
double CGHeroInstance::getMagicStrengthForCampaign() const
{
return sqrt((1.0 + 0.05 * getPrimSkillLevel(PrimarySkill::KNOWLEDGE)) * (1.0 + 0.05 * getPrimSkillLevel(PrimarySkill::SPELL_POWER)));
} }
double CGHeroInstance::getHeroStrength() const double CGHeroInstance::getHeroStrength() const
{ {
return sqrt(pow(getFightingStrength(), 2.0) * pow(getMagicStrength(), 2.0)); const auto & primarySkills = getPrimarySkills();
return getFightingStrengthImpl(primarySkills) * getMagicStrengthImpl(primarySkills);
} }
double CGHeroInstance::getHeroStrengthForCampaign() const uint64_t CGHeroInstance::getValueForDiplomacy() const
{ {
return sqrt(pow(getFightingStrength(), 2.0) * pow(getMagicStrengthForCampaign(), 2.0)); // H3 formula for hero strength when considering diplomacy skill
uint64_t armyStrength = getArmyStrength();
double heroStrength = sqrt(
(1.0 + 0.05 * getPrimSkillLevel(PrimarySkill::ATTACK)) *
(1.0 + 0.05 * getPrimSkillLevel(PrimarySkill::DEFENSE))
);
return heroStrength * armyStrength;
}
uint32_t CGHeroInstance::getValueForCampaign() const
{
/// Determined by testing H3: hero is preferred for transfer in campaigns if total sum of his primary skills and his secondary skill levels is greatest
uint32_t score = 0;
score += getPrimSkillLevel(PrimarySkill::ATTACK);
score += getPrimSkillLevel(PrimarySkill::DEFENSE);
score += getPrimSkillLevel(PrimarySkill::SPELL_POWER);
score += getPrimSkillLevel(PrimarySkill::DEFENSE);
for (const auto& secondary : secSkills)
score += secondary.second;
return score;
} }
ui64 CGHeroInstance::getTotalStrength() const ui64 CGHeroInstance::getTotalStrength() const
@ -1653,11 +1698,11 @@ void CGHeroInstance::serializeCommonOptions(JsonSerializeFormat & handler)
{ {
auto primarySkills = handler.enterStruct("primarySkills"); auto primarySkills = handler.enterStruct("primarySkills");
for(auto i = PrimarySkill::BEGIN; i < PrimarySkill::END; ++i) for(auto skill : PrimarySkill::ALL_SKILLS())
{ {
int value = valOfBonuses(Selector::typeSubtype(BonusType::PRIMARY_SKILL, BonusSubtypeID(i)).And(Selector::sourceType()(BonusSource::HERO_BASE_SKILL))); int value = valOfBonuses(Selector::typeSubtype(BonusType::PRIMARY_SKILL, BonusSubtypeID(skill)).And(Selector::sourceType()(BonusSource::HERO_BASE_SKILL)));
handler.serializeInt(NPrimarySkill::names[i.getNum()], value, 0); handler.serializeInt(NPrimarySkill::names[skill.getNum()], value, 0);
} }
} }
} }

View File

@ -62,6 +62,9 @@ private:
mutable int lowestCreatureSpeed; mutable int lowestCreatureSpeed;
ui32 movement; //remaining movement points ui32 movement; //remaining movement points
double getFightingStrengthImpl(const std::array<int, 4> & primarySkills) const;
double getMagicStrengthImpl(const std::array<int, 4> & primarySkills) const;
public: public:
////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////
@ -201,6 +204,7 @@ public:
std::vector<SecondarySkill> getLevelUpProposedSecondarySkills(vstd::RNG & rand) const; std::vector<SecondarySkill> getLevelUpProposedSecondarySkills(vstd::RNG & rand) const;
ui8 getSecSkillLevel(const SecondarySkill & skill) const; //0 - no skill ui8 getSecSkillLevel(const SecondarySkill & skill) const; //0 - no skill
std::array<int, 4> getPrimarySkills() const;
/// Returns true if hero has free secondary skill slot. /// Returns true if hero has free secondary skill slot.
bool canLearnSkill() const; bool canLearnSkill() const;
@ -225,9 +229,11 @@ public:
double getFightingStrength() const; // takes attack / defense skill into account double getFightingStrength() const; // takes attack / defense skill into account
double getMagicStrength() const; // takes knowledge / spell power skill but also current mana, whether the hero owns a spell-book and whether that books contains anything into account double getMagicStrength() const; // takes knowledge / spell power skill but also current mana, whether the hero owns a spell-book and whether that books contains anything into account
double getMagicStrengthForCampaign() const; // takes knowledge / spell power skill into account
double getHeroStrength() const; // includes fighting and magic strength double getHeroStrength() const; // includes fighting and magic strength
double getHeroStrengthForCampaign() const; // includes fighting and the for-campaign-version of magic strength
uint32_t getValueForCampaign() const;
uint64_t getValueForDiplomacy() const;
ui64 getTotalStrength() const; // includes fighting strength and army strength ui64 getTotalStrength() const; // includes fighting strength and army strength
TExpType calculateXp(TExpType exp) const; //apply learning skill TExpType calculateXp(TExpType exp) const; //apply learning skill
int getBasePrimarySkillValue(PrimarySkill which) const; //the value of a base-skill without items or temporary bonuses int getBasePrimarySkillValue(PrimarySkill which) const; //the value of a base-skill without items or temporary bonuses