diff --git a/config/bonusnames.json b/config/bonusnames.json index 27c657a85..68ad814e0 100644 --- a/config/bonusnames.json +++ b/config/bonusnames.json @@ -28,7 +28,7 @@ { "id": "MAGIC_RESISTANCE", "name": "Magic Resistance", "description": "%d% chance to resist enemy spell" }, { "id": "NO_DISTANCE_PENALTY", "name": "No distance penalty", "description": "Full damage from any distance" }, { "id": "NO_MELEE_PENALTY", "name": "No melee penalty", "description": "Creature has no Melee Penalty" }, - { "id": "NO_OBSTACLES_PENALTY", "name": "No obstacle penalty", "description": "Creature has no Obstacle Penalty" }, + { "id": "NO_WALL_PENALTY", "name": "No wall penalty", "description": "Full damage during siege" }, { "id": "BLOCKS_RETALIATION", "name": "No retaliation", "description": "Enemy cannot Retaliate" }, { "id": "NON_LIVING", "name": "Non living", "description": "Immunity to many effects" }, { "id": "SELF_LUCK", "name": "Positive luck", "description": "Always has Positive Luck" }, diff --git a/config/creatures.json b/config/creatures.json index 109f544a0..f47b10251 100644 --- a/config/creatures.json +++ b/config/creatures.json @@ -361,7 +361,7 @@ "level": 4, "name": [ "Mage" ], "faction": 2, - "ability_add": [ [ "CHANGES_SPELL_COST_FOR_ALLY", -2, 0, 0 ] ], //mages reduce spell cost + "ability_add": [ [ "CHANGES_SPELL_COST_FOR_ALLY", 2, 0, 0 ] ], //mages reduce spell cost "upgrade": 35, "defname": "CMAGE.DEF", "projectile_defname": "PMAGEX.DEF", @@ -374,7 +374,7 @@ "level": 4, "name": [ "ArchMage" ], "faction": 2, - "ability_add": [ [ "CHANGES_SPELL_COST_FOR_ALLY", -2, 0, 0 ] ], //archmages reduce spell cost //genies hate efreets + "ability_add": [ [ "CHANGES_SPELL_COST_FOR_ALLY", 2, 0, 0 ]], //archmages reduce spell cost "defname": "CAMAGE.DEF", "projectile_defname": "PMAGEX.DEF", "projectile_spin": false @@ -1416,7 +1416,7 @@ "level": 6, "name": [ "Enchanter" ], "faction": -1, - "ability_add": [ [ "NO_OBSTACLES_PENALTY", 0, 0, 0 ], + "ability_add": [ [ "NO_WALL_PENALTY", 0, 0, 0 ], [ "ENCHANTER", 3, 28, 3], //air shield [ "ENCHANTER", 3, 41, 3], //bless [ "ENCHANTER", 3, 45, 3], //wealness @@ -1434,7 +1434,7 @@ "level": 4, "name": [ "Sharpshooter" ], "faction": -1, - "ability_add": [ [ "NO_OBSTACLES_PENALTY", 0, 0, 0 ], + "ability_add": [ [ "NO_WALL_PENALTY", 0, 0, 0 ], [ "NO_DISTANCE_PENALTY", 0, 0, 0 ] ], //Sharpshooter "defname": "CSHARP.DEF", "projectile_defname": "PELFX.DEF", diff --git a/lib/BattleState.cpp b/lib/BattleState.cpp index ff47ed73e..211f855d5 100644 --- a/lib/BattleState.cpp +++ b/lib/BattleState.cpp @@ -657,11 +657,17 @@ TDmgRange BattleInfo::calculateDmgRange( const CStack* attacker, const CStack* d //wall / distance penalty + advanced air shield bool distPenalty = !NBonus::hasOfType(attackerHero, Bonus::NO_DISTANCE_PENALTY) && hasDistancePenalty(attacker, defender->position); - bool obstaclePenalty = !NBonus::hasOfType(attackerHero, Bonus::NO_OBSTACLES_PENALTY) && - hasWallPenalty(attacker, defender->position); - if (shooting && (distPenalty || obstaclePenalty || HLP::hasAdvancedAirShield(defender) )) + bool obstaclePenalty = hasWallPenalty(attacker, defender->position); + if (shooting) { - multBonus *= 0.5; + if (distPenalty || HLP::hasAdvancedAirShield(defender)) + { + multBonus *= 0.5; + } + if (obstaclePenalty) + { + multBonus *= 0.5; //cumulative + } } if (!shooting && attacker->hasBonusOfType(Bonus::SHOOTER) && !attacker->hasBonusOfType(Bonus::NO_MELEE_PENALTY)) { @@ -982,7 +988,7 @@ ui32 BattleInfo::getSpellCost(const CSpell * sp, const CGHeroInstance * caster) { if( stacks[g]->owner == caster->tempOwner && stacks[g]->hasBonusOfType(Bonus::CHANGES_SPELL_COST_FOR_ALLY) ) { - amin(manaReduction, stacks[g]->valOfBonuses(Bonus::CHANGES_SPELL_COST_FOR_ALLY)); + amax(manaReduction, stacks[g]->valOfBonuses(Bonus::CHANGES_SPELL_COST_FOR_ALLY)); } if( stacks[g]->owner != caster->tempOwner && stacks[g]->hasBonusOfType(Bonus::CHANGES_SPELL_COST_FOR_ENEMY) ) { @@ -990,7 +996,7 @@ ui32 BattleInfo::getSpellCost(const CSpell * sp, const CGHeroInstance * caster) } } - return ret + manaReduction + manaIncrease; + return ret - manaReduction + manaIncrease; } int BattleInfo::hexToWallPart(THex hex) const @@ -1000,7 +1006,8 @@ int BattleInfo::hexToWallPart(THex hex) const static const std::pair attackable[] = //potentially attackable parts of wall {std::make_pair(50, 0), std::make_pair(183, 1), std::make_pair(182, 2), std::make_pair(130, 3), - std::make_pair(62, 4), std::make_pair(29, 5), std::make_pair(12, 6), std::make_pair(95, 7), std::make_pair(96, 7)}; + std::make_pair(62, 4), std::make_pair(29, 5), std::make_pair(12, 6), std::make_pair(95, 7), std::make_pair(96, 7), + std::make_pair(45, -2), std::make_pair(78, -2), std::make_pair(112, -2), std::make_pair(147, -2)}; // -2 - indestructible walls for(int g = 0; g < ARRAY_COUNT(attackable); ++g) { @@ -1296,16 +1303,27 @@ si8 BattleInfo::sameSideOfWall(int pos1, int pos2) const si8 BattleInfo::hasWallPenalty( const CStack* stack, THex destHex ) const { - if (siege == 0) - { - return false; - } - if (stack->hasBonusOfType(Bonus::NO_WALL_PENALTY)) + if (!siege || stack->hasBonusOfType(Bonus::NO_WALL_PENALTY)) { return false; } - return !sameSideOfWall(stack->position, destHex); + int wallInStackLine = lineToWallHex(stack->position/BFIELD_WIDTH); + int wallInDestLine = lineToWallHex(destHex/BFIELD_WIDTH); + + bool stackLeft = stack->position < wallInStackLine; + bool destRight = destHex > wallInDestLine; + + if (stackLeft && destRight) //shooting from outside to inside + { + int row = (stack->position + destHex) / (2 * BFIELD_WIDTH); + if (stack->position > destHex && ((destHex & BFIELD_WIDTH - stack->position % BFIELD_WIDTH) < 2)) //shooting up high + row -= 2; + int wallPos = lineToWallHex(row); + if (hexToWallPart(wallPos) != -1) //wall still exists or is indestructible + return true; + } + return false; } si8 BattleInfo::canTeleportTo(const CStack * stack, THex destHex, int telportLevel) const diff --git a/lib/CArtHandler.cpp b/lib/CArtHandler.cpp index 7a63a0b43..838df4443 100644 --- a/lib/CArtHandler.cpp +++ b/lib/CArtHandler.cpp @@ -659,6 +659,7 @@ void CArtHandler::addBonuses() giveArtBonus(90,Bonus::WATER_WALKING, 0, 1);//Boots of Levitation giveArtBonus(91,Bonus::NO_DISTANCE_PENALTY,0, 0, 0, new HasAnotherBonusLimiter(Bonus::SHOOTER));//Golden Bow + giveArtBonus(91,Bonus::NO_WALL_PENALTY, 0, 0, 0, new HasAnotherBonusLimiter(Bonus::SHOOTER)); giveArtBonus(92,Bonus::SPELL_IMMUNITY,0,35);//Sphere of Permanence giveArtBonus(93,Bonus::NEGATE_ALL_NATURAL_IMMUNITIES,0);//Orb of Vulnerability @@ -742,7 +743,6 @@ void CArtHandler::addBonuses() giveArtBonus(134, Bonus::LEVEL_SPELL_IMMUNITY, 4, -1, Bonus::INDEPENDENT_MAX); //Titan's Thunder - // FIXME: should also add a permanent spell book, somehow. giveArtBonus(135, Bonus::SPELL, 3, 57); //Admiral's Hat @@ -750,7 +750,7 @@ void CArtHandler::addBonuses() //Bow of the Sharpshooter giveArtBonus(137, Bonus::NO_DISTANCE_PENALTY, 0, 0, 0, new HasAnotherBonusLimiter(Bonus::SHOOTER)); - giveArtBonus(137, Bonus::NO_OBSTACLES_PENALTY, 0, 0, 0, new HasAnotherBonusLimiter(Bonus::SHOOTER)); + giveArtBonus(137, Bonus::NO_WALL_PENALTY, 0, 0, 0, new HasAnotherBonusLimiter(Bonus::SHOOTER)); giveArtBonus(137, Bonus::FREE_SHOOTING, 0, 0, 0, new HasAnotherBonusLimiter(Bonus::SHOOTER)); //Wizard's Well diff --git a/lib/CCreatureHandler.cpp b/lib/CCreatureHandler.cpp index baf347167..89ba29f0a 100644 --- a/lib/CCreatureHandler.cpp +++ b/lib/CCreatureHandler.cpp @@ -890,7 +890,7 @@ void CCreatureHandler::loadStackExp(Bonus & b, BonusList & bl, std::string & src break; case 'o': enable = true; - b.type = Bonus::NO_OBSTACLES_PENALTY; + b.type = Bonus::NO_WALL_PENALTY; break; case 'a': diff --git a/lib/CCreatureSet.cpp b/lib/CCreatureSet.cpp index 48867be8d..6977f06d1 100644 --- a/lib/CCreatureSet.cpp +++ b/lib/CCreatureSet.cpp @@ -565,7 +565,7 @@ std::string CStackInstance::bonusToString(Bonus *bonus, bool description) const case Bonus::FREE_SHOOTING: case Bonus::NO_MELEE_PENALTY: case Bonus::NO_DISTANCE_PENALTY: - case Bonus::NO_OBSTACLES_PENALTY: + case Bonus::NO_WALL_PENALTY: case Bonus::JOUSTING: //TODO: percent bonus? case Bonus::RETURN_AFTER_STRIKE: case Bonus::BLOCKS_RETALIATION: @@ -743,7 +743,7 @@ std::string CStackInstance::bonusToGraphics(Bonus *bonus) const fileName = "E_MORAL.bmp"; break; case Bonus::RECEPTIVE: fileName = "E_NOFRIM.bmp"; break; - case Bonus::NO_OBSTACLES_PENALTY: + case Bonus::NO_WALL_PENALTY: fileName = "E_OBST.bmp"; break; case Bonus::ENEMY_DEFENCE_REDUCTION: fileName = "E_RDEF.bmp"; break; diff --git a/lib/HeroBonus.h b/lib/HeroBonus.h index 54bd26ed4..8d5d9dd39 100644 --- a/lib/HeroBonus.h +++ b/lib/HeroBonus.h @@ -133,7 +133,6 @@ namespace PrimarySkill BONUS_NAME(FEAR) \ BONUS_NAME(FEARLESS) \ BONUS_NAME(NO_DISTANCE_PENALTY) \ - BONUS_NAME(NO_OBSTACLES_PENALTY) \ BONUS_NAME(SELF_LUCK) /*halfling*/ \ BONUS_NAME(ENCHANTER)/* for Enchanter spells, val - skill level, subtype - spell id, additionalInfo - cooldown */ \ BONUS_NAME(HEALER) \ diff --git a/server/CGameHandler.cpp b/server/CGameHandler.cpp index 18aa1a70b..0918de3e8 100644 --- a/server/CGameHandler.cpp +++ b/server/CGameHandler.cpp @@ -3147,7 +3147,7 @@ bool CGameHandler::makeBattleAction( BattleAction &ba ) CHeroHandler::SBallisticsLevelInfo sbi = VLC->heroh->ballistics[attackingHero->getSecSkillLevel(CGHeroInstance::BALLISTICS)]; int attackedPart = gs->curB->hexToWallPart(ba.destinationTile); - if(attackedPart == -1) + if(attackedPart < 0) { complain("catapult tried to attack non-catapultable hex!"); break;