diff --git a/hch/CObjectHandler.cpp b/hch/CObjectHandler.cpp index 903bdbd15..680ada40f 100644 --- a/hch/CObjectHandler.cpp +++ b/hch/CObjectHandler.cpp @@ -1013,6 +1013,79 @@ bool CGHeroInstance::canCastThisSpell(const CSpell * spell) const return false; } +/** + * 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. + */ +std::pair CGHeroInstance::calculateNecromancy (BattleResult &battleResult) const +{ + const ui8 necromancyLevel = getSecSkillLevel(12); + + // Hero knows necromancy. + if (necromancyLevel > 0) { + double necromancySkill = necromancyLevel*0.1 + + valOfBonuses(HeroBonus::SECONDARY_SKILL_PREMY, 12)/100.0; + const std::set > &casualties = battleResult.casualties[!battleResult.winner]; + ui32 raisedUnits = 0; + + // Get lost enemy hit points convertible to units. + for (std::set >::const_iterator it = casualties.begin(); it != casualties.end(); it++) + raisedUnits += VLC->creh->creatures[it->first].hitPoints*it->second; + raisedUnits *= necromancySkill; + + // Figure out what to raise and how many. + const ui32 creatureTypes[] = {56, 58, 60, 64}; // IDs for Skeletons, Walking Dead, Wights and Liches respectively. + const bool improvedNecromancy = hasBonusOfType(HeroBonus::IMPROVED_NECROMANCY); + CCreature *raisedUnitType = &VLC->creh->creatures[creatureTypes[improvedNecromancy ? necromancyLevel : 0]]; + + raisedUnits /= raisedUnitType->hitPoints; + + // Make room for new units. + int slot = army.getSlotFor(raisedUnitType->idNumber); + if (slot == -1) { + // 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 = army.getSlotFor(raisedUnitType->idNumber); + } + if (raisedUnits <= 0) + raisedUnits = 1; + + return std::pair(raisedUnitType->idNumber, raisedUnits); + } + + return std::pair(-1, -1); +} + +/** + * Show the necromancy dialog with information about units raised. + * @param raisedStack Pair where the first element represents ID of the raised creature + * and the second element the amount. + */ +void CGHeroInstance::showNecromancyDialog (std::pair raisedStack) const +{ + const CCreature &unitType = VLC->creh->creatures[raisedStack.first]; + InfoWindow iw; + iw.soundID = soundBase::GENIE; + iw.player = tempOwner; + iw.components.push_back(Component(3, unitType.idNumber, raisedStack.second, 0)); + + if (raisedStack.second > 1) { // Practicing the dark arts of necromancy, ... (plural) + iw.text.addTxt(MetaString::GENERAL_TXT, 145); + iw.text.addReplacement(raisedStack.second); + iw.text.addReplacement(MetaString::CRE_PL_NAMES, unitType.idNumber); + } else { // Practicing the dark arts of necromancy, ... (singular) + iw.text.addTxt(MetaString::GENERAL_TXT, 146); + iw.text.addReplacement(MetaString::CRE_SING_NAMES, unitType.idNumber); + } + + cb->showInfoDialog(&iw); +} + int3 CGHeroInstance::getSightCenter() const { return getPosition(false); diff --git a/hch/CObjectHandler.h b/hch/CObjectHandler.h index 8da438ae7..437d90bcc 100644 --- a/hch/CObjectHandler.h +++ b/hch/CObjectHandler.h @@ -293,6 +293,8 @@ public: int getTotalStrength() const; ui8 getSpellSchoolLevel(const CSpell * spell) const; //returns level on which given spell would be cast by this hero bool canCastThisSpell(const CSpell * spell) const; //determines if this hero can cast given spell; takes into account existing spell in spellbook, existing spellbook and artifact bonuses + std::pair calculateNecromancy (BattleResult &battleResult) const; + void showNecromancyDialog (std::pair raisedStack) const; ////////////////////////////////////////////////////////////////////////// diff --git a/server/CGameHandler.cpp b/server/CGameHandler.cpp index 8e450cc72..9c986fee1 100644 --- a/server/CGameHandler.cpp +++ b/server/CGameHandler.cpp @@ -450,9 +450,32 @@ askInterfaceForMove: if(cb) cb(battleResult.data); - delete battleResult.data; - sendAndApply(&resultsApplied); + + // Necromancy if applicable. + const CGHeroInstance *winnerHero = battleResult.data->winner != 0 ? hero2 : hero1; + if (winnerHero) { + std::pair raisedStack = winnerHero->calculateNecromancy(*battleResult.data); + + // Give raised units to winner and show dialog, if any were raised. + if (raisedStack.first != -1) { + int slot = winnerHero->army.getSlotFor(raisedStack.first); + + if (slot != -1) { + SetGarrisons sg; + + sg.garrs[winnerHero->id] = winnerHero->army; + if (vstd::contains(winnerHero->army.slots, slot)) // Add to existing stack. + sg.garrs[winnerHero->id].slots[slot].second += raisedStack.second; + else // Create a new stack. + sg.garrs[winnerHero->id].slots[slot] = raisedStack; + winnerHero->showNecromancyDialog(raisedStack); + sendAndApply(&sg); + } + } + } + + delete battleResult.data; } void CGameHandler::prepareAttacked(BattleStackAttacked &bsa, CStack *def)