diff --git a/client/battle/CBattleInterface.cpp b/client/battle/CBattleInterface.cpp index 065a6fe4d..ed3addfb8 100644 --- a/client/battle/CBattleInterface.cpp +++ b/client/battle/CBattleInterface.cpp @@ -1750,25 +1750,14 @@ void CBattleInterface::getPossibleActionsForStack(const CStack * stack) for (Bonus * spellBonus : spellBonuses) { spell = CGI->spellh->spells[spellBonus->subtype]; - if (spell->isRisingSpell()) + switch (spellBonus->subtype) { - possibleActions.push_back (RISING_SPELL); - } - //possibleActions.push_back (NO_LOCATION); - //possibleActions.push_back (ANY_LOCATION); - //TODO: allow stacks cast aimed spells - //possibleActions.push_back (OTHER_SPELL); - else - { - switch (spellBonus->subtype) - { - case SpellID::REMOVE_OBSTACLE: - possibleActions.push_back (OBSTACLE); - break; - default: - possibleActions.push_back (selectionTypeByPositiveness (*spell)); - break; - } + case SpellID::REMOVE_OBSTACLE: + possibleActions.push_back (OBSTACLE); + break; + default: + possibleActions.push_back (selectionTypeByPositiveness (*spell)); + break; } } @@ -2194,13 +2183,18 @@ void CBattleInterface::handleHex(BattleHex myNumber, int eventType) legalAction = true; break; case FRIENDLY_CREATURE_SPELL: - if (shere && shere->alive() && ourStack && isCastingPossibleHere (sactive, shere, myNumber)) - legalAction = true; - break; - case RISING_SPELL: - if (shere && shere->canBeHealed() && ourStack && isCastingPossibleHere (sactive, shere, myNumber)) //TODO: at least one stack has to be raised by resurrection / animate dead - legalAction = true; + { + if (isCastingPossibleHere (sactive, shere, myNumber)); //need to be called before sp is determined + { + bool rise = false; //TODO: can you imagine rising hostile creatures? + sp = CGI->spellh->spells[creatureCasting ? creatureSpellToCast : spellToCast->additionalInfo]; + if (sp && sp->isRisingSpell()) + rise = true; + if (shere && (shere->alive() || rise) && ourStack) + legalAction = true; + } break; + } case RANDOM_GENIE_SPELL: { if (shere && ourStack && shere != sactive) //only positive spells for other allied creatures @@ -2379,7 +2373,6 @@ void CBattleInterface::handleHex(BattleHex myNumber, int eventType) break; case HOSTILE_CREATURE_SPELL: case FRIENDLY_CREATURE_SPELL: - case RISING_SPELL: sp = CGI->spellh->spells[creatureCasting ? creatureSpellToCast : spellToCast->additionalInfo]; //necessary if creature has random Genie spell at same time consoleMsg = boost::str(boost::format(CGI->generaltexth->allTexts[27]) % sp->name % shere->getName()); //Cast %s on %s switch (sp->id) @@ -2452,7 +2445,6 @@ void CBattleInterface::handleHex(BattleHex myNumber, int eventType) { case HOSTILE_CREATURE_SPELL: case FRIENDLY_CREATURE_SPELL: - case RISING_SPELL: case RANDOM_GENIE_SPELL: cursorFrame = ECursor::COMBAT_BLOCKED; consoleMsg = CGI->generaltexth->allTexts[23]; diff --git a/lib/BattleState.cpp b/lib/BattleState.cpp index 355d3d21d..3d69cde1d 100644 --- a/lib/BattleState.cpp +++ b/lib/BattleState.cpp @@ -112,7 +112,7 @@ void BattleInfo::calculateCasualties( std::map *casualties ) const for(auto & elem : stacks)//setting casualties { const CStack * const st = elem; - si32 killed = (st->alive() ? st->baseAmount - st->count : st->baseAmount); + si32 killed = (st->alive() ? (st->baseAmount - st->count + st->resurrected) : st->baseAmount); vstd::amax(killed, 0); if(killed) casualties[!st->attackerOwned][st->getCreature()->idNumber] += killed; @@ -161,9 +161,9 @@ CStack * BattleInfo::generateNewStack(const CStackBasicDescriptor &base, bool at } //All spells casted by hero 9resurrection, cure, sacrifice) -ui32 BattleInfo::calculateHealedHP(const CGHeroInstance * caster, const CSpell * spell, const CStack * stack, const CStack * sacrificedStack) const +ui32 CBattleInfoCallback::calculateHealedHP(const CGHeroInstance * caster, const CSpell * spell, const CStack * stack, const CStack * sacrificedStack) const { - bool resurrect = resurrects(spell->id); + bool resurrect = spell->isRisingSpell(); int healedHealth; if (spell->id == SpellID::SACRIFICE && sacrificedStack) healedHealth = (caster->getPrimSkillLevel(PrimarySkill::SPELL_POWER) + sacrificedStack->MaxHealth() + spell->powers[caster->getSpellSchoolLevel(spell)]) * sacrificedStack->count; @@ -173,15 +173,15 @@ ui32 BattleInfo::calculateHealedHP(const CGHeroInstance * caster, const CSpell * return std::min(healedHealth, stack->MaxHealth() - stack->firstHPleft + (resurrect ? stack->baseAmount * stack->MaxHealth() : 0)); } //Archangel -ui32 BattleInfo::calculateHealedHP(int healedHealth, const CSpell * spell, const CStack * stack) const +ui32 CBattleInfoCallback::calculateHealedHP(int healedHealth, const CSpell * spell, const CStack * stack) const { - bool resurrect = resurrects(spell->id); + bool resurrect = spell->isRisingSpell(); return std::min(healedHealth, stack->MaxHealth() - stack->firstHPleft + (resurrect ? stack->baseAmount * stack->MaxHealth() : 0)); } //Casted by stack, no hero bonus applied -ui32 BattleInfo::calculateHealedHP(const CSpell * spell, int usedSpellPower, int spellSchoolLevel, const CStack * stack) const +ui32 CBattleInfoCallback::calculateHealedHP(const CSpell * spell, int usedSpellPower, int spellSchoolLevel, const CStack * stack) const { - bool resurrect = resurrects(spell->id); + bool resurrect = spell->isRisingSpell(); int healedHealth = usedSpellPower * spell->power + spell->powers[spellSchoolLevel]; return std::min(healedHealth, stack->MaxHealth() - stack->firstHPleft + (resurrect ? stack->baseAmount * stack->MaxHealth() : 0)); } @@ -893,6 +893,7 @@ void CStack::postInit() shots = getCreature()->valOfBonuses(Bonus::SHOTS); counterAttacks = 1 + valOfBonuses(Bonus::ADDITIONAL_RETALIATION); casts = valOfBonuses(Bonus::CASTS); + resurrected = 0; } ui32 CStack::level() const diff --git a/lib/BattleState.h b/lib/BattleState.h index cc36f7659..451a90f82 100644 --- a/lib/BattleState.h +++ b/lib/BattleState.h @@ -125,14 +125,10 @@ struct DLL_LINKAGE BattleInfo : public CBonusSystemNode, public CBattleInfoCallb CStack * generateNewStack(const CStackBasicDescriptor &base, bool attackerOwned, SlotID slot, BattleHex position) const; //helper for CGameHandler::setupBattle and spells addign new stacks to the battlefield int getIdForNewStack() const; //suggest a currently unused ID that'd suitable for generating a new stack //std::pair getNearestStack(const CStack * closest, boost::logic::tribool attackerOwned) const; //if attackerOwned is indetermnate, returened stack is of any owner; hex is the number of hex we should be looking from; returns (nerarest creature, predecessorHex) - ui32 calculateHealedHP(const CGHeroInstance * caster, const CSpell * spell, const CStack * stack, const CStack * sacrificedStack = nullptr) const; //Sacrifice - ui32 calculateHealedHP(int healedHealth, const CSpell * spell, const CStack * stack) const; //for Archangel - ui32 calculateHealedHP(const CSpell * spell, int usedSpellPower, int spellSchoolLevel, const CStack * stack) const; //healing spells casted by stacks bool resurrects(SpellID spellid) const; //TODO: move it to spellHandler? const CGHeroInstance * getHero(PlayerColor player) const; //returns fighting hero that belongs to given player - std::vector calculateResistedStacks(const CSpell * sp, const CGHeroInstance * caster, const CGHeroInstance * hero2, const std::vector affectedCreatures, PlayerColor casterSideOwner, ECastingMode::ECastingMode mode, int usedSpellPower, int spellLevel) const; const CStack * battleGetStack(BattleHex pos, bool onlyAlive); //returns stack at given tile @@ -165,6 +161,7 @@ public: ui8 counterAttacks; //how many counter attacks can be performed more in this turn (by default set at the beginning of the round to 1) si16 shots; //how many shots left ui8 casts; //how many casts left + TQuantity resurrected; // these units will be taken back after battle is over std::set state; //overrides @@ -218,7 +215,7 @@ public: h & static_cast(*this); h & static_cast(*this); h & ID & baseAmount & firstHPleft & owner & slot & attackerOwned & position & state & counterAttacks - & shots & casts & count; + & shots & casts & count & resurrected; const CArmedInstance *army = (base ? base->armyObj : nullptr); SlotID slot = (base ? base->armyObj->findStack(base) : SlotID()); diff --git a/lib/CBattleCallback.cpp b/lib/CBattleCallback.cpp index 9261a6a4c..74940bb43 100644 --- a/lib/CBattleCallback.cpp +++ b/lib/CBattleCallback.cpp @@ -503,8 +503,8 @@ const CStack* CBattleInfoCallback::battleGetStackByPos(BattleHex pos, bool onlyA { RETURN_IF_NOT_BATTLE(nullptr); for(auto s : battleGetAllStacks()) - if(vstd::contains(s->getHexes(), pos) && (!onlyAlive || s->alive())) - return s; + if(vstd::contains(s->getHexes(), pos) && (!onlyAlive || s->alive())) + return s; return nullptr; } @@ -1562,7 +1562,8 @@ ESpellCastProblem::ESpellCastProblem CBattleInfoCallback::battleIsImmune(const C if (spell->isRisingSpell()) { - if (subject->count >= subject->baseAmount) //TODO: calculate potential hp raised + auto maxHealth = calculateHealedHP (caster, spell, subject); + if (subject->count >= subject->baseAmount || maxHealth < subject->MaxHealth()) //must be able to rise at least one full creature return ESpellCastProblem::STACK_IMMUNE_TO_SPELL; } @@ -1663,7 +1664,7 @@ ESpellCastProblem::ESpellCastProblem CBattleInfoCallback::battleCanCastThisSpell { const CGHeroInstance * caster = battleGetFightingHero(side); bool targetExists = false; - for(const CStack * stack : battleAliveStacks()) + for(const CStack * stack : battleGetAllStacks()) //dead stacks will be immune anyway { switch (spell->positiveness) { @@ -1941,8 +1942,9 @@ std::set CBattleInfoCallback::getAffectedCreatures(const CSpell * const ui8 attackerSide = playerToSide(attackerOwner) == 1; const auto attackedHexes = spell->rangeInHexes(destinationTile, skillLevel, attackerSide); - const bool onlyAlive = spell->id != SpellID::RESURRECTION && spell->id != SpellID::ANIMATE_DEAD; //when casting resurrection or animate dead we should be allow to select dead stack - //fixme: what about other rising spells (Sacrifice) ? + const bool onlyAlive = !spell->isRisingSpell(); //when casting resurrection or animate dead we should be allow to select dead stack + + //TODO: more generic solution for mass spells if(spell->id == SpellID::DEATH_RIPPLE || spell->id == SpellID::DESTROY_UNDEAD || spell->id == SpellID::ARMAGEDDON) { for(const CStack *stack : battleGetAllStacks()) diff --git a/lib/CBattleCallback.h b/lib/CBattleCallback.h index d898127bf..8491b1fc9 100644 --- a/lib/CBattleCallback.h +++ b/lib/CBattleCallback.h @@ -260,6 +260,9 @@ public: std::vector battleGetPossibleTargets(PlayerColor player, const CSpell *spell) const; ui32 calculateSpellBonus(ui32 baseDamage, const CSpell * sp, const CGHeroInstance * caster, const CStack * affectedCreature) const; ui32 calculateSpellDmg(const CSpell * sp, const CGHeroInstance * caster, const CStack * affectedCreature, int spellSchoolLevel, int usedSpellPower) const; //calculates damage inflicted by spell + ui32 calculateHealedHP(const CGHeroInstance * caster, const CSpell * spell, const CStack * stack, const CStack * sacrificedStack = nullptr) const; //Sacrifice + ui32 calculateHealedHP(int healedHealth, const CSpell * spell, const CStack * stack) const; //for Archangel + ui32 calculateHealedHP(const CSpell * spell, int usedSpellPower, int spellSchoolLevel, const CStack * stack) const; //healing spells casted by stacks std::set getAffectedCreatures(const CSpell * s, int skillLevel, PlayerColor attackerOwner, BattleHex destinationTile); //calculates stack affected by given spell SpellID battleGetRandomStackSpell(const CStack * stack, ERandomSpell mode) const; diff --git a/lib/NetPacksLib.cpp b/lib/NetPacksLib.cpp index eab7546cf..6d769271a 100644 --- a/lib/NetPacksLib.cpp +++ b/lib/NetPacksLib.cpp @@ -1403,12 +1403,12 @@ DLL_LINKAGE void StacksHealedOrResurrected::applyGs( CGameState *gs ) if(resurrected) { changedStack->state.insert(EBattleStackState::ALIVE); - if(elem.lowLevelResurrection) - changedStack->state.insert(EBattleStackState::SUMMONED); //TODO: different counter for rised units } //int missingHPfirst = changedStack->MaxHealth() - changedStack->firstHPleft; int res = std::min( elem.healedHP / changedStack->MaxHealth() , changedStack->baseAmount - changedStack->count ); changedStack->count += res; + if(elem.lowLevelResurrection) + changedStack->resurrected += res; changedStack->firstHPleft += elem.healedHP - res * changedStack->MaxHealth(); if(changedStack->firstHPleft > changedStack->MaxHealth()) { diff --git a/server/CGameHandler.cpp b/server/CGameHandler.cpp index eb1db3abd..6d4f71807 100644 --- a/server/CGameHandler.cpp +++ b/server/CGameHandler.cpp @@ -6266,6 +6266,9 @@ CasualtiesAfterBattle::CasualtiesAfterBattle(const CArmedInstance *army, BattleI if(vstd::contains(st->state, EBattleStackState::SUMMONED)) //don't take into account summoned stacks continue; + //FIXME: this info is also used in BattleInfo::calculateCasualties, refactor + st->count = std::max (0, st->count - st->resurrected); + if(st->owner==color && !army->slotEmpty(st->slot) && st->count < army->getStackCount(st->slot)) { StackLocation sl(army, st->slot);