1
0
mirror of https://github.com/vcmi/vcmi.git synced 2025-11-25 22:42:04 +02:00

* support for SPELL_AFTER_ATTACK, including most of spell 77 support

This commit is contained in:
mateuszb
2010-02-23 15:39:31 +00:00
parent d7a1898676
commit 5b0d646ddb
12 changed files with 288 additions and 178 deletions

View File

@@ -2926,6 +2926,7 @@ bool CGameHandler::makeBattleAction( BattleAction &ba )
BattleAttack bat;
prepareAttack(bat, curStack, stackAtEnd, distance);
sendAndApply(&bat);
handleAfterAttackCasting(bat);
//counterattack
if(!curStack->hasFeatureOfType(StackFeature::BLOCKS_RETALIATION)
@@ -2937,6 +2938,7 @@ bool CGameHandler::makeBattleAction( BattleAction &ba )
prepareAttack(bat, stackAtEnd, curStack, 0);
bat.flags |= 2;
sendAndApply(&bat);
handleAfterAttackCasting(bat);
}
//second attack
@@ -2948,6 +2950,7 @@ bool CGameHandler::makeBattleAction( BattleAction &ba )
bat.flags = 0;
prepareAttack(bat, curStack, stackAtEnd, 0);
sendAndApply(&bat);
handleAfterAttackCasting(bat);
}
//return
@@ -2981,6 +2984,7 @@ bool CGameHandler::makeBattleAction( BattleAction &ba )
{
prepareAttack(bat, curStack, destStack, 0);
sendAndApply(&bat);
handleAfterAttackCasting(bat);
}
sendAndApply(&EndAction());
@@ -3246,7 +3250,8 @@ static std::vector<ui32> calculateResistedStacks(const CSpell * sp, const CGHero
for(std::set<CStack*>::const_iterator it = affectedCreatures.begin(); it != affectedCreatures.end(); ++it)
{
if ((*it)->hasFeatureOfType(StackFeature::SPELL_IMMUNITY, sp->id) //100% sure spell immunity
|| ((*it)->valOfFeatures(StackFeature::LEVEL_SPELL_IMMUNITY) >= sp->level))
|| ( (*it)->hasFeatureOfType(StackFeature::LEVEL_SPELL_IMMUNITY) &&
(*it)->valOfFeatures(StackFeature::LEVEL_SPELL_IMMUNITY) >= sp->level) ) //some creature abilities have level 0
{
ret.push_back((*it)->ID);
continue;
@@ -3257,7 +3262,7 @@ static std::vector<ui32> calculateResistedStacks(const CSpell * sp, const CGHero
continue;
const CGHeroInstance * bonusHero; //hero we should take bonuses from
if((*it)->owner == caster->tempOwner)
if(caster && (*it)->owner == caster->tempOwner)
bonusHero = caster;
else
bonusHero = hero2;
@@ -3307,6 +3312,159 @@ static std::vector<ui32> calculateResistedStacks(const CSpell * sp, const CGHero
return ret;
}
void CGameHandler::handleSpellCasting( int spellID, int spellLvl, int destination, ui8 casterSide, ui8 casterColor,
const CGHeroInstance * caster, const CGHeroInstance * secHero )
{
CSpell *spell = &VLC->spellh->spells[spellID];
SpellCast sc;
sc.side = casterSide;
sc.id = spellID;
sc.skill = spellLvl;
sc.tile = destination;
sc.dmgToDisplay = 0;
sc.castedByHero = (bool)caster;
//calculating affected creatures for all spells
std::set<CStack*> attackedCres = gs->curB->getAttackedCreatures(spell, spellLvl, casterColor, destination);
for(std::set<CStack*>::const_iterator it = attackedCres.begin(); it != attackedCres.end(); ++it)
{
sc.affectedCres.insert((*it)->ID);
}
//checking if creatures resist
sc.resisted = calculateResistedStacks(spell, caster, secHero, attackedCres);
//calculating dmg to display
for(std::set<CStack*>::iterator it = attackedCres.begin(); it != attackedCres.end(); ++it)
{
if(vstd::contains(sc.resisted, (*it)->ID)) //this creature resisted the spell
continue;
sc.dmgToDisplay += gs->curB->calculateSpellDmg(spell, caster, *it, spellLvl);
}
sendAndApply(&sc);
//applying effects
switch(spellID)
{
case 15: //magic arrow
case 16: //ice bolt
case 17: //lightning bolt
case 18: //implosion
case 20: //frost ring
case 21: //fireball
case 22: //inferno
case 23: //meteor shower
case 24: //death ripple
case 25: //destroy undead
case 26: //armageddon
case 77: //Thunderbolt (thunderbirds)
{
StacksInjured si;
for(std::set<CStack*>::iterator it = attackedCres.begin(); it != attackedCres.end(); ++it)
{
if(vstd::contains(sc.resisted, (*it)->ID)) //this creature resisted the spell
continue;
BattleStackAttacked bsa;
bsa.flags |= 2;
bsa.effect = spell->mainEffectAnim;
bsa.damageAmount = gs->curB->calculateSpellDmg(spell, caster, *it, spellLvl);
bsa.stackAttacked = (*it)->ID;
bsa.attackerID = -1;
prepareAttacked(bsa,*it);
si.stacks.push_back(bsa);
}
if(!si.stacks.empty())
sendAndApply(&si);
break;
}
case 27: //shield
case 28: //air shield
case 30: //protection from air
case 31: //protection from fire
case 32: //protection from water
case 33: //protection from earth
case 34: //anti-magic
case 41: //bless
case 42: //curse
case 43: //bloodlust
case 44: //precision
case 45: //weakness
case 46: //stone skin
case 47: //disrupting ray
case 48: //prayer
case 49: //mirth
case 50: //sorrow
case 51: //fortune
case 52: //misfortune
case 53: //haste
case 54: //slow
case 55: //slayer
case 56: //frenzy
case 58: //counterstrike
case 59: //berserk
case 60: //hypnotize
case 61: //forgetfulness
case 62: //blind
{
SetStackEffect sse;
for(std::set<CStack*>::iterator it = attackedCres.begin(); it != attackedCres.end(); ++it)
{
if(vstd::contains(sc.resisted, (*it)->ID)) //this creature resisted the spell
continue;
sse.stacks.push_back((*it)->ID);
}
sse.effect.id = spellID;
sse.effect.level = spellLvl;
sse.effect.turnsRemain = BattleInfo::calculateSpellDuration(spell, caster);
if(!sse.stacks.empty())
sendAndApply(&sse);
break;
}
case 37: //cure
case 38: //resurrection
case 39: //animate dead
{
StacksHealedOrResurrected shr;
for(std::set<CStack*>::iterator it = attackedCres.begin(); it != attackedCres.end(); ++it)
{
if(vstd::contains(sc.resisted, (*it)->ID) //this creature resisted the spell
|| (spellID == 39 && !(*it)->hasFeatureOfType(StackFeature::UNDEAD)) //we try to cast animate dead on living stack
)
continue;
StacksHealedOrResurrected::HealInfo hi;
hi.stackID = (*it)->ID;
hi.healedHP = calculateHealedHP(caster, spell, *it);
hi.lowLevelResurrection = spellLvl <= 1;
shr.healedStacks.push_back(hi);
}
if(!shr.healedStacks.empty())
sendAndApply(&shr);
break;
}
case 64: //remove obstacle
{
ObstaclesRemoved obr;
for(int g=0; g<gs->curB->obstacles.size(); ++g)
{
std::vector<int> blockedHexes = VLC->heroh->obstacles[gs->curB->obstacles[g].ID].getBlocked(gs->curB->obstacles[g].pos);
if(vstd::contains(blockedHexes, destination)) //this obstacle covers given hex
{
obr.obstacles.insert(gs->curB->obstacles[g].uniqueID);
}
}
if(!obr.obstacles.empty())
sendAndApply(&obr);
break;
}
}
}
bool CGameHandler::makeCustomAction( BattleAction &ba )
{
switch(ba.actionType)
@@ -3343,149 +3501,8 @@ bool CGameHandler::makeCustomAction( BattleAction &ba )
sendAndApply(&StartAction(ba)); //start spell casting
SpellCast sc;
sc.side = ba.side;
sc.id = ba.additionalInfo;
sc.skill = skill;
sc.tile = ba.destinationTile;
sc.dmgToDisplay = 0;
handleSpellCasting(ba.additionalInfo, skill, ba.destinationTile, ba.side, h->tempOwner, h, secondHero);
//calculating affected creatures for all spells
std::set<CStack*> attackedCres = gs->curB->getAttackedCreatures(s, h, ba.destinationTile);
for(std::set<CStack*>::const_iterator it = attackedCres.begin(); it != attackedCres.end(); ++it)
{
sc.affectedCres.insert((*it)->ID);
}
//checking if creatures resist
sc.resisted = calculateResistedStacks(s, h, secondHero, attackedCres);
//calculating dmg to display
for(std::set<CStack*>::iterator it = attackedCres.begin(); it != attackedCres.end(); ++it)
{
if(vstd::contains(sc.resisted, (*it)->ID)) //this creature resisted the spell
continue;
sc.dmgToDisplay += gs->curB->calculateSpellDmg(s, h, *it);
}
sendAndApply(&sc);
//applying effects
switch(ba.additionalInfo) //spell id
{
case 15: //magic arrow
case 16: //ice bolt
case 17: //lightning bolt
case 18: //implosion
case 20: //frost ring
case 21: //fireball
case 22: //inferno
case 23: //meteor shower
case 24: //death ripple
case 25: //destroy undead
case 26: //armageddon
{
StacksInjured si;
for(std::set<CStack*>::iterator it = attackedCres.begin(); it != attackedCres.end(); ++it)
{
if(vstd::contains(sc.resisted, (*it)->ID)) //this creature resisted the spell
continue;
BattleStackAttacked bsa;
bsa.flags |= 2;
bsa.effect = VLC->spellh->spells[ba.additionalInfo].mainEffectAnim;
bsa.damageAmount = gs->curB->calculateSpellDmg(s, h, *it);
bsa.stackAttacked = (*it)->ID;
bsa.attackerID = -1;
prepareAttacked(bsa,*it);
si.stacks.push_back(bsa);
}
if(!si.stacks.empty())
sendAndApply(&si);
break;
}
case 27: //shield
case 28: //air shield
case 30: //protection from air
case 31: //protection from fire
case 32: //protection from water
case 33: //protection from earth
case 34: //anti-magic
case 41: //bless
case 42: //curse
case 43: //bloodlust
case 44: //precision
case 45: //weakness
case 46: //stone skin
case 47: //disrupting ray
case 48: //prayer
case 49: //mirth
case 50: //sorrow
case 51: //fortune
case 52: //misfortune
case 53: //haste
case 54: //slow
case 55: //slayer
case 56: //frenzy
case 58: //counterstrike
case 59: //berserk
case 60: //hypnotize
case 61: //forgetfulness
case 62: //blind
{
SetStackEffect sse;
for(std::set<CStack*>::iterator it = attackedCres.begin(); it != attackedCres.end(); ++it)
{
if(vstd::contains(sc.resisted, (*it)->ID)) //this creature resisted the spell
continue;
sse.stacks.push_back((*it)->ID);
}
sse.effect.id = ba.additionalInfo;
sse.effect.level = h->getSpellSchoolLevel(s);
sse.effect.turnsRemain = BattleInfo::calculateSpellDuration(s, h);
if(!sse.stacks.empty())
sendAndApply(&sse);
break;
}
case 37: //cure
case 38: //resurrection
case 39: //animate dead
{
StacksHealedOrResurrected shr;
for(std::set<CStack*>::iterator it = attackedCres.begin(); it != attackedCres.end(); ++it)
{
if(vstd::contains(sc.resisted, (*it)->ID) //this creature resisted the spell
|| (s->id == 39 && !(*it)->hasFeatureOfType(StackFeature::UNDEAD)) //we try to cast animate dead on living stack
)
continue;
StacksHealedOrResurrected::HealInfo hi;
hi.stackID = (*it)->ID;
hi.healedHP = calculateHealedHP(h, s, *it);
hi.lowLevelResurrection = h->getSpellSchoolLevel(s) <= 1;
shr.healedStacks.push_back(hi);
}
if(!shr.healedStacks.empty())
sendAndApply(&shr);
break;
}
case 64: //remove obstacle
{
ObstaclesRemoved obr;
for(int g=0; g<gs->curB->obstacles.size(); ++g)
{
std::vector<int> blockedHexes = VLC->heroh->obstacles[gs->curB->obstacles[g].ID].getBlocked(gs->curB->obstacles[g].pos);
if(vstd::contains(blockedHexes, ba.destinationTile)) //this obstacle covers given hex
{
obr.obstacles.insert(gs->curB->obstacles[g].uniqueID);
}
}
if(!obr.obstacles.empty())
sendAndApply(&obr);
break;
}
}
sendAndApply(&EndAction());
if( !gs->curB->getStack(gs->curB->activeStack, false)->alive() )
{
@@ -3496,6 +3513,7 @@ bool CGameHandler::makeCustomAction( BattleAction &ba )
{
endBattle(gs->curB->tile, gs->curB->heroes[0], gs->curB->heroes[1]);
}
return true;
}
}
@@ -3904,4 +3922,42 @@ bool CGameHandler::dig( const CGHeroInstance *h )
no.subID = 0;
sendAndApply(&no);
return true;
}
}
void CGameHandler::handleAfterAttackCasting( const BattleAttack & bat )
{
const CStack * attacker = gs->curB->getStack(bat.stackAttacking);
if( attacker->hasFeatureOfType(StackFeature::SPELL_AFTER_ATTACK) )
{
for (int it=0; it<attacker->features.size(); ++it)
{
const StackFeature & sf = attacker->features[it];
if (sf.type == StackFeature::SPELL_AFTER_ATTACK)
{
const CStack * oneOfAttacked = NULL;
for(int g=0; g<bat.bsa.size(); ++g)
{
if (bat.bsa[g].newAmount > 0)
{
oneOfAttacked = gs->curB->getStack(bat.bsa[g].stackAttacked);
break;
}
}
if(oneOfAttacked == NULL) //all attacked creatures have been killed
return;
int spellID = sf.subtype;
int spellLevel = sf.value;
int chance = sf.additionalInfo % 1000;
int meleeRanged = sf.additionalInfo / 1000;
int destination = oneOfAttacked->position;
//check if spell should be casted (probability handling)
if( rand()%100 >= chance )
continue;
//casting
handleSpellCasting(spellID, spellLevel, destination, !attacker->attackerOwned, attacker->owner, NULL, NULL);
}
}
}
}