1
0
mirror of https://github.com/vcmi/vcmi.git synced 2025-01-24 03:47:18 +02:00

Implemented Dragon Breath, Attack All Around & Three-Headed Attack.

Dragon Breath does not yet work correctly for all double-wide stacks, though.
This commit is contained in:
DjWarmonger 2011-07-04 19:34:49 +00:00
parent aac1c0b4e7
commit e95ae22061
6 changed files with 177 additions and 88 deletions

View File

@ -300,7 +300,6 @@ struct THex
{
h & hex;
}
private:
static void checkAndPush(int tile, std::vector<THex> & ret)
{
if( tile>=0 && tile<BFIELD_SIZE && (tile%BFIELD_WIDTH != (BFIELD_WIDTH - 1)) && (tile%BFIELD_WIDTH != 0) )

View File

@ -713,7 +713,7 @@ std::set<CStack*> BattleInfo::getAttackedCreatures( const CSpell * s, int skillL
{
if (s->id == 76) //Death Cloud //TODO: fireball and fire immunity
{
if (st->isLiving() || st->position == destinationTile) //directly hit or alive
if (st->isLiving() || st->coversPos(destinationTile)) //directly hit or alive
{
attackedCres.insert(st);
}
@ -763,6 +763,79 @@ std::set<CStack*> BattleInfo::getAttackedCreatures( const CSpell * s, int skillL
}
return attackedCres;
}
std::set<CStack*> BattleInfo::getAttackedCreatures(const CStack* attacker, THex destinationTile)
{ //TODO: caching?
std::set<CStack*> attackedCres;
const int WN = BFIELD_WIDTH;
if (attacker->hasBonusOfType(Bonus::ATTACKS_ALL_ADJACENT))
{
std::vector<THex> hexes = attacker->getSurroundingHexes();
BOOST_FOREACH (THex tile, hexes)
{
CStack * st = getStackT(tile);
if(st && st->owner != attacker->owner) //only hostile stacks - does it work well with Berserk?
{
attackedCres.insert(st);
}
}
}
ui16 hex = attacker->position.hex;
if (attacker->hasBonusOfType(Bonus::THREE_HEADED_ATTACK))
{
std::vector<THex> hexes;
if (attacker->attackerOwned)
{
THex::checkAndPush(hex - ( (hex/WN)%2 ? WN+1 : WN ), hexes);
THex::checkAndPush(hex + 1, hexes);
THex::checkAndPush(hex + ( (hex/WN)%2 ? WN : WN+1 ), hexes);
}
else
{
THex::checkAndPush(hex - ( (hex/WN)%2 ? WN : WN-1 ), hexes);
THex::checkAndPush(hex - 1, hexes);
THex::checkAndPush(hex - ( (hex/WN)%2 ? WN-1 : WN ), hexes);
}
BOOST_FOREACH (THex tile, hexes)
{
CStack * st = getStackT(tile);
if(st && st->owner != attacker->owner)
{
attackedCres.insert(st);
}
}
}
if (attacker->hasBonusOfType(Bonus::TWO_HEX_ATTACK_BREATH))
{
std::vector<THex> hexes; //only one, in fact
int pseudoVector = destinationTile.hex - hex;
switch (pseudoVector)
{
case 1:
case -1:
THex::checkAndPush(destinationTile.hex + pseudoVector, hexes);
break;
case WN: //17
case WN + 1: //18
case -WN: //-17
case -WN + 1: //-16
THex::checkAndPush(destinationTile.hex + pseudoVector + ((hex/WN)%2 ? 1 : -1 ), hexes);
break;
case WN-1: //16
case -WN-1: //-18
THex::checkAndPush(destinationTile.hex + pseudoVector + ((hex/WN)%2 ? 0 : -1), hexes);
break;
}
BOOST_FOREACH (THex tile, hexes)
{
CStack * st = getStackT(tile);
if(st) //friendly stacks can also be damaged by Dragon Breath
{
attackedCres.insert(st);
}
}
}
return attackedCres;
}
int BattleInfo::calculateSpellDuration( const CSpell * spell, const CGHeroInstance * caster, int usedSpellPower )
{
@ -2292,6 +2365,42 @@ bool CStack::coversPos(THex pos) const
return vstd::contains(getHexes(), pos);
}
std::vector<THex> CStack::getSurroundingHexes() const
{
std::vector<THex> hexes;
if (doubleWide())
{
const int WN = BFIELD_WIDTH;
if(attackerOwned)
{ //position is equal to front hex
THex::checkAndPush(position.hex - ( (position.hex/WN)%2 ? WN+1 : WN ), hexes);
THex::checkAndPush(position.hex - ( (position.hex/WN)%2 ? WN : WN-1 ), hexes);
THex::checkAndPush(position.hex - ( (position.hex/WN)%2 ? WN-1 : WN-2 ), hexes);
THex::checkAndPush(position.hex - 2, hexes);
THex::checkAndPush(position.hex + 1, hexes);
THex::checkAndPush(position.hex - ( (position.hex/WN)%2 ? WN-2 : WN-1 ), hexes);
THex::checkAndPush(position.hex + ( (position.hex/WN)%2 ? WN-1 : WN ), hexes);
THex::checkAndPush(position.hex + ( (position.hex/WN)%2 ? WN : WN+1 ), hexes);
}
else
{
THex::checkAndPush(position.hex - ( (position.hex/WN)%2 ? WN+2 : WN+1 ), hexes);
THex::checkAndPush(position.hex - ( (position.hex/WN)%2 ? WN+1 : WN ), hexes);
THex::checkAndPush(position.hex - ( (position.hex/WN)%2 ? WN : WN-1 ), hexes);
THex::checkAndPush(position.hex + 2, hexes);
THex::checkAndPush(position.hex - 1, hexes);
THex::checkAndPush(position.hex - ( (position.hex/WN)%2 ? WN-1 : WN ), hexes);
THex::checkAndPush(position.hex + ( (position.hex/WN)%2 ? WN : WN+1 ), hexes);
THex::checkAndPush(position.hex + ( (position.hex/WN)%2 ? WN+1 : WN+2 ), hexes);
}
return hexes;
}
else
{
return position.neighbouringTiles();
}
}
std::vector<si32> CStack::activeSpells() const
{
std::vector<si32> ret;

View File

@ -88,6 +88,7 @@ struct DLL_EXPORT BattleInfo : public CBonusSystemNode
TDmgRange calculateDmgRange(const CStack* attacker, const CStack* defender, const CGHeroInstance * attackerHero, const CGHeroInstance * defendingHero, bool shooting, ui8 charge, bool lucky, bool ballistaDoubleDmg) const; //charge - number of hexes travelled before attack (for champion's jousting); returns pair <min dmg, max dmg>
void calculateCasualties(std::map<ui32,si32> *casualties) const; //casualties are array of maps size 2 (attacker, defeneder), maps are (crid => amount)
std::set<CStack*> getAttackedCreatures(const CSpell * s, int skillLevel, ui8 attackerOwner, THex destinationTile); //calculates stack affected by given spell
std::set<CStack*> getAttackedCreatures(const CStack* attacker, THex destinationTile); //calculates range of multi-hex attacks
static int calculateSpellDuration(const CSpell * spell, const CGHeroInstance * caster, int usedSpellPower);
CStack * generateNewStack(const CStackInstance &base, int stackID, bool attackerOwned, int slot, THex position) const; //helper for CGameHandler::setupBattle and spells addign new stacks to the battlefield
CStack * generateNewStack(const CStackBasicDescriptor &base, int stackID, bool attackerOwned, int slot, THex position) const; //helper for CGameHandler::setupBattle and spells addign new stacks to the battlefield
@ -185,6 +186,7 @@ public:
THex occupiedHex() const; //returns number of occupied hex (not the position) if stack is double wide; otherwise -1
std::vector<THex> getHexes() const; //up to two occupied hexes, starting from front
bool coversPos(THex position) const; //checks also if unit is double-wide
std::vector<THex> getSurroundingHexes() const; // get six or 8 surrounding hexes depending on creature size
void prepareAttacked(BattleStackAttacked &bsa) const; //requires bsa.damageAmout filled

View File

@ -1304,7 +1304,7 @@ struct BattleAttack : public CPackForClient//3006
std::vector<BattleStackAttacked> bsa;
ui32 stackAttacking;
ui8 flags; //usues Eflags (below)
ui8 flags; //uses Eflags (below)
enum EFlags{SHOT = 1, COUNTER = 2, LUCKY = 4, UNLUCKY = 8, BALLISTA_DOUBLE_DMG = 16};
bool shot() const//distance attack - decrease number of shots

View File

@ -508,10 +508,6 @@ void CGameHandler::prepareAttack(BattleAttack &bat, const CStack *att, const CSt
{
bat.bsa.clear();
bat.stackAttacking = att->ID;
bat.bsa.push_back(BattleStackAttacked());
BattleStackAttacked *bsa = &bat.bsa.back();
bsa->stackAttacked = def->ID;
bsa->attackerID = att->ID;
int attackerLuck = att->LuckVal();
const CGHeroInstance * h0 = gs->curB->heroes[0],
* h1 = gs->curB->heroes[1];
@ -537,19 +533,29 @@ void CGameHandler::prepareAttack(BattleAttack &bat, const CStack *att, const CSt
bat.flags |= BattleAttack::BALLISTA_DOUBLE_DMG;
}
}
// only primary target
applyBattleEffects(bat, att, def, distance, false);
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);
//TODO: multiple attack targets
const Bonus * bonus = att->getBonus(Selector::type(Bonus::SPELL_LIKE_ATTACK));
if (bonus)
if (!bat.shot()) //multiple-hex attack - only in meele
{
bsa->flags |= BattleStackAttacked::EFFECT;
bsa->effect = VLC->spellh->spells[bonus->subtype]->mainEffectAnim; //hopefully it does not interfere with any other effect?
std::set<CStack*> attackedCreatures = gs->curB->getAttackedCreatures(att, def->position);
//TODO: get exact attacked hex for defender
BOOST_FOREACH(CStack * stack, attackedCreatures)
{
if (stack != def) //do not hit same stack twice
{
applyBattleEffects(bat, att, stack, distance, true);
}
}
}
const Bonus * bonus = att->getBonus(Selector::type(Bonus::SPELL_LIKE_ATTACK));
if (bonus && (bat.shot())) //TODO: make it work in meele?
{
bat.bsa.front().flags |= BattleStackAttacked::EFFECT;
bat.bsa.front().effect = VLC->spellh->spells[bonus->subtype]->mainEffectAnim; //hopefully it does not interfere with any other effect?
BattleStackAttacked bss = *bsa; // copy some parameters, such as attacker
std::set<CStack*> attackedCreatures = gs->curB->getAttackedCreatures(VLC->spellh->spells[bonus->subtype], bonus->val, att->owner, def->position);
//TODO: get exact attacked hex for defender
@ -557,82 +563,54 @@ void CGameHandler::prepareAttack(BattleAttack &bat, const CStack *att, const CSt
{
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<int>(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);
}
applyBattleEffects(bat, att, stack, distance, true);
}
}
}
else // only one target
{ // TODO: move duplicated code to separate function
//life drain handling
if (att->hasBonusOfType(Bonus::LIFE_DRAIN) && def->isLiving())
}
void CGameHandler::applyBattleEffects(BattleAttack &bat, const CStack *att, const CStack *def, int distance, bool secondary) //helper function for prepareAttack
{
BattleStackAttacked bsa;
if (secondary)
bsa.flags |= BattleStackAttacked::SECONDARY; //all other targets do not suffer from spells & spell-like abilities
bsa.attackerID = att->ID;
bsa.stackAttacked = def->ID;
bsa.damageAmount = gs->curB->calculateDmg(att, def, gs->curB->battleGetOwner(att), gs->curB->battleGetOwner(def), bat.shot(), distance, bat.lucky(), bat.ballistaDoubleDmg());
def->prepareAttacked(bsa); //calculate casualties
bat.bsa.push_back(bsa); //add this stack to the list of victims
//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;
StacksHealedOrResurrected::HealInfo hi;
hi.stackID = att->ID;
hi.healedHP = std::min<int>(bsa.damageAmount, att->MaxHealth() - att->firstHPleft + att->MaxHealth() * (att->baseAmount - att->count) );
hi.lowLevelResurrection = false;
shi.healedStacks.push_back(hi);
if (hi.healedHP > 0)
{
StacksHealedOrResurrected shi;
shi.lifeDrain = (ui8)true;
shi.tentHealing = (ui8)false;
shi.drainedFrom = def->ID;
StacksHealedOrResurrected::HealInfo hi;
hi.stackID = att->ID;
hi.healedHP = std::min<int>(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() && 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);
bsa.healedStacks.push_back(shi);
}
}
//fire shield handling
if (!bat.shot() && def->hasBonusOfType(Bonus::FIRE_SHIELD) && !att->hasBonusOfType (Bonus::FIRE_IMMUNITY) && !bsa.killed() )
{
BattleStackAttacked bsa;
bsa.stackAttacked = att->ID; //invert
bsa.attackerID = def->ID;
bsa.flags |= BattleStackAttacked::EFFECT;
bsa.effect = 11;
bsa.damageAmount = (bsa.damageAmount * def->valOfBonuses(Bonus::FIRE_SHIELD)) / 100;
att->prepareAttacked(bsa);
bat.bsa.push_back(bsa);
}
}
void CGameHandler::handleConnection(std::set<int> players, CConnection &c)

View File

@ -118,6 +118,7 @@ public:
//
void endBattle(int3 tile, const CGHeroInstance *hero1, const CGHeroInstance *hero2); //ends battle
void prepareAttack(BattleAttack &bat, const CStack *att, const CStack *def, int distance); //distance - number of hexes travelled before attacking
void applyBattleEffects(BattleAttack &bat, const CStack *att, const CStack *def, int distance, bool secondary); //damage, drain life & fire shield
void checkForBattleEnd( std::vector<CStack*> &stacks );
void setupBattle(int3 tile, const CArmedInstance *armies[2], const CGHeroInstance *heroes[2], bool creatureBank, const CGTownInstance *town);
void setBattleResult(int resultType, int victoriusSide);