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); }
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); }
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 }
int CGHeroInstance::movementPointsAfterEmbark(int MPsBefore, int basicCost, bool disembark /*= false*/) const { if(hasBonusOfType(Bonus::FREE_SHIP_BOARDING)) return (MPsBefore - basicCost) * static_cast<double>(maxMovePoints(disembark)) / maxMovePoints(!disembark); return 0; //take all MPs otherwise }
/** * 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(); }
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; }
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; }
void CArmedInstance::updateMoraleBonusFromArmy() { if(!validTypes(false)) //object not randomized, don't bother return; auto b = getExportedBonusList().getFirst(Selector::sourceType(Bonus::ARMY).And(Selector::type(Bonus::MORALE))); if(!b) { b = std::make_shared<Bonus>(Bonus::PERMANENT, Bonus::MORALE, Bonus::ARMY, 0, -1); addNewBonus(b); } //number of alignments and presence of undead std::set<TFaction> factions; bool hasUndead = false; for(auto slot : Slots()) { const CStackInstance * inst = slot.second; const CCreature * creature = VLC->creh->creatures[inst->getCreatureID()]; factions.insert(creature->faction); // Check for undead flag instead of faction (undead mummies are neutral) hasUndead |= inst->hasBonusOfType(Bonus::UNDEAD); } size_t factionsInArmy = factions.size(); //town garrison seems to take both sets into account // Take Angelic Alliance troop-mixing freedom of non-evil units into account. if (hasBonusOfType(Bonus::NONEVIL_ALIGNMENT_MIX)) { size_t mixableFactions = 0; for(TFaction f : factions) { if (VLC->townh->factions[f]->alignment != EAlignment::EVIL) mixableFactions++; } if (mixableFactions > 0) factionsInArmy -= mixableFactions - 1; } if(factionsInArmy == 1) { b->val = +1; b->description = VLC->generaltexth->arraytxt[115]; //All troops of one alignment +1 b->description = b->description.substr(0, b->description.size()-3);//trim "+1" } else if (!factions.empty()) // no bonus from empty garrison { b->val = 2 - factionsInArmy; b->description = boost::str(boost::format(VLC->generaltexth->arraytxt[114]) % factionsInArmy % b->val); //Troops of %d alignments %d b->description = b->description.substr(0, b->description.size()-2);//trim value } boost::algorithm::trim(b->description); CBonusSystemNode::treeHasChanged(); //-1 modifier for any Undead unit in army const ui8 UNDEAD_MODIFIER_ID = -2; auto undeadModifier = getExportedBonusList().getFirst(Selector::source(Bonus::ARMY, UNDEAD_MODIFIER_ID)); if(hasUndead) { if(!undeadModifier) { undeadModifier = std::make_shared<Bonus>(Bonus::PERMANENT, Bonus::MORALE, Bonus::ARMY, -1, UNDEAD_MODIFIER_ID, VLC->generaltexth->arraytxt[116]); undeadModifier->description = undeadModifier->description.substr(0, undeadModifier->description.size()-2);//trim value addNewBonus(undeadModifier); } } else if(undeadModifier) removeBonus(undeadModifier); }
bool CGHeroInstance::canWalkOnSea() const { return hasBonusOfType(Bonus::FLYING_MOVEMENT) || hasBonusOfType(Bonus::WATER_WALKING); }