From f5b5fa149f06d438f1b99293f7bce2ba4fdc6e18 Mon Sep 17 00:00:00 2001 From: dydzio Date: Sat, 28 Jan 2017 12:28:30 +0100 Subject: [PATCH 01/12] Build #1 --- config/bonuses_texts.json | 6 ++ lib/CBattleCallback.cpp | 157 ++++++++++++++++++++++++++++++++++++-- lib/HeroBonus.h | 1 + 3 files changed, 156 insertions(+), 8 deletions(-) diff --git a/config/bonuses_texts.json b/config/bonuses_texts.json index 9aae5157c..e52afc657 100644 --- a/config/bonuses_texts.json +++ b/config/bonuses_texts.json @@ -418,4 +418,10 @@ "name": "Water immunity", "description": "Immune to all Water school spells" } + + "WIDE_BREATH": + { + "name": "Szerokie Zionięcie", + "description": "Stożek ognia obejmuje znacznie większy obszar" + } } diff --git a/lib/CBattleCallback.cpp b/lib/CBattleCallback.cpp index 3ffe2350c..cdc1f26cb 100644 --- a/lib/CBattleCallback.cpp +++ b/lib/CBattleCallback.cpp @@ -1506,7 +1506,148 @@ 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 hexes; + const int WN = GameConstants::BFIELD_WIDTH; + logGlobal->infoStream() << hex; + logGlobal->infoStream() << destinationTile; + + // atack stand + if (hex == destinationTile.movedInDir(BattleHex::EDir::LEFT, false)) { + if (destinationTile.getY() % 2 == 0) + { + BattleHex::checkAndPush(destinationTile, hexes); + BattleHex::checkAndPush(destinationTile + 1, hexes); + BattleHex::checkAndPush(destinationTile + WN, hexes); + BattleHex::checkAndPush(destinationTile + WN + 1, hexes); + BattleHex::checkAndPush(destinationTile + (2 * WN) - 1, hexes); + BattleHex::checkAndPush(destinationTile + (2 * WN), hexes); + } + else { + BattleHex::checkAndPush(destinationTile, hexes); + BattleHex::checkAndPush(destinationTile + 1, hexes); + BattleHex::checkAndPush(destinationTile + WN, hexes); + BattleHex::checkAndPush(destinationTile + WN - 1, hexes); + BattleHex::checkAndPush(destinationTile + (2 * WN) - 1, hexes); + BattleHex::checkAndPush(destinationTile + (2 * WN), hexes); + } + + } + if (hex == destinationTile.movedInDir(BattleHex::EDir::RIGHT, false)) { + if (destinationTile.getY() % 2 == 0) + { + BattleHex::checkAndPush(destinationTile, hexes); + BattleHex::checkAndPush(destinationTile - 1, hexes); + BattleHex::checkAndPush(destinationTile + WN, hexes); + BattleHex::checkAndPush(destinationTile + WN + 1, hexes); + BattleHex::checkAndPush(destinationTile + (2 * WN), hexes); + BattleHex::checkAndPush(destinationTile + (2 * WN) + 1, hexes); + } + else + { + BattleHex::checkAndPush(destinationTile, hexes); + BattleHex::checkAndPush(destinationTile - 1, hexes); + BattleHex::checkAndPush(destinationTile + WN, hexes); + BattleHex::checkAndPush(destinationTile + WN - 1, hexes); + BattleHex::checkAndPush(destinationTile + (2 * WN), hexes); + BattleHex::checkAndPush(destinationTile + (2 * WN) + 1, hexes); + } + } + // atack down + if (hex == destinationTile.movedInDir(BattleHex::EDir::TOP_LEFT, false)) { + if (destinationTile.getY() % 2 == 1) + { + BattleHex::checkAndPush(destinationTile, hexes); + BattleHex::checkAndPush(destinationTile - 1, hexes); + BattleHex::checkAndPush(destinationTile - 2, hexes); + BattleHex::checkAndPush(destinationTile + WN, hexes); + BattleHex::checkAndPush(destinationTile + WN - 1, hexes); + BattleHex::checkAndPush(destinationTile + WN - 2, hexes); + } + else { + BattleHex::checkAndPush(destinationTile, hexes); + BattleHex::checkAndPush(destinationTile - 1, hexes); + BattleHex::checkAndPush(destinationTile - 2, hexes); + BattleHex::checkAndPush(destinationTile + WN, hexes); + BattleHex::checkAndPush(destinationTile + WN + 1, hexes); + BattleHex::checkAndPush(destinationTile + WN - 1, hexes); + } + } + if (hex == destinationTile.movedInDir(BattleHex::EDir::TOP_RIGHT, false)) { + + if (destinationTile.getY() % 2 == 1) + { + BattleHex::checkAndPush(destinationTile, hexes); + BattleHex::checkAndPush(destinationTile + 1, hexes); + BattleHex::checkAndPush(destinationTile + 2, hexes); + BattleHex::checkAndPush(destinationTile + WN - 1, hexes); + BattleHex::checkAndPush(destinationTile + WN, hexes); + BattleHex::checkAndPush(destinationTile + WN + 1, hexes); + } + else + { + BattleHex::checkAndPush(destinationTile, hexes); + BattleHex::checkAndPush(destinationTile + 1, hexes); + BattleHex::checkAndPush(destinationTile + 2, hexes); + BattleHex::checkAndPush(destinationTile + WN, hexes); + BattleHex::checkAndPush(destinationTile + WN + 1, hexes); + BattleHex::checkAndPush(destinationTile + WN + 2, hexes); + } + + } + // attack up + if (hex == destinationTile.movedInDir(BattleHex::EDir::BOTTOM_LEFT, false)) { + if (destinationTile.getY() % 2 == 1) + { + BattleHex::checkAndPush(destinationTile, hexes); + BattleHex::checkAndPush(destinationTile + 1, hexes); + BattleHex::checkAndPush(destinationTile + WN, hexes); + BattleHex::checkAndPush(destinationTile + WN + 1, hexes); + BattleHex::checkAndPush(destinationTile - WN, hexes); + BattleHex::checkAndPush(destinationTile - WN - 1, hexes); + } + else { + BattleHex::checkAndPush(destinationTile, hexes); + BattleHex::checkAndPush(destinationTile + 1, hexes); + BattleHex::checkAndPush(destinationTile + WN + 1, hexes); + BattleHex::checkAndPush(destinationTile + WN + 2, hexes); + BattleHex::checkAndPush(destinationTile - WN, hexes); + BattleHex::checkAndPush(destinationTile - WN + 1, hexes); + } + } + if (hex == destinationTile.movedInDir(BattleHex::EDir::BOTTOM_RIGHT, false)) { + + if (destinationTile.getY() % 2 == 1) + { + BattleHex::checkAndPush(destinationTile, hexes); + BattleHex::checkAndPush(destinationTile - 1, hexes); + BattleHex::checkAndPush(destinationTile + WN - 1, hexes); + BattleHex::checkAndPush(destinationTile + WN - 2, hexes); + BattleHex::checkAndPush(destinationTile - WN, hexes); + BattleHex::checkAndPush(destinationTile - WN - 1, hexes); + } + else + { + BattleHex::checkAndPush(destinationTile, hexes); + BattleHex::checkAndPush(destinationTile - 1, hexes); + BattleHex::checkAndPush(destinationTile + WN, hexes); + BattleHex::checkAndPush(destinationTile + WN - 1, hexes); + BattleHex::checkAndPush(destinationTile - WN, hexes); + BattleHex::checkAndPush(destinationTile - WN + 1, hexes); + } + + } + + for (BattleHex tile : hexes) + { + //friendly stacks can also be damaged by Dragon Breath + if (battleGetStackByPos(tile, true)) + 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 hexes; //only one, in fact int pseudoVector = destinationTile.hex - hex; @@ -1514,24 +1655,24 @@ AttackableTiles CBattleInfoCallback::getPotentiallyAttackableHexes (const CStack { case 1: case -1: - BattleHex::checkAndPush (destinationTile.hex + pseudoVector, hexes); + BattleHex::checkAndPush(destinationTile.hex + pseudoVector, hexes); break; case WN: //17 //left-down or right-down case -WN: //-17 //left-up or right-up case WN + 1: //18 //right-down 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; - case WN-1: //16 //left-down - case -WN-1: //-18 //left-up - BattleHex::checkAndPush (destinationTile.hex + pseudoVector + (((hex/WN)%2) ? 1 : 0), hexes); + case WN - 1: //16 //left-down + case -WN - 1: //-18 //left-up + BattleHex::checkAndPush(destinationTile.hex + pseudoVector + (((hex / WN) % 2) ? 1 : 0), hexes); break; } for (BattleHex tile : hexes) { //friendly stacks can also be damaged by Dragon Breath - if (battleGetStackByPos (tile, true)) - at.friendlyCreaturePositions.insert (tile); + if (battleGetStackByPos(tile, true)) + at.friendlyCreaturePositions.insert(tile); } } diff --git a/lib/HeroBonus.h b/lib/HeroBonus.h index e154ff59e..8df372c27 100644 --- a/lib/HeroBonus.h +++ b/lib/HeroBonus.h @@ -219,6 +219,7 @@ public: BONUS_NAME(DISGUISED) /* subtype - spell level */\ BONUS_NAME(VISIONS) /* subtype - spell level */\ BONUS_NAME(NO_TERRAIN_PENALTY) /* subtype - terrain type */\ + BONUS_NAME(WIDE_BREATH) /* Kuririn skill */\ /* end of list */ From 5ba8d64b63b1dab6300d33b24e654a40af2b22c2 Mon Sep 17 00:00:00 2001 From: Dydzio Date: Sat, 19 Aug 2017 19:16:44 +0200 Subject: [PATCH 02/12] Working WIDE_BREATH ability --- lib/battle/CBattleInfoCallback.cpp | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/lib/battle/CBattleInfoCallback.cpp b/lib/battle/CBattleInfoCallback.cpp index 1e7fea391..9568fa41d 100644 --- a/lib/battle/CBattleInfoCallback.cpp +++ b/lib/battle/CBattleInfoCallback.cpp @@ -1161,11 +1161,8 @@ AttackableTiles CBattleInfoCallback::getPotentiallyAttackableHexes (const CStack { std::vector hexes; const int WN = GameConstants::BFIELD_WIDTH; - logGlobal->infoStream() << hex; - logGlobal->infoStream() << destinationTile; - // atack stand - if (hex == destinationTile.movedInDir(BattleHex::EDir::LEFT, false)) { + if (hex == destinationTile.cloneInDirection(BattleHex::EDir::LEFT, false)) { if (destinationTile.getY() % 2 == 0) { BattleHex::checkAndPush(destinationTile, hexes); @@ -1185,7 +1182,7 @@ AttackableTiles CBattleInfoCallback::getPotentiallyAttackableHexes (const CStack } } - if (hex == destinationTile.movedInDir(BattleHex::EDir::RIGHT, false)) { + if (hex == destinationTile.cloneInDirection(BattleHex::EDir::RIGHT, false)) { if (destinationTile.getY() % 2 == 0) { BattleHex::checkAndPush(destinationTile, hexes); @@ -1206,7 +1203,7 @@ AttackableTiles CBattleInfoCallback::getPotentiallyAttackableHexes (const CStack } } // atack down - if (hex == destinationTile.movedInDir(BattleHex::EDir::TOP_LEFT, false)) { + if (hex == destinationTile.cloneInDirection(BattleHex::EDir::TOP_LEFT, false)) { if (destinationTile.getY() % 2 == 1) { BattleHex::checkAndPush(destinationTile, hexes); @@ -1225,7 +1222,7 @@ AttackableTiles CBattleInfoCallback::getPotentiallyAttackableHexes (const CStack BattleHex::checkAndPush(destinationTile + WN - 1, hexes); } } - if (hex == destinationTile.movedInDir(BattleHex::EDir::TOP_RIGHT, false)) { + if (hex == destinationTile.cloneInDirection(BattleHex::EDir::TOP_RIGHT, false)) { if (destinationTile.getY() % 2 == 1) { @@ -1248,7 +1245,7 @@ AttackableTiles CBattleInfoCallback::getPotentiallyAttackableHexes (const CStack } // attack up - if (hex == destinationTile.movedInDir(BattleHex::EDir::BOTTOM_LEFT, false)) { + if (hex == destinationTile.cloneInDirection(BattleHex::EDir::BOTTOM_LEFT, false)) { if (destinationTile.getY() % 2 == 1) { BattleHex::checkAndPush(destinationTile, hexes); @@ -1267,7 +1264,7 @@ AttackableTiles CBattleInfoCallback::getPotentiallyAttackableHexes (const CStack BattleHex::checkAndPush(destinationTile - WN + 1, hexes); } } - if (hex == destinationTile.movedInDir(BattleHex::EDir::BOTTOM_RIGHT, false)) { + if (hex == destinationTile.cloneInDirection(BattleHex::EDir::BOTTOM_RIGHT, false)) { if (destinationTile.getY() % 2 == 1) { From 1df939bf70e2afdc2221e8770b2837b00d3e4feb Mon Sep 17 00:00:00 2001 From: Dydzio Date: Sat, 19 Aug 2017 20:39:24 +0200 Subject: [PATCH 03/12] Working FIRST_STRIKE ability --- config/bonuses_texts.json | 11 ++++++++--- lib/HeroBonus.h | 1 + server/CGameHandler.cpp | 14 ++++++++++++++ 3 files changed, 23 insertions(+), 3 deletions(-) diff --git a/config/bonuses_texts.json b/config/bonuses_texts.json index 0b8ea5e23..029bff1a8 100644 --- a/config/bonuses_texts.json +++ b/config/bonuses_texts.json @@ -150,10 +150,15 @@ "description": "Immune to all Fire school spells" }, - "FIRE_SHIELD": + "FIRE_SHIELD": { + "name": "Fire Shield (${val}%)", + "description": "Reflects part of melee damage" + }, + + "FIRST_STRIKE": { - "name": "Fire Shield (${val}%)", - "description": "Reflects part of melee damage" + "name": "First Strike", + "description": "This creature attacks first instead of retaliating" }, "FEAR": diff --git a/lib/HeroBonus.h b/lib/HeroBonus.h index 0e59b65d3..5ba1e6cc6 100644 --- a/lib/HeroBonus.h +++ b/lib/HeroBonus.h @@ -236,6 +236,7 @@ private: 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(WIDE_BREATH) /* Kuririn skill */\ + BONUS_NAME(FIRST_STRIKE) /* Witchking skill */\ /* end of list */ diff --git a/server/CGameHandler.cpp b/server/CGameHandler.cpp index 9aeadd2fb..56f182339 100644 --- a/server/CGameHandler.cpp +++ b/server/CGameHandler.cpp @@ -3935,6 +3935,19 @@ bool CGameHandler::makeBattleAction(BattleAction &ba) 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 && stack->alive() && //move can cause death, eg. by walking into the moat destinationStack->alive()) @@ -3950,6 +3963,7 @@ bool CGameHandler::makeBattleAction(BattleAction &ba) //counterattack if (i == 0 && destinationStack && !stack->hasBonusOfType(Bonus::BLOCKS_RETALIATION) + && !destinationStack->hasBonusOfType(Bonus::FIRST_STRIKE) && destinationStack->ableToRetaliate() && stack->alive()) //attacker may have died (fire shield) { From 4cab76900f0c4225a7cb8f35f227ebe0ba9e3691 Mon Sep 17 00:00:00 2001 From: Dydzio Date: Mon, 4 Sep 2017 23:32:24 +0200 Subject: [PATCH 04/12] Add SYNERGY_TARGET and [wip]-SHOOTS_ALL_ADJACENT --- config/bonuses_texts.json | 25 +- lib/HeroBonus.h | 6 +- lib/battle/CBattleInfoCallback.cpp | 389 +++++++++++++++-------------- lib/battle/CBattleInfoCallback.h | 4 +- server/CGameHandler.cpp | 18 +- 5 files changed, 233 insertions(+), 209 deletions(-) diff --git a/config/bonuses_texts.json b/config/bonuses_texts.json index 029bff1a8..8864751ce 100644 --- a/config/bonuses_texts.json +++ b/config/bonuses_texts.json @@ -150,12 +150,13 @@ "description": "Immune to all Fire school spells" }, - "FIRE_SHIELD": { - "name": "Fire Shield (${val}%)", - "description": "Reflects part of melee damage" - }, + "FIRE_SHIELD": + { + "name": "Fire Shield (${val}%)", + "description": "Reflects part of melee damage" + }, - "FIRST_STRIKE": + "FIRST_STRIKE": { "name": "First Strike", "description": "This creature attacks first instead of retaliating" @@ -352,6 +353,12 @@ "name": "Ranged", "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": { @@ -400,13 +407,19 @@ "name": "Aura of Resistance", "description": "Nearby stacks get ${val}% resistance" }, - + "SUMMON_GUARDIANS": { "name": "Summon guardians", "description": "At battle start summons ${subtype.creature} (${val}%)" }, + "SYNERGY_TARGET": + { + "name": "Synergizable", + "description": "This creature is vulnerable to synergy effect" + }, + "TWO_HEX_ATTACK_BREATH": { "name": "Breath", diff --git a/lib/HeroBonus.h b/lib/HeroBonus.h index 5ba1e6cc6..1b9e46651 100644 --- a/lib/HeroBonus.h +++ b/lib/HeroBonus.h @@ -235,8 +235,10 @@ private: 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(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(FIRST_STRIKE) /* Witchking skill */\ + 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 */\ /* end of list */ diff --git a/lib/battle/CBattleInfoCallback.cpp b/lib/battle/CBattleInfoCallback.cpp index 9568fa41d..e4bade27e 100644 --- a/lib/battle/CBattleInfoCallback.cpp +++ b/lib/battle/CBattleInfoCallback.cpp @@ -186,7 +186,7 @@ std::set CBattleInfoCallback::battleGetAttackedHexes(const CStack* at { std::set attackedHexes; RETURN_IF_NOT_BATTLE(attackedHexes); - + AttackableTiles at = getPotentiallyAttackableHexes(attacker, destinationTile, attackerPos); for (BattleHex tile : at.hostileCreaturePositions) @@ -1120,7 +1120,7 @@ ReachabilityInfo CBattleInfoCallback::getFlyingReachability(const ReachabilityIn 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 //TODO: apply rotation to two-hex attackers @@ -1129,210 +1129,221 @@ AttackableTiles CBattleInfoCallback::getPotentiallyAttackableHexes (const CStack AttackableTiles at; RETURN_IF_NOT_BATTLE(at); - const int WN = GameConstants::BFIELD_WIDTH; - 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) - bool reverse = isToReverse (hex, destinationTile, isAttacker, attacker->doubleWide(), isAttacker); - if (reverse && attacker->doubleWide()) + if(rangedAttack) { - hex = attacker->occupiedHex(hex); //the other hex stack stands on - } - if (attacker->hasBonusOfType(Bonus::ATTACKS_ALL_ADJACENT)) - { - boost::copy (attacker->getSurroundingHexes (attackerPos), vstd::set_inserter (at.hostileCreaturePositions)); - } - if (attacker->hasBonusOfType(Bonus::THREE_HEADED_ATTACK)) - { - std::vector hexes = attacker->getSurroundingHexes(attackerPos); - for (BattleHex tile : hexes) + if(attacker->hasBonusOfType(Bonus::SHOOTS_ALL_ADJACENT) && !vstd::contains(attackerPos.neighbouringTiles(), destinationTile)) { - if ((BattleHex::mutualPosition(tile, destinationTile) > -1 && BattleHex::mutualPosition (tile, hex) > -1)) //adjacent both to attacker's head and attacked tile + std::vector targetHexes = destinationTile.neighbouringTiles(); + targetHexes.push_back(destinationTile); + boost::copy(targetHexes, vstd::set_inserter(at.hostileCreaturePositions)); + } + } + + else + { + const int WN = GameConstants::BFIELD_WIDTH; + 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) + bool reverse = isToReverse(hex, destinationTile, isAttacker, attacker->doubleWide(), isAttacker); + if(reverse && attacker->doubleWide()) + { + hex = attacker->occupiedHex(hex); //the other hex stack stands on + } + if(attacker->hasBonusOfType(Bonus::ATTACKS_ALL_ADJACENT)) + { + boost::copy(attacker->getSurroundingHexes(attackerPos), vstd::set_inserter(at.hostileCreaturePositions)); + } + if(attacker->hasBonusOfType(Bonus::THREE_HEADED_ATTACK)) + { + std::vector hexes = attacker->getSurroundingHexes(attackerPos); + for(BattleHex tile : hexes) { - const CStack * st = battleGetStackByPos(tile, true); - if(st && st->owner != attacker->owner) //only hostile stacks - does it work well with Berserk? + if((BattleHex::mutualPosition(tile, destinationTile) > -1 && BattleHex::mutualPosition(tile, hex) > -1)) //adjacent both to attacker's head and attacked tile { - at.hostileCreaturePositions.insert(tile); + const CStack * st = battleGetStackByPos(tile, true); + if(st && st->owner != attacker->owner) //only hostile stacks - does it work well with Berserk? + { + at.hostileCreaturePositions.insert(tile); + } } } } - } - if (attacker->hasBonusOfType(Bonus::WIDE_BREATH)) - { - std::vector hexes; - const int WN = GameConstants::BFIELD_WIDTH; - // atack stand - if (hex == destinationTile.cloneInDirection(BattleHex::EDir::LEFT, false)) { - if (destinationTile.getY() % 2 == 0) - { - BattleHex::checkAndPush(destinationTile, hexes); - BattleHex::checkAndPush(destinationTile + 1, hexes); - BattleHex::checkAndPush(destinationTile + WN, hexes); - BattleHex::checkAndPush(destinationTile + WN + 1, hexes); - BattleHex::checkAndPush(destinationTile + (2 * WN) - 1, hexes); - BattleHex::checkAndPush(destinationTile + (2 * WN), hexes); - } - else { - BattleHex::checkAndPush(destinationTile, hexes); - BattleHex::checkAndPush(destinationTile + 1, hexes); - BattleHex::checkAndPush(destinationTile + WN, hexes); - BattleHex::checkAndPush(destinationTile + WN - 1, hexes); - BattleHex::checkAndPush(destinationTile + (2 * WN) - 1, hexes); - BattleHex::checkAndPush(destinationTile + (2 * WN), hexes); - } - - } - if (hex == destinationTile.cloneInDirection(BattleHex::EDir::RIGHT, false)) { - if (destinationTile.getY() % 2 == 0) - { - BattleHex::checkAndPush(destinationTile, hexes); - BattleHex::checkAndPush(destinationTile - 1, hexes); - BattleHex::checkAndPush(destinationTile + WN, hexes); - BattleHex::checkAndPush(destinationTile + WN + 1, hexes); - BattleHex::checkAndPush(destinationTile + (2 * WN), hexes); - BattleHex::checkAndPush(destinationTile + (2 * WN) + 1, hexes); - } - else - { - BattleHex::checkAndPush(destinationTile, hexes); - BattleHex::checkAndPush(destinationTile - 1, hexes); - BattleHex::checkAndPush(destinationTile + WN, hexes); - BattleHex::checkAndPush(destinationTile + WN - 1, hexes); - BattleHex::checkAndPush(destinationTile + (2 * WN), hexes); - BattleHex::checkAndPush(destinationTile + (2 * WN) + 1, hexes); - } - } - // atack down - if (hex == destinationTile.cloneInDirection(BattleHex::EDir::TOP_LEFT, false)) { - if (destinationTile.getY() % 2 == 1) - { - BattleHex::checkAndPush(destinationTile, hexes); - BattleHex::checkAndPush(destinationTile - 1, hexes); - BattleHex::checkAndPush(destinationTile - 2, hexes); - BattleHex::checkAndPush(destinationTile + WN, hexes); - BattleHex::checkAndPush(destinationTile + WN - 1, hexes); - BattleHex::checkAndPush(destinationTile + WN - 2, hexes); - } - else { - BattleHex::checkAndPush(destinationTile, hexes); - BattleHex::checkAndPush(destinationTile - 1, hexes); - BattleHex::checkAndPush(destinationTile - 2, hexes); - BattleHex::checkAndPush(destinationTile + WN, hexes); - BattleHex::checkAndPush(destinationTile + WN + 1, hexes); - BattleHex::checkAndPush(destinationTile + WN - 1, hexes); - } - } - if (hex == destinationTile.cloneInDirection(BattleHex::EDir::TOP_RIGHT, false)) { - - if (destinationTile.getY() % 2 == 1) - { - BattleHex::checkAndPush(destinationTile, hexes); - BattleHex::checkAndPush(destinationTile + 1, hexes); - BattleHex::checkAndPush(destinationTile + 2, hexes); - BattleHex::checkAndPush(destinationTile + WN - 1, hexes); - BattleHex::checkAndPush(destinationTile + WN, hexes); - BattleHex::checkAndPush(destinationTile + WN + 1, hexes); - } - else - { - BattleHex::checkAndPush(destinationTile, hexes); - BattleHex::checkAndPush(destinationTile + 1, hexes); - BattleHex::checkAndPush(destinationTile + 2, hexes); - BattleHex::checkAndPush(destinationTile + WN, hexes); - BattleHex::checkAndPush(destinationTile + WN + 1, hexes); - BattleHex::checkAndPush(destinationTile + WN + 2, hexes); - } - - } - // attack up - if (hex == destinationTile.cloneInDirection(BattleHex::EDir::BOTTOM_LEFT, false)) { - if (destinationTile.getY() % 2 == 1) - { - BattleHex::checkAndPush(destinationTile, hexes); - BattleHex::checkAndPush(destinationTile + 1, hexes); - BattleHex::checkAndPush(destinationTile + WN, hexes); - BattleHex::checkAndPush(destinationTile + WN + 1, hexes); - BattleHex::checkAndPush(destinationTile - WN, hexes); - BattleHex::checkAndPush(destinationTile - WN - 1, hexes); - } - else { - BattleHex::checkAndPush(destinationTile, hexes); - BattleHex::checkAndPush(destinationTile + 1, hexes); - BattleHex::checkAndPush(destinationTile + WN + 1, hexes); - BattleHex::checkAndPush(destinationTile + WN + 2, hexes); - BattleHex::checkAndPush(destinationTile - WN, hexes); - BattleHex::checkAndPush(destinationTile - WN + 1, hexes); - } - } - if (hex == destinationTile.cloneInDirection(BattleHex::EDir::BOTTOM_RIGHT, false)) { - - if (destinationTile.getY() % 2 == 1) - { - BattleHex::checkAndPush(destinationTile, hexes); - BattleHex::checkAndPush(destinationTile - 1, hexes); - BattleHex::checkAndPush(destinationTile + WN - 1, hexes); - BattleHex::checkAndPush(destinationTile + WN - 2, hexes); - BattleHex::checkAndPush(destinationTile - WN, hexes); - BattleHex::checkAndPush(destinationTile - WN - 1, hexes); - } - else - { - BattleHex::checkAndPush(destinationTile, hexes); - BattleHex::checkAndPush(destinationTile - 1, hexes); - BattleHex::checkAndPush(destinationTile + WN, hexes); - BattleHex::checkAndPush(destinationTile + WN - 1, hexes); - BattleHex::checkAndPush(destinationTile - WN, hexes); - BattleHex::checkAndPush(destinationTile - WN + 1, hexes); - } - - } - - for (BattleHex tile : hexes) + if(attacker->hasBonusOfType(Bonus::WIDE_BREATH)) { - //friendly stacks can also be damaged by Dragon Breath - if (battleGetStackByPos(tile, true)) - at.friendlyCreaturePositions.insert(tile); + std::vector hexes; + // attack stand + if(hex == destinationTile.cloneInDirection(BattleHex::EDir::LEFT, false)) { + if(destinationTile.getY() % 2 == 0) + { + BattleHex::checkAndPush(destinationTile, hexes); + BattleHex::checkAndPush(destinationTile + 1, hexes); + BattleHex::checkAndPush(destinationTile + WN, hexes); + BattleHex::checkAndPush(destinationTile + WN + 1, hexes); + BattleHex::checkAndPush(destinationTile + (2 * WN) - 1, hexes); + BattleHex::checkAndPush(destinationTile + (2 * WN), hexes); + } + else { + BattleHex::checkAndPush(destinationTile, hexes); + BattleHex::checkAndPush(destinationTile + 1, hexes); + BattleHex::checkAndPush(destinationTile + WN, hexes); + BattleHex::checkAndPush(destinationTile + WN - 1, hexes); + BattleHex::checkAndPush(destinationTile + (2 * WN) - 1, hexes); + BattleHex::checkAndPush(destinationTile + (2 * WN), hexes); + } + + } + if(hex == destinationTile.cloneInDirection(BattleHex::EDir::RIGHT, false)) { + if(destinationTile.getY() % 2 == 0) + { + BattleHex::checkAndPush(destinationTile, hexes); + BattleHex::checkAndPush(destinationTile - 1, hexes); + BattleHex::checkAndPush(destinationTile + WN, hexes); + BattleHex::checkAndPush(destinationTile + WN + 1, hexes); + BattleHex::checkAndPush(destinationTile + (2 * WN), hexes); + BattleHex::checkAndPush(destinationTile + (2 * WN) + 1, hexes); + } + else + { + BattleHex::checkAndPush(destinationTile, hexes); + BattleHex::checkAndPush(destinationTile - 1, hexes); + BattleHex::checkAndPush(destinationTile + WN, hexes); + BattleHex::checkAndPush(destinationTile + WN - 1, hexes); + BattleHex::checkAndPush(destinationTile + (2 * WN), hexes); + BattleHex::checkAndPush(destinationTile + (2 * WN) + 1, hexes); + } + } + // attack down + if(hex == destinationTile.cloneInDirection(BattleHex::EDir::TOP_LEFT, false)) { + if(destinationTile.getY() % 2 == 1) + { + BattleHex::checkAndPush(destinationTile, hexes); + BattleHex::checkAndPush(destinationTile - 1, hexes); + BattleHex::checkAndPush(destinationTile - 2, hexes); + BattleHex::checkAndPush(destinationTile + WN, hexes); + BattleHex::checkAndPush(destinationTile + WN - 1, hexes); + BattleHex::checkAndPush(destinationTile + WN - 2, hexes); + } + else { + BattleHex::checkAndPush(destinationTile, hexes); + BattleHex::checkAndPush(destinationTile - 1, hexes); + BattleHex::checkAndPush(destinationTile - 2, hexes); + BattleHex::checkAndPush(destinationTile + WN, hexes); + BattleHex::checkAndPush(destinationTile + WN + 1, hexes); + BattleHex::checkAndPush(destinationTile + WN - 1, hexes); + } + } + if(hex == destinationTile.cloneInDirection(BattleHex::EDir::TOP_RIGHT, false)) { + + if(destinationTile.getY() % 2 == 1) + { + BattleHex::checkAndPush(destinationTile, hexes); + BattleHex::checkAndPush(destinationTile + 1, hexes); + BattleHex::checkAndPush(destinationTile + 2, hexes); + BattleHex::checkAndPush(destinationTile + WN - 1, hexes); + BattleHex::checkAndPush(destinationTile + WN, hexes); + BattleHex::checkAndPush(destinationTile + WN + 1, hexes); + } + else + { + BattleHex::checkAndPush(destinationTile, hexes); + BattleHex::checkAndPush(destinationTile + 1, hexes); + BattleHex::checkAndPush(destinationTile + 2, hexes); + BattleHex::checkAndPush(destinationTile + WN, hexes); + BattleHex::checkAndPush(destinationTile + WN + 1, hexes); + BattleHex::checkAndPush(destinationTile + WN + 2, hexes); + } + + } + // attack up + if(hex == destinationTile.cloneInDirection(BattleHex::EDir::BOTTOM_LEFT, false)) { + if(destinationTile.getY() % 2 == 1) + { + BattleHex::checkAndPush(destinationTile, hexes); + BattleHex::checkAndPush(destinationTile + 1, hexes); + BattleHex::checkAndPush(destinationTile + WN, hexes); + BattleHex::checkAndPush(destinationTile + WN + 1, hexes); + BattleHex::checkAndPush(destinationTile - WN, hexes); + BattleHex::checkAndPush(destinationTile - WN - 1, hexes); + } + else { + BattleHex::checkAndPush(destinationTile, hexes); + BattleHex::checkAndPush(destinationTile + 1, hexes); + BattleHex::checkAndPush(destinationTile + WN + 1, hexes); + BattleHex::checkAndPush(destinationTile + WN + 2, hexes); + BattleHex::checkAndPush(destinationTile - WN, hexes); + BattleHex::checkAndPush(destinationTile - WN + 1, hexes); + } + } + if(hex == destinationTile.cloneInDirection(BattleHex::EDir::BOTTOM_RIGHT, false)) { + + if(destinationTile.getY() % 2 == 1) + { + BattleHex::checkAndPush(destinationTile, hexes); + BattleHex::checkAndPush(destinationTile - 1, hexes); + BattleHex::checkAndPush(destinationTile + WN - 1, hexes); + BattleHex::checkAndPush(destinationTile + WN - 2, hexes); + BattleHex::checkAndPush(destinationTile - WN, hexes); + BattleHex::checkAndPush(destinationTile - WN - 1, hexes); + } + else + { + BattleHex::checkAndPush(destinationTile, hexes); + BattleHex::checkAndPush(destinationTile - 1, hexes); + BattleHex::checkAndPush(destinationTile + WN, hexes); + BattleHex::checkAndPush(destinationTile + WN - 1, hexes); + BattleHex::checkAndPush(destinationTile - WN, hexes); + BattleHex::checkAndPush(destinationTile - WN + 1, hexes); + } + + } + + for(BattleHex tile : hexes) + { + //friendly stacks can also be damaged by Dragon Breath + if(battleGetStackByPos(tile, true)) + 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 hexes; //only one, in fact + int pseudoVector = destinationTile.hex - hex; + switch(pseudoVector) + { + case 1: + case -1: + BattleHex::checkAndPush(destinationTile.hex + pseudoVector, hexes); + break; + case WN: //17 //left-down or right-down + case -WN: //-17 //left-up or right-up + case WN + 1: //18 //right-down + case -WN + 1: //-16 //right-up + BattleHex::checkAndPush(destinationTile.hex + pseudoVector + (((hex / WN) % 2) ? 1 : -1), hexes); + break; + case WN - 1: //16 //left-down + case -WN - 1: //-18 //left-up + BattleHex::checkAndPush(destinationTile.hex + pseudoVector + (((hex / WN) % 2) ? 1 : 0), hexes); + break; + } + for(BattleHex tile : hexes) + { + //friendly stacks can also be damaged by Dragon Breath + if(battleGetStackByPos(tile, true)) + 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 hexes; //only one, in fact - int pseudoVector = destinationTile.hex - hex; - switch (pseudoVector) - { - case 1: - case -1: - BattleHex::checkAndPush(destinationTile.hex + pseudoVector, hexes); - break; - case WN: //17 //left-down or right-down - case -WN: //-17 //left-up or right-up - case WN + 1: //18 //right-down - case -WN + 1: //-16 //right-up - BattleHex::checkAndPush(destinationTile.hex + pseudoVector + (((hex / WN) % 2) ? 1 : -1), hexes); - break; - case WN - 1: //16 //left-down - case -WN - 1: //-18 //left-up - BattleHex::checkAndPush(destinationTile.hex + pseudoVector + (((hex / WN) % 2) ? 1 : 0), hexes); - break; - } - for (BattleHex tile : hexes) - { - //friendly stacks can also be damaged by Dragon Breath - if (battleGetStackByPos(tile, true)) - at.friendlyCreaturePositions.insert(tile); - } - } - return at; } -std::set CBattleInfoCallback::getAttackedCreatures(const CStack* attacker, BattleHex destinationTile, BattleHex attackerPos) const +std::set CBattleInfoCallback::getAttackedCreatures(const CStack* attacker, BattleHex destinationTile, bool rangedAttack, BattleHex attackerPos) const { std::set 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 { const CStack * st = battleGetStackByPos(tile, true); diff --git a/lib/battle/CBattleInfoCallback.h b/lib/battle/CBattleInfoCallback.h index 0f4ff00d9..e7b19dbaf 100644 --- a/lib/battle/CBattleInfoCallback.h +++ b/lib/battle/CBattleInfoCallback.h @@ -92,8 +92,8 @@ public: 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) - AttackableTiles getPotentiallyAttackableHexes(const CStack* attacker, BattleHex destinationTile, BattleHex attackerPos) const; //TODO: apply rotation to two-hex attacker - std::set getAttackedCreatures(const CStack* attacker, BattleHex destinationTile, BattleHex attackerPos = BattleHex::INVALID) const; //calculates range of multi-hex attacks + AttackableTiles getPotentiallyAttackableHexes(const CStack* attacker, BattleHex destinationTile, BattleHex attackerPos, bool rangedAttack) const; //TODO: apply rotation to two-hex attacker + std::set 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 isToReverseHlp(BattleHex hexFrom, BattleHex hexTo, bool curDir) const; //helper for isToReverse diff --git a/server/CGameHandler.cpp b/server/CGameHandler.cpp index 56f182339..ce6c90c41 100644 --- a/server/CGameHandler.cpp +++ b/server/CGameHandler.cpp @@ -892,16 +892,14 @@ void CGameHandler::prepareAttack(BattleAttack &bat, const CStack *att, const CSt // only primary target applyBattleEffects(bat, att, def, distance, false); - if (!bat.shot()) //multiple-hex attack - only in meele - { - std::set attackedCreatures = gs->curB->getAttackedCreatures(att, targetHex); //creatures other than primary target + //multiple-hex normal attack + std::set attackedCreatures = gs->curB->getAttackedCreatures(att, targetHex, bat.shot()); //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); } } @@ -914,11 +912,11 @@ void CGameHandler::prepareAttack(BattleAttack &bat, const CStack *att, const CSt //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 - for (const CStack * stack : attackedCreatures) + for (const CStack * stack : affectedCreatures) { if (stack != def) //do not hit same stack twice { From d2e9848443ec112dc47dfcc70d67208c03007edd Mon Sep 17 00:00:00 2001 From: Dydzio Date: Mon, 4 Sep 2017 23:35:48 +0200 Subject: [PATCH 05/12] Fix RANGED_RETALIATION bug --- server/CGameHandler.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/server/CGameHandler.cpp b/server/CGameHandler.cpp index ce6c90c41..7a87121a0 100644 --- a/server/CGameHandler.cpp +++ b/server/CGameHandler.cpp @@ -4009,6 +4009,7 @@ bool CGameHandler::makeBattleAction(BattleAction &ba) if (destinationStack->hasBonusOfType(Bonus::RANGED_RETALIATION) && !stack->hasBonusOfType(Bonus::BLOCKS_RANGED_RETALIATION) && destinationStack->ableToRetaliate() + && gs->curB->battleCanShoot(destinationStack, stack->position) && stack->alive()) //attacker may have died (fire shield) { BattleAttack bat; From 5c17e172b684a5b4d8a3d211be0a67970c019bb7 Mon Sep 17 00:00:00 2001 From: Dydzio Date: Tue, 5 Sep 2017 22:29:31 +0200 Subject: [PATCH 06/12] Revert some changes, update WIDE_BREATH --- lib/battle/CBattleInfoCallback.cpp | 256 ++++++++--------------------- lib/battle/CBattleInfoCallback.h | 2 +- 2 files changed, 69 insertions(+), 189 deletions(-) diff --git a/lib/battle/CBattleInfoCallback.cpp b/lib/battle/CBattleInfoCallback.cpp index e4bade27e..12e26ad26 100644 --- a/lib/battle/CBattleInfoCallback.cpp +++ b/lib/battle/CBattleInfoCallback.cpp @@ -1120,7 +1120,7 @@ ReachabilityInfo CBattleInfoCallback::getFlyingReachability(const ReachabilityIn return ret; } -AttackableTiles CBattleInfoCallback::getPotentiallyAttackableHexes (const CStack* attacker, BattleHex destinationTile, BattleHex attackerPos, bool rangedAttack) const +AttackableTiles CBattleInfoCallback::getPotentiallyAttackableHexes (const CStack* attacker, BattleHex destinationTile, BattleHex attackerPos) const { //does not return hex attacked directly //TODO: apply rotation to two-hex attackers @@ -1129,7 +1129,7 @@ AttackableTiles CBattleInfoCallback::getPotentiallyAttackableHexes (const CStack AttackableTiles at; RETURN_IF_NOT_BATTLE(at); - if(rangedAttack) + /*if(rangedAttack) { if(attacker->hasBonusOfType(Bonus::SHOOTS_ALL_ADJACENT) && !vstd::contains(attackerPos.neighbouringTiles(), destinationTile)) { @@ -1137,202 +1137,82 @@ AttackableTiles CBattleInfoCallback::getPotentiallyAttackableHexes (const CStack targetHexes.push_back(destinationTile); boost::copy(targetHexes, vstd::set_inserter(at.hostileCreaturePositions)); } + }*/ + + const int WN = GameConstants::BFIELD_WIDTH; + 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) + bool reverse = isToReverse(hex, destinationTile, isAttacker, attacker->doubleWide(), isAttacker); + if(reverse && attacker->doubleWide()) + { + hex = attacker->occupiedHex(hex); //the other hex stack stands on + } + if(attacker->hasBonusOfType(Bonus::ATTACKS_ALL_ADJACENT)) + { + boost::copy(attacker->getSurroundingHexes(attackerPos), vstd::set_inserter(at.hostileCreaturePositions)); + } + if(attacker->hasBonusOfType(Bonus::THREE_HEADED_ATTACK)) + { + std::vector hexes = attacker->getSurroundingHexes(attackerPos); + for(BattleHex tile : hexes) + { + 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); + if(st && st->owner != attacker->owner) //only hostile stacks - does it work well with Berserk? + { + at.hostileCreaturePositions.insert(tile); + } + } + } } - else + if(attacker->hasBonusOfType(Bonus::WIDE_BREATH)) { - const int WN = GameConstants::BFIELD_WIDTH; - 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) - bool reverse = isToReverse(hex, destinationTile, isAttacker, attacker->doubleWide(), isAttacker); - if(reverse && attacker->doubleWide()) + std::vector hexes = destinationTile.neighbouringTiles(); + for(int i = 0; ioccupiedHex(hex); //the other hex stack stands on - } - if(attacker->hasBonusOfType(Bonus::ATTACKS_ALL_ADJACENT)) - { - boost::copy(attacker->getSurroundingHexes(attackerPos), vstd::set_inserter(at.hostileCreaturePositions)); - } - if(attacker->hasBonusOfType(Bonus::THREE_HEADED_ATTACK)) - { - std::vector hexes = attacker->getSurroundingHexes(attackerPos); - for(BattleHex tile : hexes) - { - 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); - if(st && st->owner != attacker->owner) //only hostile stacks - does it work well with Berserk? - { - at.hostileCreaturePositions.insert(tile); - } - } + if(hexes.at(i) == hex) { + hexes.erase(hexes.begin() + i); + i = 0; } } - if(attacker->hasBonusOfType(Bonus::WIDE_BREATH)) + for(BattleHex tile : hexes) { - std::vector hexes; - // attack stand - if(hex == destinationTile.cloneInDirection(BattleHex::EDir::LEFT, false)) { - if(destinationTile.getY() % 2 == 0) - { - BattleHex::checkAndPush(destinationTile, hexes); - BattleHex::checkAndPush(destinationTile + 1, hexes); - BattleHex::checkAndPush(destinationTile + WN, hexes); - BattleHex::checkAndPush(destinationTile + WN + 1, hexes); - BattleHex::checkAndPush(destinationTile + (2 * WN) - 1, hexes); - BattleHex::checkAndPush(destinationTile + (2 * WN), hexes); - } - else { - BattleHex::checkAndPush(destinationTile, hexes); - BattleHex::checkAndPush(destinationTile + 1, hexes); - BattleHex::checkAndPush(destinationTile + WN, hexes); - BattleHex::checkAndPush(destinationTile + WN - 1, hexes); - BattleHex::checkAndPush(destinationTile + (2 * WN) - 1, hexes); - BattleHex::checkAndPush(destinationTile + (2 * WN), hexes); - } - - } - if(hex == destinationTile.cloneInDirection(BattleHex::EDir::RIGHT, false)) { - if(destinationTile.getY() % 2 == 0) - { - BattleHex::checkAndPush(destinationTile, hexes); - BattleHex::checkAndPush(destinationTile - 1, hexes); - BattleHex::checkAndPush(destinationTile + WN, hexes); - BattleHex::checkAndPush(destinationTile + WN + 1, hexes); - BattleHex::checkAndPush(destinationTile + (2 * WN), hexes); - BattleHex::checkAndPush(destinationTile + (2 * WN) + 1, hexes); - } - else - { - BattleHex::checkAndPush(destinationTile, hexes); - BattleHex::checkAndPush(destinationTile - 1, hexes); - BattleHex::checkAndPush(destinationTile + WN, hexes); - BattleHex::checkAndPush(destinationTile + WN - 1, hexes); - BattleHex::checkAndPush(destinationTile + (2 * WN), hexes); - BattleHex::checkAndPush(destinationTile + (2 * WN) + 1, hexes); - } - } - // attack down - if(hex == destinationTile.cloneInDirection(BattleHex::EDir::TOP_LEFT, false)) { - if(destinationTile.getY() % 2 == 1) - { - BattleHex::checkAndPush(destinationTile, hexes); - BattleHex::checkAndPush(destinationTile - 1, hexes); - BattleHex::checkAndPush(destinationTile - 2, hexes); - BattleHex::checkAndPush(destinationTile + WN, hexes); - BattleHex::checkAndPush(destinationTile + WN - 1, hexes); - BattleHex::checkAndPush(destinationTile + WN - 2, hexes); - } - else { - BattleHex::checkAndPush(destinationTile, hexes); - BattleHex::checkAndPush(destinationTile - 1, hexes); - BattleHex::checkAndPush(destinationTile - 2, hexes); - BattleHex::checkAndPush(destinationTile + WN, hexes); - BattleHex::checkAndPush(destinationTile + WN + 1, hexes); - BattleHex::checkAndPush(destinationTile + WN - 1, hexes); - } - } - if(hex == destinationTile.cloneInDirection(BattleHex::EDir::TOP_RIGHT, false)) { - - if(destinationTile.getY() % 2 == 1) - { - BattleHex::checkAndPush(destinationTile, hexes); - BattleHex::checkAndPush(destinationTile + 1, hexes); - BattleHex::checkAndPush(destinationTile + 2, hexes); - BattleHex::checkAndPush(destinationTile + WN - 1, hexes); - BattleHex::checkAndPush(destinationTile + WN, hexes); - BattleHex::checkAndPush(destinationTile + WN + 1, hexes); - } - else - { - BattleHex::checkAndPush(destinationTile, hexes); - BattleHex::checkAndPush(destinationTile + 1, hexes); - BattleHex::checkAndPush(destinationTile + 2, hexes); - BattleHex::checkAndPush(destinationTile + WN, hexes); - BattleHex::checkAndPush(destinationTile + WN + 1, hexes); - BattleHex::checkAndPush(destinationTile + WN + 2, hexes); - } - - } - // attack up - if(hex == destinationTile.cloneInDirection(BattleHex::EDir::BOTTOM_LEFT, false)) { - if(destinationTile.getY() % 2 == 1) - { - BattleHex::checkAndPush(destinationTile, hexes); - BattleHex::checkAndPush(destinationTile + 1, hexes); - BattleHex::checkAndPush(destinationTile + WN, hexes); - BattleHex::checkAndPush(destinationTile + WN + 1, hexes); - BattleHex::checkAndPush(destinationTile - WN, hexes); - BattleHex::checkAndPush(destinationTile - WN - 1, hexes); - } - else { - BattleHex::checkAndPush(destinationTile, hexes); - BattleHex::checkAndPush(destinationTile + 1, hexes); - BattleHex::checkAndPush(destinationTile + WN + 1, hexes); - BattleHex::checkAndPush(destinationTile + WN + 2, hexes); - BattleHex::checkAndPush(destinationTile - WN, hexes); - BattleHex::checkAndPush(destinationTile - WN + 1, hexes); - } - } - if(hex == destinationTile.cloneInDirection(BattleHex::EDir::BOTTOM_RIGHT, false)) { - - if(destinationTile.getY() % 2 == 1) - { - BattleHex::checkAndPush(destinationTile, hexes); - BattleHex::checkAndPush(destinationTile - 1, hexes); - BattleHex::checkAndPush(destinationTile + WN - 1, hexes); - BattleHex::checkAndPush(destinationTile + WN - 2, hexes); - BattleHex::checkAndPush(destinationTile - WN, hexes); - BattleHex::checkAndPush(destinationTile - WN - 1, hexes); - } - else - { - BattleHex::checkAndPush(destinationTile, hexes); - BattleHex::checkAndPush(destinationTile - 1, hexes); - BattleHex::checkAndPush(destinationTile + WN, hexes); - BattleHex::checkAndPush(destinationTile + WN - 1, hexes); - BattleHex::checkAndPush(destinationTile - WN, hexes); - BattleHex::checkAndPush(destinationTile - WN + 1, hexes); - } - - } - - for(BattleHex tile : hexes) - { - //friendly stacks can also be damaged by Dragon Breath - if(battleGetStackByPos(tile, true)) + //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 + 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 hexes; //only one, in fact + int pseudoVector = destinationTile.hex - hex; + switch(pseudoVector) { - std::vector hexes; //only one, in fact - int pseudoVector = destinationTile.hex - hex; - switch(pseudoVector) - { - case 1: - case -1: - BattleHex::checkAndPush(destinationTile.hex + pseudoVector, hexes); - break; - case WN: //17 //left-down or right-down - case -WN: //-17 //left-up or right-up - case WN + 1: //18 //right-down - case -WN + 1: //-16 //right-up - BattleHex::checkAndPush(destinationTile.hex + pseudoVector + (((hex / WN) % 2) ? 1 : -1), hexes); - break; - case WN - 1: //16 //left-down - case -WN - 1: //-18 //left-up - BattleHex::checkAndPush(destinationTile.hex + pseudoVector + (((hex / WN) % 2) ? 1 : 0), hexes); - break; - } - for(BattleHex tile : hexes) - { - //friendly stacks can also be damaged by Dragon Breath - if(battleGetStackByPos(tile, true)) - at.friendlyCreaturePositions.insert(tile); - } + case 1: + case -1: + BattleHex::checkAndPush(destinationTile.hex + pseudoVector, hexes); + break; + case WN: //17 //left-down or right-down + case -WN: //-17 //left-up or right-up + case WN + 1: //18 //right-down + case -WN + 1: //-16 //right-up + BattleHex::checkAndPush(destinationTile.hex + pseudoVector + (((hex / WN) % 2) ? 1 : -1), hexes); + break; + case WN - 1: //16 //left-down + case -WN - 1: //-18 //left-up + BattleHex::checkAndPush(destinationTile.hex + pseudoVector + (((hex / WN) % 2) ? 1 : 0), hexes); + break; + } + for(BattleHex tile : hexes) + { + //friendly stacks can also be damaged by Dragon Breath + if(battleGetStackByPos(tile, true)) + at.friendlyCreaturePositions.insert(tile); } } return at; @@ -1343,7 +1223,7 @@ std::set CBattleInfoCallback::getAttackedCreatures(const CStack* std::set attackedCres; RETURN_IF_NOT_BATTLE(attackedCres); - AttackableTiles at = getPotentiallyAttackableHexes(attacker, destinationTile, attackerPos, rangedAttack); + AttackableTiles at = getPotentiallyAttackableHexes(attacker, destinationTile, attackerPos); for (BattleHex tile : at.hostileCreaturePositions) //all around & three-headed attack { const CStack * st = battleGetStackByPos(tile, true); diff --git a/lib/battle/CBattleInfoCallback.h b/lib/battle/CBattleInfoCallback.h index e7b19dbaf..b3ad93464 100644 --- a/lib/battle/CBattleInfoCallback.h +++ b/lib/battle/CBattleInfoCallback.h @@ -92,7 +92,7 @@ public: 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) - AttackableTiles getPotentiallyAttackableHexes(const CStack* attacker, BattleHex destinationTile, BattleHex attackerPos, bool rangedAttack) 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 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 isToReverseHlp(BattleHex hexFrom, BattleHex hexTo, bool curDir) const; //helper for isToReverse From 59764d83497c6bd320e1e62eb30b543363bd13fb Mon Sep 17 00:00:00 2001 From: Dydzio Date: Tue, 5 Sep 2017 23:45:31 +0200 Subject: [PATCH 07/12] Implemented SHOOTS_ALL_ADJACENT --- lib/battle/CBattleInfoCallback.cpp | 34 ++++++++++++++++++++---------- lib/battle/CBattleInfoCallback.h | 1 + 2 files changed, 24 insertions(+), 11 deletions(-) diff --git a/lib/battle/CBattleInfoCallback.cpp b/lib/battle/CBattleInfoCallback.cpp index 12e26ad26..37e2d8f6e 100644 --- a/lib/battle/CBattleInfoCallback.cpp +++ b/lib/battle/CBattleInfoCallback.cpp @@ -1129,16 +1129,6 @@ AttackableTiles CBattleInfoCallback::getPotentiallyAttackableHexes (const CStack AttackableTiles at; RETURN_IF_NOT_BATTLE(at); - /*if(rangedAttack) - { - if(attacker->hasBonusOfType(Bonus::SHOOTS_ALL_ADJACENT) && !vstd::contains(attackerPos.neighbouringTiles(), destinationTile)) - { - std::vector targetHexes = destinationTile.neighbouringTiles(); - targetHexes.push_back(destinationTile); - boost::copy(targetHexes, vstd::set_inserter(at.hostileCreaturePositions)); - } - }*/ - const int WN = GameConstants::BFIELD_WIDTH; BattleHex hex = (attackerPos != BattleHex::INVALID) ? attackerPos.hex : attacker->position.hex; //real or hypothetical (cursor) position @@ -1218,12 +1208,34 @@ AttackableTiles CBattleInfoCallback::getPotentiallyAttackableHexes (const CStack 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 targetHexes = destinationTile.neighbouringTiles(); + targetHexes.push_back(destinationTile); + boost::copy(targetHexes, vstd::set_inserter(at.hostileCreaturePositions)); + } + + return at; +} + std::set CBattleInfoCallback::getAttackedCreatures(const CStack* attacker, BattleHex destinationTile, bool rangedAttack, BattleHex attackerPos) const { std::set 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 { const CStack * st = battleGetStackByPos(tile, true); diff --git a/lib/battle/CBattleInfoCallback.h b/lib/battle/CBattleInfoCallback.h index b3ad93464..324925996 100644 --- a/lib/battle/CBattleInfoCallback.h +++ b/lib/battle/CBattleInfoCallback.h @@ -93,6 +93,7 @@ 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) AttackableTiles getPotentiallyAttackableHexes(const CStack* attacker, BattleHex destinationTile, BattleHex attackerPos) const; //TODO: apply rotation to two-hex attacker + AttackableTiles getPotentiallyShootableHexes(const CStack* attacker, BattleHex destinationTile, BattleHex attackerPos) const; std::set 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 isToReverseHlp(BattleHex hexFrom, BattleHex hexTo, bool curDir) const; //helper for isToReverse From 7cbd9acd6764f57cff5943fd3bb9fdea33965a06 Mon Sep 17 00:00:00 2001 From: Dydzio Date: Wed, 6 Sep 2017 00:03:32 +0200 Subject: [PATCH 08/12] Add BLOCK_MAGIC_BELOW bonus --- lib/HeroBonus.h | 1 + lib/battle/CBattleInfoCallback.cpp | 18 ++++++++++++++++++ lib/battle/CBattleInfoCallback.h | 1 + lib/spells/CSpellHandler.cpp | 2 +- 4 files changed, 21 insertions(+), 1 deletion(-) diff --git a/lib/HeroBonus.h b/lib/HeroBonus.h index 1b9e46651..8a72f9ce8 100644 --- a/lib/HeroBonus.h +++ b/lib/HeroBonus.h @@ -239,6 +239,7 @@ private: 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 */ \ /* end of list */ diff --git a/lib/battle/CBattleInfoCallback.cpp b/lib/battle/CBattleInfoCallback.cpp index 37e2d8f6e..d093cf284 100644 --- a/lib/battle/CBattleInfoCallback.cpp +++ b/lib/battle/CBattleInfoCallback.cpp @@ -1647,6 +1647,24 @@ int CBattleInfoCallback::battleGetSurrenderCost(PlayerColor Player) const 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 1; + + auto b = node->getBonuses(Selector::type(Bonus::BLOCK_MAGIC_BELOW)); + if(b->size()) + return b->totalValue(); + + return 1; +} + si8 CBattleInfoCallback::battleMaxSpellLevel(ui8 side) const { const IBonusBearer *node = nullptr; diff --git a/lib/battle/CBattleInfoCallback.h b/lib/battle/CBattleInfoCallback.h index 324925996..44657922e 100644 --- a/lib/battle/CBattleInfoCallback.h +++ b/lib/battle/CBattleInfoCallback.h @@ -75,6 +75,7 @@ public: std::vector getAttackableBattleHexes() const; //*** 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 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 diff --git a/lib/spells/CSpellHandler.cpp b/lib/spells/CSpellHandler.cpp index db85e254e..0617076d3 100644 --- a/lib/spells/CSpellHandler.cpp +++ b/lib/spells/CSpellHandler.cpp @@ -194,7 +194,7 @@ ESpellCastProblem::ESpellCastProblem CSpell::canBeCast(const CBattleInfoCallback //effect like Recanter's Cloak. Blocks also passive casting. //TODO: check creature abilities to block //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; const ESpellCastProblem::ESpellCastProblem specificProblem = mechanics->canBeCast(cb, mode, caster); From 3eef689005383feb8d6933f8f041f231c70c1760 Mon Sep 17 00:00:00 2001 From: Dydzio Date: Sat, 9 Sep 2017 21:01:12 +0200 Subject: [PATCH 09/12] Add TERMINATOR ability support, small fixes --- config/bonuses_texts.json | 12 ++++++++--- lib/HeroBonus.h | 1 + lib/battle/CBattleInfoCallback.cpp | 4 ++-- server/CGameHandler.cpp | 32 ++++++++++++++++++++++++++++++ 4 files changed, 44 insertions(+), 5 deletions(-) diff --git a/config/bonuses_texts.json b/config/bonuses_texts.json index 8864751ce..46b9eacd7 100644 --- a/config/bonuses_texts.json +++ b/config/bonuses_texts.json @@ -420,6 +420,12 @@ "description": "This creature is vulnerable to synergy effect" }, + "TERMINATOR": + { + "name": "Terminator", + "description": "Has ${val}% chance to kill extra units after attack" + }, + "TWO_HEX_ATTACK_BREATH": { "name": "Breath", @@ -454,11 +460,11 @@ { "name": "Water immunity", "description": "Immune to all Water school spells" - } + }, "WIDE_BREATH": { - "name": "Szerokie Zionięcie", - "description": "Stożek ognia obejmuje znacznie większy obszar" + "name": "Wide breath", + "description": "Wide breath attack (multiple hexes)" } } diff --git a/lib/HeroBonus.h b/lib/HeroBonus.h index 8a72f9ce8..8c4efefdc 100644 --- a/lib/HeroBonus.h +++ b/lib/HeroBonus.h @@ -240,6 +240,7 @@ private: 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(TERMINATOR) /*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 */ diff --git a/lib/battle/CBattleInfoCallback.cpp b/lib/battle/CBattleInfoCallback.cpp index d093cf284..30918194f 100644 --- a/lib/battle/CBattleInfoCallback.cpp +++ b/lib/battle/CBattleInfoCallback.cpp @@ -1656,13 +1656,13 @@ si8 CBattleInfoCallback::battleMinSpellLevel(ui8 side) const node = getBattleNode(); if(!node) - return 1; + return 0; auto b = node->getBonuses(Selector::type(Bonus::BLOCK_MAGIC_BELOW)); if(b->size()) return b->totalValue(); - return 1; + return 0; } si8 CBattleInfoCallback::battleMaxSpellLevel(ui8 side) const diff --git a/server/CGameHandler.cpp b/server/CGameHandler.cpp index 7a87121a0..893410604 100644 --- a/server/CGameHandler.cpp +++ b/server/CGameHandler.cpp @@ -5418,6 +5418,38 @@ void CGameHandler::handleAfterAttackCasting(const BattleAttack & bat) sendAndApply(&victimInfo); sendAndApply(&resurrectInfo); } + if(attacker->hasBonusOfType(Bonus::TERMINATOR, 0) || attacker->hasBonusOfType(Bonus::TERMINATOR, 1)) + { + double chanceToTrigger = 0; + int amountToDie = 0; + + if(attacker->hasBonusOfType(Bonus::TERMINATOR, 0)) //killing by percentage + { + chanceToTrigger = attacker->valOfBonuses(Bonus::TERMINATOR, 0) / 100.0f; + int percentageToDie = attacker->getBonus(Selector::type(Bonus::TERMINATOR).And(Selector::subtype(0)))->additionalInfo; + amountToDie = defender->getCount() * (percentageToDie / 100.0); + + } + else if(attacker->hasBonusOfType(Bonus::TERMINATOR, 1)) //killing by count + { + chanceToTrigger = attacker->valOfBonuses(Bonus::TERMINATOR, 1) / 100.0f; + amountToDie = attacker->getBonus(Selector::type(Bonus::TERMINATOR).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; + attacker->prepareAttacked(bsa, getRandomGenerator()); + sendAndApply(&bsa); + } } void CGameHandler::visitObjectOnTile(const TerrainTile &t, const CGHeroInstance * h) From eab8e8f869e0d1bbc75d505e474169418461f7d3 Mon Sep 17 00:00:00 2001 From: Dydzio Date: Sat, 9 Sep 2017 21:27:22 +0200 Subject: [PATCH 10/12] Fixed "commit merge" fail --- lib/HeroBonus.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/HeroBonus.h b/lib/HeroBonus.h index 716330f2e..7c6d4c525 100644 --- a/lib/HeroBonus.h +++ b/lib/HeroBonus.h @@ -234,8 +234,8 @@ private: BONUS_NAME(SUMMON_GUARDIANS) /*val - amount in % of stack count, subtype = creature ID*/\ 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(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(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(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 */\ From da6d01b0c723ce731ed5520a8f448cc593c2a691 Mon Sep 17 00:00:00 2001 From: Dydzio Date: Mon, 13 Nov 2017 01:59:41 +0100 Subject: [PATCH 11/12] Ability rename + bugfix + changelog extend --- ChangeLog | 6 ++++++ config/bonuses_texts.json | 12 ++++++------ lib/HeroBonus.h | 2 +- server/CGameHandler.cpp | 19 +++++++++---------- 4 files changed, 22 insertions(+), 17 deletions(-) diff --git a/ChangeLog b/ChangeLog index 57add05bf..db8795119 100644 --- a/ChangeLog +++ b/ChangeLog @@ -13,6 +13,12 @@ GENERAL: - BLOCKS_RANGED_RETALIATION - disallow enemy ranged counterattack - SECONDARY_SKILL_VAL2 - set additional parameter for certain secondary skills - 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: * Implemented cumulative effects for spells diff --git a/config/bonuses_texts.json b/config/bonuses_texts.json index 46b9eacd7..6b78376ab 100644 --- a/config/bonuses_texts.json +++ b/config/bonuses_texts.json @@ -96,6 +96,12 @@ "description": "+${val} Defense when defending" }, + "DESTRUCTION": + { + "name": "Destruction", + "description": "Has ${val}% chance to kill extra units after attack" + }, + "DOUBLE_DAMAGE_CHANCE": { "name": "Death Blow", @@ -420,12 +426,6 @@ "description": "This creature is vulnerable to synergy effect" }, - "TERMINATOR": - { - "name": "Terminator", - "description": "Has ${val}% chance to kill extra units after attack" - }, - "TWO_HEX_ATTACK_BREATH": { "name": "Breath", diff --git a/lib/HeroBonus.h b/lib/HeroBonus.h index 7c6d4c525..2f76c76ea 100644 --- a/lib/HeroBonus.h +++ b/lib/HeroBonus.h @@ -242,7 +242,7 @@ private: 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(TERMINATOR) /*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*/ \ + 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 */ diff --git a/server/CGameHandler.cpp b/server/CGameHandler.cpp index fd53f2b57..7a32574d0 100644 --- a/server/CGameHandler.cpp +++ b/server/CGameHandler.cpp @@ -5417,22 +5417,21 @@ void CGameHandler::handleAfterAttackCasting(const BattleAttack & bat) sendAndApply(&victimInfo); sendAndApply(&resurrectInfo); } - if(attacker->hasBonusOfType(Bonus::TERMINATOR, 0) || attacker->hasBonusOfType(Bonus::TERMINATOR, 1)) + if(attacker->hasBonusOfType(Bonus::DESTRUCTION, 0) || attacker->hasBonusOfType(Bonus::DESTRUCTION, 1)) { double chanceToTrigger = 0; int amountToDie = 0; - if(attacker->hasBonusOfType(Bonus::TERMINATOR, 0)) //killing by percentage + if(attacker->hasBonusOfType(Bonus::DESTRUCTION, 0)) //killing by percentage { - chanceToTrigger = attacker->valOfBonuses(Bonus::TERMINATOR, 0) / 100.0f; - int percentageToDie = attacker->getBonus(Selector::type(Bonus::TERMINATOR).And(Selector::subtype(0)))->additionalInfo; - amountToDie = defender->getCount() * (percentageToDie / 100.0); - + 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::TERMINATOR, 1)) //killing by count + else if(attacker->hasBonusOfType(Bonus::DESTRUCTION, 1)) //killing by count { - chanceToTrigger = attacker->valOfBonuses(Bonus::TERMINATOR, 1) / 100.0f; - amountToDie = attacker->getBonus(Selector::type(Bonus::TERMINATOR).And(Selector::subtype(1)))->additionalInfo; + 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% @@ -5446,7 +5445,7 @@ void CGameHandler::handleAfterAttackCasting(const BattleAttack & bat) bsa.damageAmount = amountToDie * defender->getCreature()->MaxHealth(); bsa.flags = BattleStackAttacked::SPELL_EFFECT; bsa.spellID = SpellID::SLAYER; - attacker->prepareAttacked(bsa, getRandomGenerator()); + defender->prepareAttacked(bsa, getRandomGenerator()); sendAndApply(&bsa); } } From c6731f3b90f1d21c553a9a0761cdce8f590be297 Mon Sep 17 00:00:00 2001 From: Dydzio Date: Mon, 13 Nov 2017 14:13:55 +0100 Subject: [PATCH 12/12] Fix code formatting --- lib/battle/CBattleInfoCallback.cpp | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/lib/battle/CBattleInfoCallback.cpp b/lib/battle/CBattleInfoCallback.cpp index 30918194f..aa167894e 100644 --- a/lib/battle/CBattleInfoCallback.cpp +++ b/lib/battle/CBattleInfoCallback.cpp @@ -186,7 +186,7 @@ std::set CBattleInfoCallback::battleGetAttackedHexes(const CStack* at { std::set attackedHexes; RETURN_IF_NOT_BATTLE(attackedHexes); - + AttackableTiles at = getPotentiallyAttackableHexes(attacker, destinationTile, attackerPos); for (BattleHex tile : at.hostileCreaturePositions) @@ -1157,13 +1157,13 @@ AttackableTiles CBattleInfoCallback::getPotentiallyAttackableHexes (const CStack } } } - if(attacker->hasBonusOfType(Bonus::WIDE_BREATH)) { std::vector hexes = destinationTile.neighbouringTiles(); for(int i = 0; ihasBonusOfType(Bonus::TWO_HEX_ATTACK_BREATH) && BattleHex::mutualPosition(destinationTile.hex, hex) > -1) //only adjacent hexes are subject of dragon breath calculation { std::vector hexes; //only one, in fact @@ -1649,7 +1650,7 @@ int CBattleInfoCallback::battleGetSurrenderCost(PlayerColor Player) const si8 CBattleInfoCallback::battleMinSpellLevel(ui8 side) const { - const IBonusBearer *node = nullptr; + const IBonusBearer * node = nullptr; if(const CGHeroInstance * h = battleGetFightingHero(side)) node = h; else