1
0
mirror of https://github.com/vcmi/vcmi.git synced 2025-01-12 02:28:11 +02:00

Add SYNERGY_TARGET and [wip]-SHOOTS_ALL_ADJACENT

This commit is contained in:
Dydzio 2017-09-04 23:32:24 +02:00
parent 1df939bf70
commit 4cab76900f
5 changed files with 233 additions and 209 deletions

View File

@ -150,7 +150,8 @@
"description": "Immune to all Fire school spells" "description": "Immune to all Fire school spells"
}, },
"FIRE_SHIELD": { "FIRE_SHIELD":
{
"name": "Fire Shield (${val}%)", "name": "Fire Shield (${val}%)",
"description": "Reflects part of melee damage" "description": "Reflects part of melee damage"
}, },
@ -353,6 +354,12 @@
"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":
{ {
"name": "Soul Steal", "name": "Soul Steal",
@ -407,6 +414,12 @@
"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",

View File

@ -235,8 +235,10 @@ 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(WIDE_BREATH) /* Kuririn skill */\ BONUS_NAME(WIDE_BREATH) /* initial desigh: dragon breath affecting multiple nearby hexes */\
BONUS_NAME(FIRST_STRIKE) /* Witchking skill */\ 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 */\
/* end of list */ /* end of list */

View File

@ -1120,7 +1120,7 @@ ReachabilityInfo CBattleInfoCallback::getFlyingReachability(const ReachabilityIn
return ret; return ret;
} }
AttackableTiles CBattleInfoCallback::getPotentiallyAttackableHexes (const CStack* attacker, BattleHex destinationTile, BattleHex attackerPos) const AttackableTiles CBattleInfoCallback::getPotentiallyAttackableHexes (const CStack* attacker, BattleHex destinationTile, BattleHex attackerPos, bool rangedAttack) const
{ {
//does not return hex attacked directly //does not return hex attacked directly
//TODO: apply rotation to two-hex attackers //TODO: apply rotation to two-hex attackers
@ -1129,25 +1129,37 @@ AttackableTiles CBattleInfoCallback::getPotentiallyAttackableHexes (const CStack
AttackableTiles at; AttackableTiles at;
RETURN_IF_NOT_BATTLE(at); RETURN_IF_NOT_BATTLE(at);
if(rangedAttack)
{
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));
}
}
else
{
const int WN = GameConstants::BFIELD_WIDTH; const int WN = GameConstants::BFIELD_WIDTH;
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?
@ -1157,13 +1169,12 @@ AttackableTiles CBattleInfoCallback::getPotentiallyAttackableHexes (const CStack
} }
} }
} }
if (attacker->hasBonusOfType(Bonus::WIDE_BREATH)) if(attacker->hasBonusOfType(Bonus::WIDE_BREATH))
{ {
std::vector<BattleHex> hexes; std::vector<BattleHex> hexes;
const int WN = GameConstants::BFIELD_WIDTH; // attack stand
// atack stand if(hex == destinationTile.cloneInDirection(BattleHex::EDir::LEFT, false)) {
if (hex == destinationTile.cloneInDirection(BattleHex::EDir::LEFT, false)) { if(destinationTile.getY() % 2 == 0)
if (destinationTile.getY() % 2 == 0)
{ {
BattleHex::checkAndPush(destinationTile, hexes); BattleHex::checkAndPush(destinationTile, hexes);
BattleHex::checkAndPush(destinationTile + 1, hexes); BattleHex::checkAndPush(destinationTile + 1, hexes);
@ -1182,8 +1193,8 @@ AttackableTiles CBattleInfoCallback::getPotentiallyAttackableHexes (const CStack
} }
} }
if (hex == destinationTile.cloneInDirection(BattleHex::EDir::RIGHT, false)) { if(hex == destinationTile.cloneInDirection(BattleHex::EDir::RIGHT, false)) {
if (destinationTile.getY() % 2 == 0) if(destinationTile.getY() % 2 == 0)
{ {
BattleHex::checkAndPush(destinationTile, hexes); BattleHex::checkAndPush(destinationTile, hexes);
BattleHex::checkAndPush(destinationTile - 1, hexes); BattleHex::checkAndPush(destinationTile - 1, hexes);
@ -1202,9 +1213,9 @@ AttackableTiles CBattleInfoCallback::getPotentiallyAttackableHexes (const CStack
BattleHex::checkAndPush(destinationTile + (2 * WN) + 1, hexes); BattleHex::checkAndPush(destinationTile + (2 * WN) + 1, hexes);
} }
} }
// atack down // attack down
if (hex == destinationTile.cloneInDirection(BattleHex::EDir::TOP_LEFT, false)) { if(hex == destinationTile.cloneInDirection(BattleHex::EDir::TOP_LEFT, false)) {
if (destinationTile.getY() % 2 == 1) if(destinationTile.getY() % 2 == 1)
{ {
BattleHex::checkAndPush(destinationTile, hexes); BattleHex::checkAndPush(destinationTile, hexes);
BattleHex::checkAndPush(destinationTile - 1, hexes); BattleHex::checkAndPush(destinationTile - 1, hexes);
@ -1222,9 +1233,9 @@ AttackableTiles CBattleInfoCallback::getPotentiallyAttackableHexes (const CStack
BattleHex::checkAndPush(destinationTile + WN - 1, hexes); BattleHex::checkAndPush(destinationTile + WN - 1, hexes);
} }
} }
if (hex == destinationTile.cloneInDirection(BattleHex::EDir::TOP_RIGHT, false)) { if(hex == destinationTile.cloneInDirection(BattleHex::EDir::TOP_RIGHT, false)) {
if (destinationTile.getY() % 2 == 1) if(destinationTile.getY() % 2 == 1)
{ {
BattleHex::checkAndPush(destinationTile, hexes); BattleHex::checkAndPush(destinationTile, hexes);
BattleHex::checkAndPush(destinationTile + 1, hexes); BattleHex::checkAndPush(destinationTile + 1, hexes);
@ -1245,8 +1256,8 @@ AttackableTiles CBattleInfoCallback::getPotentiallyAttackableHexes (const CStack
} }
// attack up // attack up
if (hex == destinationTile.cloneInDirection(BattleHex::EDir::BOTTOM_LEFT, false)) { if(hex == destinationTile.cloneInDirection(BattleHex::EDir::BOTTOM_LEFT, false)) {
if (destinationTile.getY() % 2 == 1) if(destinationTile.getY() % 2 == 1)
{ {
BattleHex::checkAndPush(destinationTile, hexes); BattleHex::checkAndPush(destinationTile, hexes);
BattleHex::checkAndPush(destinationTile + 1, hexes); BattleHex::checkAndPush(destinationTile + 1, hexes);
@ -1264,9 +1275,9 @@ AttackableTiles CBattleInfoCallback::getPotentiallyAttackableHexes (const CStack
BattleHex::checkAndPush(destinationTile - WN + 1, hexes); BattleHex::checkAndPush(destinationTile - WN + 1, hexes);
} }
} }
if (hex == destinationTile.cloneInDirection(BattleHex::EDir::BOTTOM_RIGHT, false)) { if(hex == destinationTile.cloneInDirection(BattleHex::EDir::BOTTOM_RIGHT, false)) {
if (destinationTile.getY() % 2 == 1) if(destinationTile.getY() % 2 == 1)
{ {
BattleHex::checkAndPush(destinationTile, hexes); BattleHex::checkAndPush(destinationTile, hexes);
BattleHex::checkAndPush(destinationTile - 1, hexes); BattleHex::checkAndPush(destinationTile - 1, hexes);
@ -1287,19 +1298,19 @@ AttackableTiles CBattleInfoCallback::getPotentiallyAttackableHexes (const CStack
} }
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);
} }
} }
else if (attacker->hasBonusOfType(Bonus::TWO_HEX_ATTACK_BREATH) && BattleHex::mutualPosition(destinationTile.hex, hex) > -1) //only adjacent hexes are subject of dragon breath calculation 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:
@ -1316,23 +1327,23 @@ AttackableTiles CBattleInfoCallback::getPotentiallyAttackableHexes (const CStack
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; 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 = getPotentiallyAttackableHexes(attacker, destinationTile, attackerPos, rangedAttack);
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);

View File

@ -92,8 +92,8 @@ public:
bool isInTacticRange(BattleHex dest) const; bool isInTacticRange(BattleHex dest) const;
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, bool rangedAttack) 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 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

View File

@ -892,9 +892,8 @@ 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)
{ {
@ -903,7 +902,6 @@ void CGameHandler::prepareAttack(BattleAttack &bat, const CStack *att, const CSt
applyBattleEffects(bat, att, stack, distance, true); applyBattleEffects(bat, att, stack, distance, true);
} }
} }
}
const std::shared_ptr<Bonus> bonus = att->getBonusLocalFirst(Selector::type(Bonus::SPELL_LIKE_ATTACK)); const std::shared_ptr<Bonus> bonus = att->getBonusLocalFirst(Selector::type(Bonus::SPELL_LIKE_ATTACK));
if (bonus && (bat.shot())) //TODO: make it work in melee? if (bonus && (bat.shot())) //TODO: make it work in melee?
@ -914,11 +912,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
{ {