diff --git a/client/CHeroWindow.cpp b/client/CHeroWindow.cpp index 616583435..f448c58fd 100644 --- a/client/CHeroWindow.cpp +++ b/client/CHeroWindow.cpp @@ -6,8 +6,8 @@ #include "CHeroWindow.h" #include "CMessage.h" #include "CKingdomInterface.h" -#include -#include +#include "SDL.h" +#include "SDL_Extensions.h" #include "CBitmapHandler.h" #include "Graphics.h" #include "CSpellWindow.h" diff --git a/lib/BattleState.cpp b/lib/BattleState.cpp index aaa281101..260a44075 100644 --- a/lib/BattleState.cpp +++ b/lib/BattleState.cpp @@ -710,7 +710,17 @@ std::set BattleInfo::getAttackedCreatures( const CSpell * s, int skillL { CStack * st = getStackT(*it, onlyAlive); if(st) - attackedCres.insert(st); + { + if (s->id == 76) //Death Cloud //TODO: fireball and fire immunity + { + if (st->isLiving() || st->position == destinationTile) //directly hit or alive + { + attackedCres.insert(st); + } + } + else + attackedCres.insert(st); + } } } else if(s->getTargetType() == CSpell::CREATURE_EXPERT_MASSIVE) diff --git a/lib/HeroBonus.h b/lib/HeroBonus.h index d8755a1ec..dfcb40058 100644 --- a/lib/HeroBonus.h +++ b/lib/HeroBonus.h @@ -101,7 +101,7 @@ namespace PrimarySkill BONUS_NAME(BLOCKS_RETALIATION) /*eg. naga*/ \ BONUS_NAME(SPELL_IMMUNITY) /*subid - spell id*/ \ BONUS_NAME(MANA_CHANNELING) /*value in %, eg. familiar*/ \ - BONUS_NAME(SPELL_LIKE_ATTACK) /*value - spell id; range is taken from spell, but damage from creature; eg. magog*/ \ + BONUS_NAME(SPELL_LIKE_ATTACK) /*subtype - spell, value - spell level; range is taken from spell, but damage from creature; eg. magog*/ \ BONUS_NAME(THREE_HEADED_ATTACK) /*eg. cerberus*/ \ BONUS_NAME(DAEMON_SUMMONING) /*pit lord*/ \ BONUS_NAME(FIRE_IMMUNITY) /*subtype 0 - all, 1 - all except positive, 2 - only damage spells*/ \ diff --git a/lib/NetPacks.h b/lib/NetPacks.h index e88b69303..95c478424 100644 --- a/lib/NetPacks.h +++ b/lib/NetPacks.h @@ -1262,7 +1262,7 @@ struct BattleStackAttacked : public CPackForClient//3005 ui32 stackAttacked, attackerID; ui32 newAmount, newHP, killedAmount, damageAmount; - enum EFlags {KILLED = 1, EFFECT = 2}; + enum EFlags {KILLED = 1, EFFECT = 2, SECONDARY = 4}; ui8 flags; //uses EFlags (above) ui32 effect; //set only if flag EFFECT is set std::vector healedStacks; //used when life drain @@ -1276,6 +1276,10 @@ struct BattleStackAttacked : public CPackForClient//3005 { return flags & EFFECT; } + bool isSecondary() const//if stack was not a primary target (receives no spell effects) + { + return flags & SECONDARY; + } bool lifeDrain() const //if this attack involves life drain effect { return healedStacks.size() > 0; diff --git a/server/CGameHandler.cpp b/server/CGameHandler.cpp index f42b52d7e..4cddae5db 100644 --- a/server/CGameHandler.cpp +++ b/server/CGameHandler.cpp @@ -539,48 +539,97 @@ void CGameHandler::prepareAttack(BattleAttack &bat, const CStack *att, const CSt } bsa->damageAmount = gs->curB->calculateDmg(att, def, gs->curB->battleGetOwner(att), gs->curB->battleGetOwner(def), bat.shot(), distance, bat.lucky(), bat.ballistaDoubleDmg());//counting dealt damage - - int dmg = bsa->damageAmount; def->prepareAttacked(*bsa); - //life drain handling - if (att->hasBonusOfType(Bonus::LIFE_DRAIN) && def->isLiving()) + //TODO: multiple attack targets + const Bonus * bonus = att->getBonus(Selector::type(Bonus::SPELL_LIKE_ATTACK)); + if (bonus) { - StacksHealedOrResurrected shi; - shi.lifeDrain = (ui8)true; - shi.tentHealing = (ui8)false; - shi.drainedFrom = def->ID; + BattleStackAttacked bss = *bsa; // copy some parameters, such as attacker + std::set attackedCreatures = gs->curB->getAttackedCreatures(VLC->spellh->spells[bonus->subtype], bonus->val, att->owner, def->position); - StacksHealedOrResurrected::HealInfo hi; - hi.stackID = att->ID; - hi.healedHP = std::min(dmg, att->MaxHealth() - att->firstHPleft + att->MaxHealth() * (att->baseAmount - att->count) ); - hi.lowLevelResurrection = false; - shi.healedStacks.push_back(hi); - - if (hi.healedHP > 0) + BOOST_FOREACH(CStack * stack, attackedCreatures) { - bsa->healedStacks.push_back(shi); + if (stack != def) //do not hit same stack twice + { + bss.flags |= BattleStackAttacked::SECONDARY; //all other targets do not suffer from spells & spell-like abilities + bss.stackAttacked = stack->ID; + bss.damageAmount = gs->curB->calculateDmg(att, stack, gs->curB->battleGetOwner(att), + gs->curB->battleGetOwner(stack), bat.shot(), distance, bat.lucky(), bat.ballistaDoubleDmg()); + bat.bsa.push_back(bss); //add this stack to the list of victims + stack->prepareAttacked(bss); + + //life drain handling + if (att->hasBonusOfType(Bonus::LIFE_DRAIN) && stack->isLiving()) + { + StacksHealedOrResurrected shi; + shi.lifeDrain = (ui8)true; + shi.tentHealing = (ui8)false; + shi.drainedFrom = stack->ID; + + StacksHealedOrResurrected::HealInfo hi; + hi.stackID = stack->ID; + hi.healedHP = std::min(dmg, att->MaxHealth() - att->firstHPleft + att->MaxHealth() * (att->baseAmount - att->count) ); + hi.lowLevelResurrection = false; + shi.healedStacks.push_back(hi); + + if (hi.healedHP > 0) + { + bsa->healedStacks.push_back(shi); + } + } + + //fire shield handling + if (!bat.shot() && stack->hasBonusOfType(Bonus::FIRE_SHIELD) && !att->hasBonusOfType (Bonus::FIRE_IMMUNITY) && !bsa->killed() ) + { + bss.stackAttacked = att->ID; //invert + bss.attackerID = stack->ID; + bss.flags = BattleStackAttacked::EFFECT; //not seondary - fire shield damages only one creature + bss.effect = 11; + + bsa->damageAmount = (dmg * stack->valOfBonuses(Bonus::FIRE_SHIELD)) / 100; + att->prepareAttacked(*bsa); + } + } } - } - else - { } + else // only one target + { // TODO: move duplicated code to separate function + //life drain handling + if (att->hasBonusOfType(Bonus::LIFE_DRAIN) && def->isLiving()) + { + StacksHealedOrResurrected shi; + shi.lifeDrain = (ui8)true; + shi.tentHealing = (ui8)false; + shi.drainedFrom = def->ID; - //fire shield handling - if ( !bat.shot() && def->hasBonusOfType(Bonus::FIRE_SHIELD) && !bsa->killed() ) - { - bat.bsa.push_back(BattleStackAttacked()); - BattleStackAttacked *bsa = &bat.bsa.back(); - bsa->stackAttacked = att->ID; - bsa->attackerID = def->ID; - bsa->flags |= BattleStackAttacked::EFFECT; - bsa->effect = 11; + StacksHealedOrResurrected::HealInfo hi; + hi.stackID = att->ID; + hi.healedHP = std::min(dmg, att->MaxHealth() - att->firstHPleft + att->MaxHealth() * (att->baseAmount - att->count) ); + hi.lowLevelResurrection = false; + shi.healedStacks.push_back(hi); - bsa->damageAmount = (dmg * def->valOfBonuses(Bonus::FIRE_SHIELD)) / 100; - att->prepareAttacked(*bsa); + if (hi.healedHP > 0) + { + bsa->healedStacks.push_back(shi); + } + } + + //fire shield handling + if ( !bat.shot() && def->hasBonusOfType(Bonus::FIRE_SHIELD) && !att->hasBonusOfType (Bonus::FIRE_IMMUNITY) && !bsa->killed() ) + { + bat.bsa.push_back(BattleStackAttacked()); + BattleStackAttacked *bsa = &bat.bsa.back(); + bsa->stackAttacked = att->ID; + bsa->attackerID = def->ID; + bsa->flags |= BattleStackAttacked::EFFECT; + bsa->effect = 11; + + bsa->damageAmount = (dmg * def->valOfBonuses(Bonus::FIRE_SHIELD)) / 100; + att->prepareAttacked(*bsa); + } } - } void CGameHandler::handleConnection(std::set players, CConnection &c) { @@ -4273,9 +4322,9 @@ void CGameHandler::handleAfterAttackCasting( const BattleAttack & bat ) BOOST_FOREACH(ui32 spellID, spellsToCast) { const CStack * oneOfAttacked = NULL; - for(int g=0; g 0) + if (bat.bsa[g].newAmount > 0 && !bat.bsa[g].isSecondary()) //apply effects only to first target stack if it's alive { oneOfAttacked = gs->curB->getStack(bat.bsa[g].stackAttacked); break;