int CGHeroInstance::maxMovePoints(bool onLand) const { int base; if(onLand) { // used function is f(x) = 66.6x + 1300, rounded to second digit, where x is lowest speed in army static const int baseSpeed = 1300; // base speed from creature with 0 speed int armySpeed = lowestSpeed(this) * 20 / 3; base = armySpeed * 10 + baseSpeed; // separate *10 is intentional to receive same rounding as in h3 vstd::abetween(base, 1500, 2000); // base speed is limited by these values } else { base = 1500; //on water base movement is always 1500 (speed of army doesn't matter) } const Bonus::BonusType bt = onLand ? Bonus::LAND_MOVEMENT : Bonus::SEA_MOVEMENT; const int bonus = valOfBonuses(Bonus::MOVEMENT) + valOfBonuses(bt); const int subtype = onLand ? SecondarySkill::LOGISTICS : SecondarySkill::NAVIGATION; const double modifier = valOfBonuses(Bonus::SECONDARY_SKILL_PREMY, subtype) / 100.0; return int(base* (1+modifier)) + bonus; }
si32 CGHeroInstance::manaRegain() const { if (hasBonusOfType(Bonus::FULL_MANA_REGENERATION)) return manaLimit(); return 1 + valOfBonuses(Bonus::SECONDARY_SKILL_PREMY, 8) + valOfBonuses(Bonus::MANA_REGENERATION); //1 + Mysticism level }
bool compareArtifacts(const CArtifactInstance *a1, const CArtifactInstance *a2) { auto art1 = a1->artType; auto art2 = a2->artType; if (art1->valOfBonuses(Bonus::PRIMARY_SKILL) > art2->valOfBonuses(Bonus::PRIMARY_SKILL)) return true; else return art1->price > art2->price; }
si32 IBonusBearer::Attack() const { si32 ret = valOfBonuses(Bonus::PRIMARY_SKILL, PrimarySkill::ATTACK); if (double frenzyPower = valOfBonuses(Bonus::IN_FRENZY)) //frenzy for attacker { ret += (frenzyPower/100) * (double)Defense(false); } vstd::amax(ret, 0); return ret; }
int IBonusBearer::valOfBonuses(Bonus::BonusType type, int subtype /*= -1*/) const { std::stringstream cachingStr; cachingStr << "type_" << type << "s_" << subtype; CSelector s = Selector::type(type); if(subtype != -1) s = s.And(Selector::subtype(subtype)); return valOfBonuses(s, cachingStr.str()); }
si32 CStackInstance::magicResistance() const { si32 val = valOfBonuses(Selector::type(Bonus::MAGIC_RESISTANCE)); if (const CGHeroInstance * hero = dynamic_cast<const CGHeroInstance *>(_armyObj)) { //resistance skill val += hero->valOfBonuses(Bonus::SECONDARY_SKILL_PREMY, SecondarySkill::RESISTANCE); } vstd::amin (val, 100); return val; }
int IBonusBearer::LuckVal() const { if(hasBonusOfType(Bonus::NO_LUCK)) return 0; int ret = valOfBonuses(Bonus::LUCK); if(hasBonusOfType(Bonus::SELF_LUCK)) //eg. halfling vstd::amax(ret, +1); return vstd::abetween(ret, -3, +3); }
si32 IBonusBearer::Defense(bool withFrenzy /*= true*/) const { si32 ret = valOfBonuses(Bonus::PRIMARY_SKILL, PrimarySkill::DEFENSE); if(withFrenzy && hasBonusOfType(Bonus::IN_FRENZY)) //frenzy for defender { return 0; } vstd::amax(ret, 0); return ret; }
int IBonusBearer::getPrimSkillLevel(PrimarySkill::PrimarySkill id) const { int ret = 0; if(id == PrimarySkill::ATTACK) ret = Attack(); else if(id == PrimarySkill::DEFENSE) ret = Defense(); else ret = valOfBonuses(Bonus::PRIMARY_SKILL, id); vstd::amax(ret, id/2); //minimal value is 0 for attack and defense and 1 for spell power and knowledge return ret; }
int IBonusBearer::MoraleVal() const { if(hasBonusOfType(Bonus::NON_LIVING) || hasBonusOfType(Bonus::UNDEAD) || hasBonusOfType(Bonus::NO_MORALE) || hasBonusOfType(Bonus::SIEGE_WEAPON)) return 0; int ret = valOfBonuses(Bonus::MORALE); if(hasBonusOfType(Bonus::SELF_MORALE)) //eg. minotaur vstd::amax(ret, +1); return vstd::abetween(ret, -3, +3); }
ui8 CGHeroInstance::getSpellSchoolLevel(const CSpell * spell, int *outSelectedSchool) const { si16 skill = -1; //skill level spell->forEachSchool([&, this](const SpellSchoolInfo & cnf, bool & stop) { int thisSchool = std::max<int>(getSecSkillLevel(cnf.skill), valOfBonuses(Bonus::MAGIC_SCHOOL_SKILL, 1 << ((ui8)cnf.id))); if(thisSchool > skill) { skill = thisSchool; if(outSelectedSchool) *outSelectedSchool = (ui8)cnf.id; } }); vstd::amax(skill, valOfBonuses(Bonus::MAGIC_SCHOOL_SKILL, 0)); //any school bonus vstd::amax(skill, valOfBonuses(Bonus::SPELL, spell->id.toEnum())); //given by artifact or other effect if (hasBonusOfType(Bonus::MAXED_SPELL, spell->id))//hero specialty (Daremyth, Melodia) skill = 3; assert(skill >= 0 && skill <= 3); return skill; }
ui32 IBonusBearer::Speed( int turn /*= 0*/ , bool useBind /* = false*/) const { //war machines cannot move if(hasBonus(Selector::type(Bonus::SIEGE_WEAPON).And(Selector::turns(turn)))) { return 0; } //bind effect check - doesn't influence stack initiative if (useBind && getEffect (SpellID::BIND)) { return 0; } return valOfBonuses(Selector::type(Bonus::STACKS_SPEED).And(Selector::turns(turn))); }
/** * Calculates what creatures and how many to be raised from a battle. * @param battleResult The results of the battle. * @return Returns a pair with the first value indicating the ID of the creature * type and second value the amount. Both values are returned as -1 if necromancy * could not be applied. */ CStackBasicDescriptor CGHeroInstance::calculateNecromancy (const BattleResult &battleResult) const { const ui8 necromancyLevel = getSecSkillLevel(SecondarySkill::NECROMANCY); // Hero knows necromancy or has Necromancer Cloak if (necromancyLevel > 0 || hasBonusOfType(Bonus::IMPROVED_NECROMANCY)) { double necromancySkill = valOfBonuses(Bonus::SECONDARY_SKILL_PREMY, SecondarySkill::NECROMANCY)/100.0; vstd::amin(necromancySkill, 1.0); //it's impossible to raise more creatures than all... const std::map<ui32,si32> &casualties = battleResult.casualties[!battleResult.winner]; ui32 raisedUnits = 0; // Figure out what to raise and how many. const CreatureID creatureTypes[] = {CreatureID::SKELETON, CreatureID::WALKING_DEAD, CreatureID::WIGHTS, CreatureID::LICHES}; const bool improvedNecromancy = hasBonusOfType(Bonus::IMPROVED_NECROMANCY); const CCreature *raisedUnitType = VLC->creh->creatures[creatureTypes[improvedNecromancy ? necromancyLevel : 0]]; const ui32 raisedUnitHP = raisedUnitType->valOfBonuses(Bonus::STACK_HEALTH); //calculate creatures raised from each defeated stack for (auto & casualtie : casualties) { // Get lost enemy hit points convertible to units. CCreature * c = VLC->creh->creatures[casualtie.first]; const ui32 raisedHP = c->valOfBonuses(Bonus::STACK_HEALTH) * casualtie.second * necromancySkill; raisedUnits += std::min<ui32>(raisedHP / raisedUnitHP, casualtie.second * necromancySkill); //limit to % of HP and % of original stack count } // Make room for new units. SlotID slot = getSlotFor(raisedUnitType->idNumber); if (slot == SlotID()) { // If there's no room for unit, try it's upgraded version 2/3rds the size. raisedUnitType = VLC->creh->creatures[*raisedUnitType->upgrades.begin()]; raisedUnits = (raisedUnits*2)/3; slot = getSlotFor(raisedUnitType->idNumber); } if (raisedUnits <= 0) raisedUnits = 1; return CStackBasicDescriptor(raisedUnitType->idNumber, raisedUnits); } return CStackBasicDescriptor(); }
bool CGHeroInstance::hasVisions(const CGObjectInstance * target, const int subtype) const { //VISIONS spell support const std::string cached = boost::to_string((boost::format("type_%d__subtype_%d") % Bonus::VISIONS % subtype)); const int visionsMultiplier = valOfBonuses(Selector::typeSubtype(Bonus::VISIONS,subtype), cached); int visionsRange = visionsMultiplier * getPrimSkillLevel(PrimarySkill::SPELL_POWER); if (visionsMultiplier > 0) vstd::amax(visionsRange, 3); //minimum range is 3 tiles, but only if VISIONS bonus present const int distance = target->pos.dist2d(getPosition(false)); //logGlobal->debug(boost::to_string(boost::format("Visions: dist %d, mult %d, range %d") % distance % visionsMultiplier % visionsRange)); return (distance < visionsRange) && (target->pos.z == pos.z); }
si32 IBonusBearer::magicResistance() const { return valOfBonuses(Bonus::MAGIC_RESISTANCE); }
si32 IBonusBearer::manaLimit() const { return si32(getPrimSkillLevel(PrimarySkill::KNOWLEDGE) * (100.0 + valOfBonuses(Bonus::SECONDARY_SKILL_PREMY, SecondarySkill::INTELLIGENCE)) / 10.0); }
ui32 IBonusBearer::getMaxDamage() const { std::stringstream cachingStr; cachingStr << "type_" << Bonus::CREATURE_DAMAGE << "s_0Otype_" << Bonus::CREATURE_DAMAGE << "s_2"; return valOfBonuses(Selector::typeSubtype(Bonus::CREATURE_DAMAGE, 0).Or(Selector::typeSubtype(Bonus::CREATURE_DAMAGE, 2)), cachingStr.str()); }
ui32 IBonusBearer::MaxHealth() const { return std::max(1, valOfBonuses(Bonus::STACK_HEALTH)); //never 0 }
TExpType CGHeroInstance::calculateXp(TExpType exp) const { return exp * (100 + valOfBonuses(Bonus::SECONDARY_SKILL_PREMY, SecondarySkill::LEARNING))/100.0; }
int IBonusBearer::valOfBonuses(Bonus::BonusType type, const CSelector &selector) const { return valOfBonuses(Selector::type(type).And(selector)); }
int CGHeroInstance::getSightRadious() const { return 5 + getSecSkillLevel(SecondarySkill::SCOUTING) + valOfBonuses(Bonus::SIGHT_RADIOUS); //default + scouting }