mirror of
https://github.com/vcmi/vcmi.git
synced 2024-12-24 22:14:36 +02:00
Merge pull request #371 from dydzio0614/NewAbilities
Tested and approved. Congrats!
This commit is contained in:
commit
c2a0b13332
@ -13,6 +13,12 @@ GENERAL:
|
|||||||
- BLOCKS_RANGED_RETALIATION - disallow enemy ranged counterattack
|
- BLOCKS_RANGED_RETALIATION - disallow enemy ranged counterattack
|
||||||
- SECONDARY_SKILL_VAL2 - set additional parameter for certain secondary skills
|
- SECONDARY_SKILL_VAL2 - set additional parameter for certain secondary skills
|
||||||
- MANUAL_CONTROL - grant manual control over war machine
|
- MANUAL_CONTROL - grant manual control over war machine
|
||||||
|
- WIDE_BREATH - melee creature attacks affect many nearby hexes
|
||||||
|
- FIRST_STRIKE - creature counterattacks before attack if possible
|
||||||
|
- SYNERGY_TARGET - placeholder bonus for Mod Design Team (subject to removal in future)
|
||||||
|
- SHOOTS_ALL_ADJACENT - makes creature shots affect all neighbouring hexes
|
||||||
|
- BLOCK_MAGIC_BELOW - allows blocking spells below particular spell level. HotA cape artifact can be implemented with this
|
||||||
|
- DESTRUCTION - creature ability for killing extra units after hit, configurable
|
||||||
|
|
||||||
SPELLS:
|
SPELLS:
|
||||||
* Implemented cumulative effects for spells
|
* Implemented cumulative effects for spells
|
||||||
|
@ -96,6 +96,12 @@
|
|||||||
"description": "+${val} Defense when defending"
|
"description": "+${val} Defense when defending"
|
||||||
},
|
},
|
||||||
|
|
||||||
|
"DESTRUCTION":
|
||||||
|
{
|
||||||
|
"name": "Destruction",
|
||||||
|
"description": "Has ${val}% chance to kill extra units after attack"
|
||||||
|
},
|
||||||
|
|
||||||
"DOUBLE_DAMAGE_CHANCE":
|
"DOUBLE_DAMAGE_CHANCE":
|
||||||
{
|
{
|
||||||
"name": "Death Blow",
|
"name": "Death Blow",
|
||||||
@ -156,6 +162,12 @@
|
|||||||
"description": "Reflects part of melee damage"
|
"description": "Reflects part of melee damage"
|
||||||
},
|
},
|
||||||
|
|
||||||
|
"FIRST_STRIKE":
|
||||||
|
{
|
||||||
|
"name": "First Strike",
|
||||||
|
"description": "This creature attacks first instead of retaliating"
|
||||||
|
},
|
||||||
|
|
||||||
"FEAR":
|
"FEAR":
|
||||||
{
|
{
|
||||||
"name": "Fear",
|
"name": "Fear",
|
||||||
@ -347,6 +359,12 @@
|
|||||||
"name": "Ranged",
|
"name": "Ranged",
|
||||||
"description": "Creature can shoot"
|
"description": "Creature can shoot"
|
||||||
},
|
},
|
||||||
|
|
||||||
|
"SHOOTS_ALL_ADJACENT":
|
||||||
|
{
|
||||||
|
"name": "Shoot all around",
|
||||||
|
"description": "This creature's ranged attacks strike all targets in a small area"
|
||||||
|
},
|
||||||
|
|
||||||
"SOUL_STEAL":
|
"SOUL_STEAL":
|
||||||
{
|
{
|
||||||
@ -395,13 +413,19 @@
|
|||||||
"name": "Aura of Resistance",
|
"name": "Aura of Resistance",
|
||||||
"description": "Nearby stacks get ${val}% resistance"
|
"description": "Nearby stacks get ${val}% resistance"
|
||||||
},
|
},
|
||||||
|
|
||||||
"SUMMON_GUARDIANS":
|
"SUMMON_GUARDIANS":
|
||||||
{
|
{
|
||||||
"name": "Summon guardians",
|
"name": "Summon guardians",
|
||||||
"description": "At battle start summons ${subtype.creature} (${val}%)"
|
"description": "At battle start summons ${subtype.creature} (${val}%)"
|
||||||
},
|
},
|
||||||
|
|
||||||
|
"SYNERGY_TARGET":
|
||||||
|
{
|
||||||
|
"name": "Synergizable",
|
||||||
|
"description": "This creature is vulnerable to synergy effect"
|
||||||
|
},
|
||||||
|
|
||||||
"TWO_HEX_ATTACK_BREATH":
|
"TWO_HEX_ATTACK_BREATH":
|
||||||
{
|
{
|
||||||
"name": "Breath",
|
"name": "Breath",
|
||||||
@ -436,5 +460,11 @@
|
|||||||
{
|
{
|
||||||
"name": "Water immunity",
|
"name": "Water immunity",
|
||||||
"description": "Immune to all Water school spells"
|
"description": "Immune to all Water school spells"
|
||||||
|
},
|
||||||
|
|
||||||
|
"WIDE_BREATH":
|
||||||
|
{
|
||||||
|
"name": "Wide breath",
|
||||||
|
"description": "Wide breath attack (multiple hexes)"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -235,8 +235,15 @@ private:
|
|||||||
BONUS_NAME(CATAPULT_EXTRA_SHOTS) /*val - number of additional shots, requires CATAPULT bonus to work*/\
|
BONUS_NAME(CATAPULT_EXTRA_SHOTS) /*val - number of additional shots, requires CATAPULT bonus to work*/\
|
||||||
BONUS_NAME(RANGED_RETALIATION) /*allows shooters to perform ranged retaliation*/\
|
BONUS_NAME(RANGED_RETALIATION) /*allows shooters to perform ranged retaliation*/\
|
||||||
BONUS_NAME(BLOCKS_RANGED_RETALIATION) /*disallows ranged retaliation for shooter unit, BLOCKS_RETALIATION bonus is for melee retaliation only*/\
|
BONUS_NAME(BLOCKS_RANGED_RETALIATION) /*disallows ranged retaliation for shooter unit, BLOCKS_RETALIATION bonus is for melee retaliation only*/\
|
||||||
BONUS_NAME(SECONDARY_SKILL_VAL2) /*for secondary skills that have multiple effects, like eagle eye (max level and chance)*/ \
|
BONUS_NAME(SECONDARY_SKILL_VAL2) /*for secondary skills that have multiple effects, like eagle eye (max level and chance)*/ \
|
||||||
BONUS_NAME(MANUAL_CONTROL) /* manually control warmachine with id = subtype, chance = val */ \
|
BONUS_NAME(MANUAL_CONTROL) /* manually control warmachine with id = subtype, chance = val */ \
|
||||||
|
BONUS_NAME(WIDE_BREATH) /* initial desigh: dragon breath affecting multiple nearby hexes */\
|
||||||
|
BONUS_NAME(FIRST_STRIKE) /* first counterattack, then attack if possible */\
|
||||||
|
BONUS_NAME(SYNERGY_TARGET) /* dummy skill for alternative upgrades mod */\
|
||||||
|
BONUS_NAME(SHOOTS_ALL_ADJACENT) /* H4 Cyclops-like shoot (attacks all hexes neighboring with target) without spell-like mechanics */\
|
||||||
|
BONUS_NAME(BLOCK_MAGIC_BELOW) /*blocks casting spells of the level < value */ \
|
||||||
|
BONUS_NAME(DESTRUCTION) /*kills extra units after hit, subtype = 0 - kill percentage of units, 1 - kill amount, val = chance in percent to trigger, additional info - amount/percentage to kill*/ \
|
||||||
|
|
||||||
/* end of list */
|
/* end of list */
|
||||||
|
|
||||||
|
|
||||||
|
@ -1155,21 +1155,21 @@ AttackableTiles CBattleInfoCallback::getPotentiallyAttackableHexes (const CStack
|
|||||||
BattleHex hex = (attackerPos != BattleHex::INVALID) ? attackerPos.hex : attacker->position.hex; //real or hypothetical (cursor) position
|
BattleHex hex = (attackerPos != BattleHex::INVALID) ? attackerPos.hex : attacker->position.hex; //real or hypothetical (cursor) position
|
||||||
|
|
||||||
//FIXME: dragons or cerbers can rotate before attack, making their base hex different (#1124)
|
//FIXME: dragons or cerbers can rotate before attack, making their base hex different (#1124)
|
||||||
bool reverse = isToReverse (hex, destinationTile, isAttacker, attacker->doubleWide(), isAttacker);
|
bool reverse = isToReverse(hex, destinationTile, isAttacker, attacker->doubleWide(), isAttacker);
|
||||||
if (reverse && attacker->doubleWide())
|
if(reverse && attacker->doubleWide())
|
||||||
{
|
{
|
||||||
hex = attacker->occupiedHex(hex); //the other hex stack stands on
|
hex = attacker->occupiedHex(hex); //the other hex stack stands on
|
||||||
}
|
}
|
||||||
if (attacker->hasBonusOfType(Bonus::ATTACKS_ALL_ADJACENT))
|
if(attacker->hasBonusOfType(Bonus::ATTACKS_ALL_ADJACENT))
|
||||||
{
|
{
|
||||||
boost::copy (attacker->getSurroundingHexes (attackerPos), vstd::set_inserter (at.hostileCreaturePositions));
|
boost::copy(attacker->getSurroundingHexes(attackerPos), vstd::set_inserter(at.hostileCreaturePositions));
|
||||||
}
|
}
|
||||||
if (attacker->hasBonusOfType(Bonus::THREE_HEADED_ATTACK))
|
if(attacker->hasBonusOfType(Bonus::THREE_HEADED_ATTACK))
|
||||||
{
|
{
|
||||||
std::vector<BattleHex> hexes = attacker->getSurroundingHexes(attackerPos);
|
std::vector<BattleHex> hexes = attacker->getSurroundingHexes(attackerPos);
|
||||||
for (BattleHex tile : hexes)
|
for(BattleHex tile : hexes)
|
||||||
{
|
{
|
||||||
if ((BattleHex::mutualPosition(tile, destinationTile) > -1 && BattleHex::mutualPosition (tile, hex) > -1)) //adjacent both to attacker's head and attacked tile
|
if((BattleHex::mutualPosition(tile, destinationTile) > -1 && BattleHex::mutualPosition(tile, hex) > -1)) //adjacent both to attacker's head and attacked tile
|
||||||
{
|
{
|
||||||
const CStack * st = battleGetStackByPos(tile, true);
|
const CStack * st = battleGetStackByPos(tile, true);
|
||||||
if(st && st->owner != attacker->owner) //only hostile stacks - does it work well with Berserk?
|
if(st && st->owner != attacker->owner) //only hostile stacks - does it work well with Berserk?
|
||||||
@ -1179,44 +1179,86 @@ AttackableTiles CBattleInfoCallback::getPotentiallyAttackableHexes (const CStack
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (attacker->hasBonusOfType(Bonus::TWO_HEX_ATTACK_BREATH) && BattleHex::mutualPosition (destinationTile.hex, hex) > -1) //only adjacent hexes are subject of dragon breath calculation
|
if(attacker->hasBonusOfType(Bonus::WIDE_BREATH))
|
||||||
|
{
|
||||||
|
std::vector<BattleHex> hexes = destinationTile.neighbouringTiles();
|
||||||
|
for(int i = 0; i<hexes.size(); i++)
|
||||||
|
{
|
||||||
|
if(hexes.at(i) == hex)
|
||||||
|
{
|
||||||
|
hexes.erase(hexes.begin() + i);
|
||||||
|
i = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for(BattleHex tile : hexes)
|
||||||
|
{
|
||||||
|
//friendly stacks can also be damaged by Dragon Breath
|
||||||
|
if(battleGetStackByPos(tile, true))
|
||||||
|
{
|
||||||
|
if(battleGetStackByPos(tile, true) != attacker)
|
||||||
|
at.friendlyCreaturePositions.insert(tile);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if(attacker->hasBonusOfType(Bonus::TWO_HEX_ATTACK_BREATH) && BattleHex::mutualPosition(destinationTile.hex, hex) > -1) //only adjacent hexes are subject of dragon breath calculation
|
||||||
{
|
{
|
||||||
std::vector<BattleHex> hexes; //only one, in fact
|
std::vector<BattleHex> hexes; //only one, in fact
|
||||||
int pseudoVector = destinationTile.hex - hex;
|
int pseudoVector = destinationTile.hex - hex;
|
||||||
switch (pseudoVector)
|
switch(pseudoVector)
|
||||||
{
|
{
|
||||||
case 1:
|
case 1:
|
||||||
case -1:
|
case -1:
|
||||||
BattleHex::checkAndPush (destinationTile.hex + pseudoVector, hexes);
|
BattleHex::checkAndPush(destinationTile.hex + pseudoVector, hexes);
|
||||||
break;
|
break;
|
||||||
case WN: //17 //left-down or right-down
|
case WN: //17 //left-down or right-down
|
||||||
case -WN: //-17 //left-up or right-up
|
case -WN: //-17 //left-up or right-up
|
||||||
case WN + 1: //18 //right-down
|
case WN + 1: //18 //right-down
|
||||||
case -WN + 1: //-16 //right-up
|
case -WN + 1: //-16 //right-up
|
||||||
BattleHex::checkAndPush (destinationTile.hex + pseudoVector + (((hex/WN)%2) ? 1 : -1), hexes);
|
BattleHex::checkAndPush(destinationTile.hex + pseudoVector + (((hex / WN) % 2) ? 1 : -1), hexes);
|
||||||
break;
|
break;
|
||||||
case WN-1: //16 //left-down
|
case WN - 1: //16 //left-down
|
||||||
case -WN-1: //-18 //left-up
|
case -WN - 1: //-18 //left-up
|
||||||
BattleHex::checkAndPush (destinationTile.hex + pseudoVector + (((hex/WN)%2) ? 1 : 0), hexes);
|
BattleHex::checkAndPush(destinationTile.hex + pseudoVector + (((hex / WN) % 2) ? 1 : 0), hexes);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
for (BattleHex tile : hexes)
|
for(BattleHex tile : hexes)
|
||||||
{
|
{
|
||||||
//friendly stacks can also be damaged by Dragon Breath
|
//friendly stacks can also be damaged by Dragon Breath
|
||||||
if (battleGetStackByPos (tile, true))
|
if(battleGetStackByPos(tile, true))
|
||||||
at.friendlyCreaturePositions.insert (tile);
|
at.friendlyCreaturePositions.insert(tile);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return at;
|
||||||
|
}
|
||||||
|
|
||||||
|
AttackableTiles CBattleInfoCallback::getPotentiallyShootableHexes(const CStack * attacker, BattleHex destinationTile, BattleHex attackerPos) const
|
||||||
|
{
|
||||||
|
//does not return hex attacked directly
|
||||||
|
AttackableTiles at;
|
||||||
|
RETURN_IF_NOT_BATTLE(at);
|
||||||
|
|
||||||
|
if(attacker->hasBonusOfType(Bonus::SHOOTS_ALL_ADJACENT) && !vstd::contains(attackerPos.neighbouringTiles(), destinationTile))
|
||||||
|
{
|
||||||
|
std::vector<BattleHex> targetHexes = destinationTile.neighbouringTiles();
|
||||||
|
targetHexes.push_back(destinationTile);
|
||||||
|
boost::copy(targetHexes, vstd::set_inserter(at.hostileCreaturePositions));
|
||||||
|
}
|
||||||
|
|
||||||
return at;
|
return at;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::set<const CStack*> CBattleInfoCallback::getAttackedCreatures(const CStack* attacker, BattleHex destinationTile, BattleHex attackerPos) const
|
std::set<const CStack*> CBattleInfoCallback::getAttackedCreatures(const CStack* attacker, BattleHex destinationTile, bool rangedAttack, BattleHex attackerPos) const
|
||||||
{
|
{
|
||||||
std::set<const CStack*> attackedCres;
|
std::set<const CStack*> attackedCres;
|
||||||
RETURN_IF_NOT_BATTLE(attackedCres);
|
RETURN_IF_NOT_BATTLE(attackedCres);
|
||||||
|
|
||||||
AttackableTiles at = getPotentiallyAttackableHexes(attacker, destinationTile, attackerPos);
|
AttackableTiles at;
|
||||||
|
|
||||||
|
if(rangedAttack)
|
||||||
|
at = getPotentiallyShootableHexes(attacker, destinationTile, attackerPos);
|
||||||
|
else
|
||||||
|
at = getPotentiallyAttackableHexes(attacker, destinationTile, attackerPos);
|
||||||
|
|
||||||
for (BattleHex tile : at.hostileCreaturePositions) //all around & three-headed attack
|
for (BattleHex tile : at.hostileCreaturePositions) //all around & three-headed attack
|
||||||
{
|
{
|
||||||
const CStack * st = battleGetStackByPos(tile, true);
|
const CStack * st = battleGetStackByPos(tile, true);
|
||||||
@ -1628,6 +1670,24 @@ int CBattleInfoCallback::battleGetSurrenderCost(PlayerColor Player) const
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
si8 CBattleInfoCallback::battleMinSpellLevel(ui8 side) const
|
||||||
|
{
|
||||||
|
const IBonusBearer * node = nullptr;
|
||||||
|
if(const CGHeroInstance * h = battleGetFightingHero(side))
|
||||||
|
node = h;
|
||||||
|
else
|
||||||
|
node = getBattleNode();
|
||||||
|
|
||||||
|
if(!node)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
auto b = node->getBonuses(Selector::type(Bonus::BLOCK_MAGIC_BELOW));
|
||||||
|
if(b->size())
|
||||||
|
return b->totalValue();
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
si8 CBattleInfoCallback::battleMaxSpellLevel(ui8 side) const
|
si8 CBattleInfoCallback::battleMaxSpellLevel(ui8 side) const
|
||||||
{
|
{
|
||||||
const IBonusBearer *node = nullptr;
|
const IBonusBearer *node = nullptr;
|
||||||
|
@ -75,6 +75,7 @@ public:
|
|||||||
std::vector<BattleHex> getAttackableBattleHexes() const;
|
std::vector<BattleHex> getAttackableBattleHexes() const;
|
||||||
|
|
||||||
//*** MAGIC
|
//*** MAGIC
|
||||||
|
si8 battleMinSpellLevel(ui8 side) const; //calculates maximum spell level possible to be cast on battlefield - takes into account artifacts of both heroes; if no effects are set, 0 is returned
|
||||||
si8 battleMaxSpellLevel(ui8 side) const; //calculates minimum spell level possible to be cast on battlefield - takes into account artifacts of both heroes; if no effects are set, 0 is returned
|
si8 battleMaxSpellLevel(ui8 side) const; //calculates minimum spell level possible to be cast on battlefield - takes into account artifacts of both heroes; if no effects are set, 0 is returned
|
||||||
ui32 battleGetSpellCost(const CSpell * sp, const CGHeroInstance * caster) const; //returns cost of given spell
|
ui32 battleGetSpellCost(const CSpell * sp, const CGHeroInstance * caster) const; //returns cost of given spell
|
||||||
ESpellCastProblem::ESpellCastProblem battleCanCastSpell(const ISpellCaster * caster, ECastingMode::ECastingMode mode) const; //returns true if there are no general issues preventing from casting a spell
|
ESpellCastProblem::ESpellCastProblem battleCanCastSpell(const ISpellCaster * caster, ECastingMode::ECastingMode mode) const; //returns true if there are no general issues preventing from casting a spell
|
||||||
@ -93,7 +94,8 @@ public:
|
|||||||
si8 battleGetTacticDist() const; //returns tactic distance for calling player or 0 if this player is not in tactic phase (for ALL_KNOWING actual distance for tactic side)
|
si8 battleGetTacticDist() const; //returns tactic distance for calling player or 0 if this player is not in tactic phase (for ALL_KNOWING actual distance for tactic side)
|
||||||
|
|
||||||
AttackableTiles getPotentiallyAttackableHexes(const CStack* attacker, BattleHex destinationTile, BattleHex attackerPos) const; //TODO: apply rotation to two-hex attacker
|
AttackableTiles getPotentiallyAttackableHexes(const CStack* attacker, BattleHex destinationTile, BattleHex attackerPos) const; //TODO: apply rotation to two-hex attacker
|
||||||
std::set<const CStack*> getAttackedCreatures(const CStack* attacker, BattleHex destinationTile, BattleHex attackerPos = BattleHex::INVALID) const; //calculates range of multi-hex attacks
|
AttackableTiles getPotentiallyShootableHexes(const CStack* attacker, BattleHex destinationTile, BattleHex attackerPos) const;
|
||||||
|
std::set<const CStack*> getAttackedCreatures(const CStack* attacker, BattleHex destinationTile, bool rangedAttack, BattleHex attackerPos = BattleHex::INVALID) const; //calculates range of multi-hex attacks
|
||||||
bool isToReverse(BattleHex hexFrom, BattleHex hexTo, bool curDir /*if true, creature is in attacker's direction*/, bool toDoubleWide, bool toDir) const; //determines if creature should be reversed (it stands on hexFrom and should 'see' hexTo)
|
bool isToReverse(BattleHex hexFrom, BattleHex hexTo, bool curDir /*if true, creature is in attacker's direction*/, bool toDoubleWide, bool toDir) const; //determines if creature should be reversed (it stands on hexFrom and should 'see' hexTo)
|
||||||
bool isToReverseHlp(BattleHex hexFrom, BattleHex hexTo, bool curDir) const; //helper for isToReverse
|
bool isToReverseHlp(BattleHex hexFrom, BattleHex hexTo, bool curDir) const; //helper for isToReverse
|
||||||
|
|
||||||
|
@ -194,7 +194,7 @@ ESpellCastProblem::ESpellCastProblem CSpell::canBeCast(const CBattleInfoCallback
|
|||||||
//effect like Recanter's Cloak. Blocks also passive casting.
|
//effect like Recanter's Cloak. Blocks also passive casting.
|
||||||
//TODO: check creature abilities to block
|
//TODO: check creature abilities to block
|
||||||
//TODO: check any possible caster
|
//TODO: check any possible caster
|
||||||
if(cb->battleMaxSpellLevel(side.get()) < level)
|
if(cb->battleMaxSpellLevel(side.get()) < level || cb->battleMinSpellLevel(side.get()) > level)
|
||||||
return ESpellCastProblem::SPELL_LEVEL_LIMIT_EXCEEDED;
|
return ESpellCastProblem::SPELL_LEVEL_LIMIT_EXCEEDED;
|
||||||
|
|
||||||
const ESpellCastProblem::ESpellCastProblem specificProblem = mechanics->canBeCast(cb, mode, caster);
|
const ESpellCastProblem::ESpellCastProblem specificProblem = mechanics->canBeCast(cb, mode, caster);
|
||||||
|
@ -890,16 +890,14 @@ void CGameHandler::prepareAttack(BattleAttack &bat, const CStack *att, const CSt
|
|||||||
// only primary target
|
// only primary target
|
||||||
applyBattleEffects(bat, att, def, distance, false);
|
applyBattleEffects(bat, att, def, distance, false);
|
||||||
|
|
||||||
if (!bat.shot()) //multiple-hex attack - only in meele
|
//multiple-hex normal attack
|
||||||
{
|
std::set<const CStack*> attackedCreatures = gs->curB->getAttackedCreatures(att, targetHex, bat.shot()); //creatures other than primary target
|
||||||
std::set<const CStack*> attackedCreatures = gs->curB->getAttackedCreatures(att, targetHex); //creatures other than primary target
|
|
||||||
|
|
||||||
for (const CStack * stack : attackedCreatures)
|
for (const CStack * stack : attackedCreatures)
|
||||||
|
{
|
||||||
|
if (stack != def) //do not hit same stack twice
|
||||||
{
|
{
|
||||||
if (stack != def) //do not hit same stack twice
|
applyBattleEffects(bat, att, stack, distance, true);
|
||||||
{
|
|
||||||
applyBattleEffects(bat, att, stack, distance, true);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -912,11 +910,11 @@ void CGameHandler::prepareAttack(BattleAttack &bat, const CStack *att, const CSt
|
|||||||
|
|
||||||
//TODO: should spell override creature`s projectile?
|
//TODO: should spell override creature`s projectile?
|
||||||
|
|
||||||
auto attackedCreatures = SpellID(bonus->subtype).toSpell()->getAffectedStacks(gs->curB, ECastingMode::SPELL_LIKE_ATTACK, att, bonus->val, targetHex);
|
auto affectedCreatures = SpellID(bonus->subtype).toSpell()->getAffectedStacks(gs->curB, ECastingMode::SPELL_LIKE_ATTACK, att, bonus->val, targetHex);
|
||||||
|
|
||||||
//TODO: get exact attacked hex for defender
|
//TODO: get exact attacked hex for defender
|
||||||
|
|
||||||
for (const CStack * stack : attackedCreatures)
|
for (const CStack * stack : affectedCreatures)
|
||||||
{
|
{
|
||||||
if (stack != def) //do not hit same stack twice
|
if (stack != def) //do not hit same stack twice
|
||||||
{
|
{
|
||||||
@ -3937,6 +3935,19 @@ bool CGameHandler::makeBattleAction(BattleAction &ba)
|
|||||||
|
|
||||||
for (int i = 0; i < totalAttacks; ++i)
|
for (int i = 0; i < totalAttacks; ++i)
|
||||||
{
|
{
|
||||||
|
//first strike
|
||||||
|
if(i == 0 && destinationStack
|
||||||
|
&& destinationStack->hasBonusOfType(Bonus::FIRST_STRIKE)
|
||||||
|
&& destinationStack->ableToRetaliate()
|
||||||
|
&& stack->alive()) //probably not needed
|
||||||
|
{
|
||||||
|
BattleAttack bat;
|
||||||
|
prepareAttack(bat, destinationStack, stack, 0, stack->position);
|
||||||
|
bat.flags |= BattleAttack::COUNTER;
|
||||||
|
sendAndApply(&bat);
|
||||||
|
handleAfterAttackCasting(bat);
|
||||||
|
}
|
||||||
|
|
||||||
if (stack &&
|
if (stack &&
|
||||||
stack->alive() && //move can cause death, eg. by walking into the moat
|
stack->alive() && //move can cause death, eg. by walking into the moat
|
||||||
destinationStack->alive())
|
destinationStack->alive())
|
||||||
@ -3952,6 +3963,7 @@ bool CGameHandler::makeBattleAction(BattleAction &ba)
|
|||||||
//counterattack
|
//counterattack
|
||||||
if (i == 0 && destinationStack
|
if (i == 0 && destinationStack
|
||||||
&& !stack->hasBonusOfType(Bonus::BLOCKS_RETALIATION)
|
&& !stack->hasBonusOfType(Bonus::BLOCKS_RETALIATION)
|
||||||
|
&& !destinationStack->hasBonusOfType(Bonus::FIRST_STRIKE)
|
||||||
&& destinationStack->ableToRetaliate()
|
&& destinationStack->ableToRetaliate()
|
||||||
&& stack->alive()) //attacker may have died (fire shield)
|
&& stack->alive()) //attacker may have died (fire shield)
|
||||||
{
|
{
|
||||||
@ -3999,6 +4011,7 @@ bool CGameHandler::makeBattleAction(BattleAction &ba)
|
|||||||
if (destinationStack->hasBonusOfType(Bonus::RANGED_RETALIATION)
|
if (destinationStack->hasBonusOfType(Bonus::RANGED_RETALIATION)
|
||||||
&& !stack->hasBonusOfType(Bonus::BLOCKS_RANGED_RETALIATION)
|
&& !stack->hasBonusOfType(Bonus::BLOCKS_RANGED_RETALIATION)
|
||||||
&& destinationStack->ableToRetaliate()
|
&& destinationStack->ableToRetaliate()
|
||||||
|
&& gs->curB->battleCanShoot(destinationStack, stack->position)
|
||||||
&& stack->alive()) //attacker may have died (fire shield)
|
&& stack->alive()) //attacker may have died (fire shield)
|
||||||
{
|
{
|
||||||
BattleAttack bat;
|
BattleAttack bat;
|
||||||
@ -5406,6 +5419,37 @@ void CGameHandler::handleAfterAttackCasting(const BattleAttack & bat)
|
|||||||
sendAndApply(&victimInfo);
|
sendAndApply(&victimInfo);
|
||||||
sendAndApply(&resurrectInfo);
|
sendAndApply(&resurrectInfo);
|
||||||
}
|
}
|
||||||
|
if(attacker->hasBonusOfType(Bonus::DESTRUCTION, 0) || attacker->hasBonusOfType(Bonus::DESTRUCTION, 1))
|
||||||
|
{
|
||||||
|
double chanceToTrigger = 0;
|
||||||
|
int amountToDie = 0;
|
||||||
|
|
||||||
|
if(attacker->hasBonusOfType(Bonus::DESTRUCTION, 0)) //killing by percentage
|
||||||
|
{
|
||||||
|
chanceToTrigger = attacker->valOfBonuses(Bonus::DESTRUCTION, 0) / 100.0f;
|
||||||
|
int percentageToDie = attacker->getBonus(Selector::type(Bonus::DESTRUCTION).And(Selector::subtype(0)))->additionalInfo;
|
||||||
|
amountToDie = defender->getCount() * percentageToDie * 0.01f;
|
||||||
|
}
|
||||||
|
else if(attacker->hasBonusOfType(Bonus::DESTRUCTION, 1)) //killing by count
|
||||||
|
{
|
||||||
|
chanceToTrigger = attacker->valOfBonuses(Bonus::DESTRUCTION, 1) / 100.0f;
|
||||||
|
amountToDie = attacker->getBonus(Selector::type(Bonus::DESTRUCTION).And(Selector::subtype(1)))->additionalInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
vstd::amin(chanceToTrigger, 1); //cap trigger chance at 100%
|
||||||
|
|
||||||
|
if(getRandomGenerator().getDoubleRange(0, 1)() > chanceToTrigger)
|
||||||
|
return;
|
||||||
|
|
||||||
|
BattleStackAttacked bsa;
|
||||||
|
bsa.attackerID = -1;
|
||||||
|
bsa.stackAttacked = defender->ID;
|
||||||
|
bsa.damageAmount = amountToDie * defender->getCreature()->MaxHealth();
|
||||||
|
bsa.flags = BattleStackAttacked::SPELL_EFFECT;
|
||||||
|
bsa.spellID = SpellID::SLAYER;
|
||||||
|
defender->prepareAttacked(bsa, getRandomGenerator());
|
||||||
|
sendAndApply(&bsa);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void CGameHandler::visitObjectOnTile(const TerrainTile &t, const CGHeroInstance * h)
|
void CGameHandler::visitObjectOnTile(const TerrainTile &t, const CGHeroInstance * h)
|
||||||
|
Loading…
Reference in New Issue
Block a user