1
0
mirror of https://github.com/vcmi/vcmi.git synced 2025-07-15 01:24:45 +02:00

- Partially fixed mantis #1065 (Gate with hex 95 can't be attacked)

- Fixed 'catapult tried to attack non-catapultable hex!' problem, now catapult attacks attackable wall parts only
- Fixed problem that the server performed applying damage on a wall part twice
- Added methods for checking what wall parts are attackable and if a wall part is potentially attackable
- Added functionality to trace net packages
- Added functionality to trace std::vectors
- Added tracing for CatapultAttack(CPack)
- Updated various toString methods to use {} instead of []
- Refactoring
This commit is contained in:
beegee1
2013-12-08 17:54:13 +00:00
parent 5de70ba235
commit 69eee05ccc
13 changed files with 156 additions and 87 deletions

View File

@ -226,14 +226,23 @@ template<typename T, size_t N> char (&_ArrayCountObj(const T (&)[N]))[N];
/* VCMI standard library */ /* VCMI standard library */
/* ---------------------------------------------------------------------------- */ /* ---------------------------------------------------------------------------- */
template<typename T> template<typename T>
std::ostream &operator<<(std::ostream &out, const boost::optional<T> &opt) std::ostream & operator<<(std::ostream & out, const boost::optional<T> & opt)
{ {
if(opt) if(opt) return out << *opt;
return out << *opt; else return out << "empty";
else
return out<< "empty";
} }
template<typename T>
std::ostream & operator<<(std::ostream & out, const std::vector<T> & container)
{
out << "[";
for(auto it = container.begin(); it != container.end(); ++it)
{
out << *it;
if(std::prev(container.end()) != it) out << ", ";
}
return out << "]";
}
namespace vstd namespace vstd
{ {

View File

@ -1156,14 +1156,12 @@ bool CBattleInterface::isTileAttackable(const BattleHex & number) const
bool CBattleInterface::isCatapultAttackable(BattleHex hex) const bool CBattleInterface::isCatapultAttackable(BattleHex hex) const
{ {
if(!siegeH || tacticsMode) if(!siegeH || tacticsMode) return false;
return false;
int wallUnder = curInt->cb->battleHexToWallPart(hex); auto wallPart = curInt->cb->battleHexToWallPart(hex);
if(wallUnder < 0) //invalid or indestructible if(!curInt->cb->isWallPartPotentiallyAttackable(wallPart)) return false;
return false;
auto state = curInt->cb->battleGetWallState(wallUnder); auto state = curInt->cb->battleGetWallState(static_cast<int>(wallPart));
return state != EWallState::DESTROYED && state != EWallState::NONE; return state != EWallState::DESTROYED && state != EWallState::NONE;
} }

View File

@ -98,6 +98,6 @@ std::ostream & operator<<(std::ostream & os, const BattleAction & ba)
std::stringstream actionTypeStream; std::stringstream actionTypeStream;
actionTypeStream << ba.actionType; actionTypeStream << ba.actionType;
return os << boost::str(boost::format("[BattleAction: side '%d', stackNumber '%d', actionType '%s', destinationTile '%s', additionalInfo '%d', selectedStack '%d']") return os << boost::str(boost::format("{BattleAction: side '%d', stackNumber '%d', actionType '%s', destinationTile '%s', additionalInfo '%d', selectedStack '%d'}")
% static_cast<int>(ba.side) % ba.stackNumber % actionTypeStream.str() % ba.destinationTile % ba.additionalInfo % ba.selectedStack); % static_cast<int>(ba.side) % ba.stackNumber % actionTypeStream.str() % ba.destinationTile % ba.additionalInfo % ba.selectedStack);
} }

View File

@ -161,5 +161,5 @@ BattleHex BattleHex::getClosestTile(bool attackerOwned, BattleHex initialPos, st
std::ostream & operator<<(std::ostream & os, const BattleHex & hex) std::ostream & operator<<(std::ostream & os, const BattleHex & hex)
{ {
return os << boost::str(boost::format("[BattleHex: x '%d', y '%d', hex '%d']") % hex.getX() % hex.getY() % hex.hex); return os << boost::str(boost::format("{BattleHex: x '%d', y '%d', hex '%d'}") % hex.getX() % hex.getY() % hex.hex);
} }

View File

@ -386,13 +386,13 @@ BattleInfo * BattleInfo::setupBattle( int3 tile, ETerrainType terrain, BFieldTyp
if (!town->hasBuilt(BuildingID::CITADEL)) if (!town->hasBuilt(BuildingID::CITADEL))
{ {
curB->si.wallState[EWallParts::KEEP] = EWallState::NONE; curB->si.wallState[EWallPart::KEEP] = EWallState::NONE;
} }
if (!town->hasBuilt(BuildingID::CASTLE)) if (!town->hasBuilt(BuildingID::CASTLE))
{ {
curB->si.wallState[EWallParts::UPPER_TOWER] = EWallState::NONE; curB->si.wallState[EWallPart::UPPER_TOWER] = EWallState::NONE;
curB->si.wallState[EWallParts::BOTTOM_TOWER] = EWallState::NONE; curB->si.wallState[EWallPart::BOTTOM_TOWER] = EWallState::NONE;
} }
} }

View File

@ -33,7 +33,7 @@ struct BattleStackAttacked;
//only for use in BattleInfo //only for use in BattleInfo
struct DLL_LINKAGE SiegeInfo struct DLL_LINKAGE SiegeInfo
{ {
std::array<si8, EWallParts::PARTS_COUNT> wallState; std::array<si8, EWallPart::PARTS_COUNT> wallState;
// return EWallState decreased by value of damage points // return EWallState decreased by value of damage points
static EWallState::EWallState applyDamage(EWallState::EWallState state, unsigned int value) static EWallState::EWallState applyDamage(EWallState::EWallState state, unsigned int value)

View File

@ -54,38 +54,38 @@ namespace SiegeStuffThatShouldBeMovedToHandlers // <=== TODO
return stackLeft != destLeft; return stackLeft != destLeft;
} }
//potentially attackable parts of wall // parts of wall
static const std::pair<int, EWallParts::EWallParts> attackable[] = static const std::pair<int, EWallPart::EWallPart> wallParts[] =
{ {
std::make_pair(50, EWallParts::KEEP), std::make_pair(50, EWallPart::KEEP),
std::make_pair(183, EWallParts::BOTTOM_TOWER), std::make_pair(183, EWallPart::BOTTOM_TOWER),
std::make_pair(182, EWallParts::BOTTOM_WALL), std::make_pair(182, EWallPart::BOTTOM_WALL),
std::make_pair(130, EWallParts::BELOW_GATE), std::make_pair(130, EWallPart::BELOW_GATE),
std::make_pair(62, EWallParts::OVER_GATE), std::make_pair(62, EWallPart::OVER_GATE),
std::make_pair(29, EWallParts::UPPER_WALL), std::make_pair(29, EWallPart::UPPER_WALL),
std::make_pair(12, EWallParts::UPPER_TOWER), std::make_pair(12, EWallPart::UPPER_TOWER),
std::make_pair(95, EWallParts::GATE), std::make_pair(95, EWallPart::INDESTRUCTIBLE_PART_OF_GATE),
std::make_pair(96, EWallParts::GATE), std::make_pair(96, EWallPart::GATE),
std::make_pair(45, EWallParts::INDESTRUCTIBLE_PART), std::make_pair(45, EWallPart::INDESTRUCTIBLE_PART),
std::make_pair(78, EWallParts::INDESTRUCTIBLE_PART), std::make_pair(78, EWallPart::INDESTRUCTIBLE_PART),
std::make_pair(112, EWallParts::INDESTRUCTIBLE_PART), std::make_pair(112, EWallPart::INDESTRUCTIBLE_PART),
std::make_pair(147, EWallParts::INDESTRUCTIBLE_PART) std::make_pair(147, EWallPart::INDESTRUCTIBLE_PART)
}; };
static EWallParts::EWallParts hexToWallPart(BattleHex hex) static EWallPart::EWallPart hexToWallPart(BattleHex hex)
{ {
for(auto & elem : attackable) for(auto & elem : wallParts)
{ {
if(elem.first == hex) if(elem.first == hex)
return elem.second; return elem.second;
} }
return EWallParts::INVALID; //not found! return EWallPart::INVALID; //not found!
} }
static BattleHex WallPartToHex(EWallParts::EWallParts part) static BattleHex WallPartToHex(EWallPart::EWallPart part)
{ {
for(auto & elem : attackable) for(auto & elem : wallParts)
{ {
if(elem.second == part) if(elem.second == part)
return elem.first; return elem.first;
@ -425,7 +425,7 @@ si8 CBattleInfoEssentials::battleGetWallState(int partOfWall) const
if(getBattle()->siege == CGTownInstance::NONE) if(getBattle()->siege == CGTownInstance::NONE)
return EWallState::NONE; return EWallState::NONE;
assert(partOfWall >= 0 && partOfWall < EWallParts::PARTS_COUNT); assert(partOfWall >= 0 && partOfWall < EWallPart::PARTS_COUNT);
return getBattle()->si.wallState[partOfWall]; return getBattle()->si.wallState[partOfWall];
} }
@ -452,8 +452,7 @@ si8 CBattleInfoCallback::battleHasWallPenalty(const IBonusBearer *bonusBearer, B
if (shooterPosition > destHex && ((destHex % GameConstants::BFIELD_WIDTH - shooterPosition % GameConstants::BFIELD_WIDTH) < 2)) //shooting up high if (shooterPosition > destHex && ((destHex % GameConstants::BFIELD_WIDTH - shooterPosition % GameConstants::BFIELD_WIDTH) < 2)) //shooting up high
row -= 2; row -= 2;
const int wallPos = lineToWallHex(row); const int wallPos = lineToWallHex(row);
if (battleHexToWallPart(wallPos) < 0) //wall still exists or is indestructible if (!isWallPartPotentiallyAttackable(battleHexToWallPart(wallPos))) return true;
return true;
} }
return false; return false;
@ -1094,7 +1093,7 @@ AccessibilityInfo CBattleInfoCallback::getAccesibility() const
} }
//gate -> should be before stacks //gate -> should be before stacks
if(battleGetSiegeLevel() > 0 && battleGetWallState(EWallParts::GATE) != EWallState::DESTROYED) if(battleGetSiegeLevel() > 0 && battleGetWallState(EWallPart::GATE) != EWallState::DESTROYED)
{ {
ret[95] = ret[96] = EAccessibility::GATE; //block gate's hexes ret[95] = ret[96] = EAccessibility::GATE; //block gate's hexes
} }
@ -1514,18 +1513,45 @@ si8 CBattleInfoCallback::battleHasDistancePenalty(const IBonusBearer *bonusBeare
return true; return true;
} }
BattleHex CBattleInfoCallback::wallPartToBattleHex(EWallParts::EWallParts part) const BattleHex CBattleInfoCallback::wallPartToBattleHex(EWallPart::EWallPart part) const
{ {
RETURN_IF_NOT_BATTLE(BattleHex::INVALID); RETURN_IF_NOT_BATTLE(BattleHex::INVALID);
return WallPartToHex(part); return WallPartToHex(part);
} }
EWallParts::EWallParts CBattleInfoCallback::battleHexToWallPart(BattleHex hex) const EWallPart::EWallPart CBattleInfoCallback::battleHexToWallPart(BattleHex hex) const
{ {
RETURN_IF_NOT_BATTLE(EWallParts::INVALID); RETURN_IF_NOT_BATTLE(EWallPart::INVALID);
return hexToWallPart(hex); return hexToWallPart(hex);
} }
bool CBattleInfoCallback::isWallPartPotentiallyAttackable(EWallPart::EWallPart wallPart) const
{
RETURN_IF_NOT_BATTLE(false);
return wallPart != EWallPart::INDESTRUCTIBLE_PART && wallPart != EWallPart::INDESTRUCTIBLE_PART_OF_GATE &&
wallPart != EWallPart::INVALID;
}
std::vector<BattleHex> CBattleInfoCallback::getAttackableBattleHexes() const
{
std::vector<BattleHex> attackableBattleHexes;
RETURN_IF_NOT_BATTLE(attackableBattleHexes);
for(auto & wallPartPair : wallParts)
{
if(isWallPartPotentiallyAttackable(wallPartPair.second))
{
auto wallState = static_cast<EWallState::EWallState>(battleGetWallState(static_cast<int>(wallPartPair.second)));
if(wallState == EWallState::INTACT || wallState == EWallState::DAMAGED)
{
attackableBattleHexes.push_back(BattleHex(wallPartPair.first));
}
}
}
return attackableBattleHexes;
}
ESpellCastProblem::ESpellCastProblem CBattleInfoCallback::battleIsImmune(const CGHeroInstance * caster, const CSpell * spell, ECastingMode::ECastingMode mode, BattleHex dest) const ESpellCastProblem::ESpellCastProblem CBattleInfoCallback::battleIsImmune(const CGHeroInstance * caster, const CSpell * spell, ECastingMode::ECastingMode mode, BattleHex dest) const
{ {
RETURN_IF_NOT_BATTLE(ESpellCastProblem::INVALID); RETURN_IF_NOT_BATTLE(ESpellCastProblem::INVALID);

View File

@ -170,7 +170,6 @@ public:
std::vector<shared_ptr<const CObstacleInstance> > battleGetAllObstacles(boost::optional<BattlePerspective::BattlePerspective> perspective = boost::none) const; //returns all obstacles on the battlefield std::vector<shared_ptr<const CObstacleInstance> > battleGetAllObstacles(boost::optional<BattlePerspective::BattlePerspective> perspective = boost::none) const; //returns all obstacles on the battlefield
TStacks battleGetAllStacks(bool includeTurrets = false) const; //returns all stacks, alive or dead or undead or mechanical :) TStacks battleGetAllStacks(bool includeTurrets = false) const; //returns all stacks, alive or dead or undead or mechanical :)
bool battleHasNativeStack(ui8 side) const; bool battleHasNativeStack(ui8 side) const;
si8 battleGetWallState(int partOfWall) const; //for determining state of a part of the wall; format: parameter [0] - keep, [1] - bottom tower, [2] - bottom wall, [3] - below gate, [4] - over gate, [5] - upper wall, [6] - uppert tower, [7] - gate; returned value: 1 - intact, 2 - damaged, 3 - destroyed; 0 - no battle
int battleGetMoatDmg() const; //what dmg unit will suffer if ending turn in the moat int battleGetMoatDmg() const; //what dmg unit will suffer if ending turn in the moat
const CGTownInstance * battleGetDefendedTown() const; //returns defended town if current battle is a siege, nullptr instead const CGTownInstance * battleGetDefendedTown() const; //returns defended town if current battle is a siege, nullptr instead
const CStack *battleActiveStack() const; const CStack *battleActiveStack() const;
@ -186,6 +185,10 @@ public:
const CArmedInstance * battleGetArmyObject(ui8 side) const; const CArmedInstance * battleGetArmyObject(ui8 side) const;
InfoAboutHero battleGetHeroInfo(ui8 side) const; InfoAboutHero battleGetHeroInfo(ui8 side) const;
// for determining state of a part of the wall; format: parameter [0] - keep, [1] - bottom tower, [2] - bottom wall,
// [3] - below gate, [4] - over gate, [5] - upper wall, [6] - uppert tower, [7] - gate; returned value: 1 - intact, 2 - damaged, 3 - destroyed; 0 - no battle
si8 battleGetWallState(int partOfWall) const;
//helpers //helpers
TStacks battleAliveStacks() const; TStacks battleAliveStacks() const;
TStacks battleAliveStacks(ui8 side) const; TStacks battleAliveStacks(ui8 side) const;
@ -251,8 +254,10 @@ public:
si8 battleHasWallPenalty(const CStack * stack, BattleHex destHex) const; //checks if given stack has wall penalty si8 battleHasWallPenalty(const CStack * stack, BattleHex destHex) const; //checks if given stack has wall penalty
si8 battleHasWallPenalty(const IBonusBearer *bonusBearer, BattleHex shooterPosition, BattleHex destHex) const; //checks if given stack has wall penalty si8 battleHasWallPenalty(const IBonusBearer *bonusBearer, BattleHex shooterPosition, BattleHex destHex) const; //checks if given stack has wall penalty
BattleHex wallPartToBattleHex(EWallParts::EWallParts part) const; BattleHex wallPartToBattleHex(EWallPart::EWallPart part) const;
EWallParts::EWallParts battleHexToWallPart(BattleHex hex) const; //returns part of destructible wall / gate / keep under given hex or -1 if not found EWallPart::EWallPart battleHexToWallPart(BattleHex hex) const; //returns part of destructible wall / gate / keep under given hex or -1 if not found
bool isWallPartPotentiallyAttackable(EWallPart::EWallPart wallPart) const; // returns true if the wall part is potentially attackable (independent of wall state), false if not
std::vector<BattleHex> getAttackableBattleHexes() const;
//*** MAGIC //*** MAGIC
si8 battleMaxSpellLevel() const; //calculates minimum spell level possible to be cast on battlefield - takes into account artifacts of both heroes; if no effects are set, 0 is returned si8 battleMaxSpellLevel() 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

View File

@ -224,7 +224,7 @@ CPack * CConnection::retreivePack()
boost::unique_lock<boost::mutex> lock(*rmx); boost::unique_lock<boost::mutex> lock(*rmx);
logNetwork->traceStream() << "Listening... "; logNetwork->traceStream() << "Listening... ";
*this >> ret; *this >> ret;
logNetwork->traceStream() << "\treceived server message of type " << typeid(*ret).name(); logNetwork->traceStream() << "\treceived server message of type " << typeid(*ret).name() << ", data: " << ret;
return ret; return ret;
} }

View File

@ -439,13 +439,13 @@ namespace ECommander
const int MAX_SKILL_LEVEL = 5; const int MAX_SKILL_LEVEL = 5;
} }
namespace EWallParts namespace EWallPart
{ {
enum EWallParts enum EWallPart
{ {
INDESTRUCTIBLE_PART = -2, INVALID = -1, INDESTRUCTIBLE_PART_OF_GATE = -3, INDESTRUCTIBLE_PART = -2, INVALID = -1,
KEEP = 0, BOTTOM_TOWER, BOTTOM_WALL, BELOW_GATE, OVER_GATE, UPPER_WALL, UPPER_TOWER, GATE, KEEP = 0, BOTTOM_TOWER, BOTTOM_WALL, BELOW_GATE, OVER_GATE, UPPER_WALL, UPPER_TOWER, GATE,
PARTS_COUNT PARTS_COUNT /* This constant SHOULD always stay as the last item in the enum. */
}; };
} }

View File

@ -48,10 +48,12 @@ struct CPack
{ {
logNetwork->errorStream() << "CPack serialized... this should not happen!"; logNetwork->errorStream() << "CPack serialized... this should not happen!";
} }
void applyGs(CGameState *gs) void applyGs(CGameState *gs) { }
{}; virtual std::string toString() const { return boost::str(boost::format("{CPack: type '%d'}") % type); }
}; };
std::ostream & operator<<(std::ostream & out, const CPack * pack);
struct CPackForClient : public CPack struct CPackForClient : public CPack
{ {
CPackForClient(){type = 1;}; CPackForClient(){type = 1;};
@ -1678,6 +1680,8 @@ struct CatapultAttack : public CPackForClient //3015
ui8 attackedPart; ui8 attackedPart;
ui8 damageDealt; ui8 damageDealt;
DLL_LINKAGE std::string toString() const;
template <typename Handler> void serialize(Handler &h, const int version) template <typename Handler> void serialize(Handler &h, const int version)
{ {
h & destinationTile & attackedPart & damageDealt; h & destinationTile & attackedPart & damageDealt;
@ -1688,9 +1692,9 @@ struct CatapultAttack : public CPackForClient //3015
DLL_LINKAGE void applyGs(CGameState *gs); DLL_LINKAGE void applyGs(CGameState *gs);
void applyCl(CClient *cl); void applyCl(CClient *cl);
DLL_LINKAGE std::string toString() const override;
std::vector< AttackInfo > attackedParts; std::vector< AttackInfo > attackedParts;
int attacker; //if -1, then a spell caused this int attacker; //if -1, then a spell caused this
template <typename Handler> void serialize(Handler &h, const int version) template <typename Handler> void serialize(Handler &h, const int version)
@ -1699,6 +1703,8 @@ struct CatapultAttack : public CPackForClient //3015
} }
}; };
DLL_LINKAGE std::ostream & operator<<(std::ostream & out, const CatapultAttack::AttackInfo & attackInfo);
struct BattleStacksRemoved : public CPackForClient //3016 struct BattleStacksRemoved : public CPackForClient //3016
{ {
BattleStacksRemoved(){type = 3016;} BattleStacksRemoved(){type = 3016;}

View File

@ -35,6 +35,11 @@
#undef max #undef max
#endif #endif
std::ostream & operator<<(std::ostream & out, const CPack * pack)
{
return out << pack->toString();
}
DLL_LINKAGE void SetResource::applyGs( CGameState *gs ) DLL_LINKAGE void SetResource::applyGs( CGameState *gs )
{ {
assert(player < PlayerColor::PLAYER_LIMIT); assert(player < PlayerColor::PLAYER_LIMIT);
@ -1508,6 +1513,22 @@ DLL_LINKAGE void CatapultAttack::applyGs( CGameState *gs )
} }
} }
DLL_LINKAGE std::string CatapultAttack::AttackInfo::toString() const
{
return boost::str(boost::format("{AttackInfo: destinationTile '%d', attackedPart '%d', damageDealt '%d'}")
% destinationTile % static_cast<int>(attackedPart) % static_cast<int>(damageDealt));
}
DLL_LINKAGE std::ostream & operator<<(std::ostream & out, const CatapultAttack::AttackInfo & attackInfo)
{
return out << attackInfo.toString();
}
DLL_LINKAGE std::string CatapultAttack::toString() const
{
return boost::str(boost::format("{CatapultAttack: attackedParts '%s', attacker '%d'}") % attackedParts % attacker);
}
DLL_LINKAGE void BattleStacksRemoved::applyGs( CGameState *gs ) DLL_LINKAGE void BattleStacksRemoved::applyGs( CGameState *gs )
{ {
if(!gs->curB) if(!gs->curB)

View File

@ -3476,21 +3476,21 @@ bool CGameHandler::makeBattleAction( BattleAction &ba )
} }
case Battle::CATAPULT: case Battle::CATAPULT:
{ {
auto getCatapultHitChance = [&](EWallParts::EWallParts part, const CHeroHandler::SBallisticsLevelInfo & sbi) -> int auto getCatapultHitChance = [&](EWallPart::EWallPart part, const CHeroHandler::SBallisticsLevelInfo & sbi) -> int
{ {
switch(part) switch(part)
{ {
case EWallParts::GATE: case EWallPart::GATE:
return sbi.gate; return sbi.gate;
case EWallParts::KEEP: case EWallPart::KEEP:
return sbi.keep; return sbi.keep;
case EWallParts::BOTTOM_TOWER: case EWallPart::BOTTOM_TOWER:
case EWallParts::UPPER_TOWER: case EWallPart::UPPER_TOWER:
return sbi.tower; return sbi.tower;
case EWallParts::BOTTOM_WALL: case EWallPart::BOTTOM_WALL:
case EWallParts::BELOW_GATE: case EWallPart::BELOW_GATE:
case EWallParts::OVER_GATE: case EWallPart::OVER_GATE:
case EWallParts::UPPER_WALL: case EWallPart::UPPER_WALL:
return sbi.wall; return sbi.wall;
default: default:
return 0; return 0;
@ -3504,8 +3504,8 @@ bool CGameHandler::makeBattleAction( BattleAction &ba )
const CGHeroInstance * attackingHero = gs->curB->battleGetFightingHero(ba.side); const CGHeroInstance * attackingHero = gs->curB->battleGetFightingHero(ba.side);
CHeroHandler::SBallisticsLevelInfo sbi = VLC->heroh->ballistics.at(attackingHero->getSecSkillLevel(SecondarySkill::BALLISTICS)); CHeroHandler::SBallisticsLevelInfo sbi = VLC->heroh->ballistics.at(attackingHero->getSecSkillLevel(SecondarySkill::BALLISTICS));
EWallParts::EWallParts desiredTarget = gs->curB->battleHexToWallPart(ba.destinationTile); auto wallPart = gs->curB->battleHexToWallPart(ba.destinationTile);
if(desiredTarget < 0) if(!gs->curB->isWallPartPotentiallyAttackable(wallPart))
{ {
complain("catapult tried to attack non-catapultable hex!"); complain("catapult tried to attack non-catapultable hex!");
break; break;
@ -3514,7 +3514,7 @@ bool CGameHandler::makeBattleAction( BattleAction &ba )
//in successive iterations damage is dealt but not yet subtracted from wall's HPs //in successive iterations damage is dealt but not yet subtracted from wall's HPs
auto &currentHP = gs->curB->si.wallState; auto &currentHP = gs->curB->si.wallState;
if (currentHP.at(desiredTarget) == EWallState::DESTROYED || currentHP.at(desiredTarget) == EWallState::NONE) if (currentHP.at(wallPart) == EWallState::DESTROYED || currentHP.at(wallPart) == EWallState::NONE)
{ {
complain("catapult tried to attack already destroyed wall part!"); complain("catapult tried to attack already destroyed wall part!");
break; break;
@ -3523,7 +3523,7 @@ bool CGameHandler::makeBattleAction( BattleAction &ba )
for(int g=0; g<sbi.shots; ++g) for(int g=0; g<sbi.shots; ++g)
{ {
bool hitSuccessfull = false; bool hitSuccessfull = false;
EWallParts::EWallParts attackedPart = desiredTarget; auto attackedPart = wallPart;
do // catapult has chance to attack desired target. Othervice - attacks randomly do // catapult has chance to attack desired target. Othervice - attacks randomly
{ {
@ -3535,12 +3535,12 @@ bool CGameHandler::makeBattleAction( BattleAction &ba )
} }
else // select new target else // select new target
{ {
std::vector<EWallParts::EWallParts> allowedTargets; std::vector<EWallPart::EWallPart> allowedTargets;
for (size_t i=0; i< currentHP.size(); i++) for (size_t i=0; i< currentHP.size(); i++)
{ {
if (currentHP.at(i) != EWallState::DESTROYED && if (currentHP.at(i) != EWallState::DESTROYED &&
currentHP.at(i) != EWallState::NONE) currentHP.at(i) != EWallState::NONE)
allowedTargets.push_back(EWallParts::EWallParts(i)); allowedTargets.push_back(EWallPart::EWallPart(i));
} }
if (allowedTargets.empty()) if (allowedTargets.empty())
break; break;
@ -3569,32 +3569,30 @@ bool CGameHandler::makeBattleAction( BattleAction &ba )
{ {
if(dmgRand <= dmgChance[damage]) if(dmgRand <= dmgChance[damage])
{ {
currentHP[attackedPart] = SiegeInfo::applyDamage(EWallState::EWallState(currentHP.at(attackedPart)), damage);
attack.damageDealt = damage; attack.damageDealt = damage;
break; break;
} }
} }
// attacked tile may have changed - update destination // attacked tile may have changed - update destination
attack.destinationTile = gs->curB->wallPartToBattleHex(EWallParts::EWallParts(attack.attackedPart)); attack.destinationTile = gs->curB->wallPartToBattleHex(EWallPart::EWallPart(attack.attackedPart));
logGlobal->traceStream() << "Catapult attacks " << (int)attack.attackedPart logGlobal->traceStream() << "Catapult attacks " << (int)attack.attackedPart
<< " dealing " << (int)attack.damageDealt << " damage"; << " dealing " << (int)attack.damageDealt << " damage";
//removing creatures in turrets / keep if one is destroyed //removing creatures in turrets / keep if one is destroyed
if(attack.damageDealt > 0 && (attackedPart == EWallParts::KEEP || if(attack.damageDealt > 0 && (attackedPart == EWallPart::KEEP ||
attackedPart == EWallParts::BOTTOM_TOWER || attackedPart == EWallParts::UPPER_TOWER)) attackedPart == EWallPart::BOTTOM_TOWER || attackedPart == EWallPart::UPPER_TOWER))
{ {
int posRemove = -1; int posRemove = -1;
switch(attackedPart) switch(attackedPart)
{ {
case EWallParts::KEEP: case EWallPart::KEEP:
posRemove = -2; posRemove = -2;
break; break;
case EWallParts::BOTTOM_TOWER: case EWallPart::BOTTOM_TOWER:
posRemove = -3; posRemove = -3;
break; break;
case EWallParts::UPPER_TOWER: case EWallPart::UPPER_TOWER:
posRemove = -4; posRemove = -4;
break; break;
} }
@ -5975,20 +5973,26 @@ void CGameHandler::runBattle()
if(next->getCreature()->idNumber == CreatureID::CATAPULT && (!curOwner || curOwner->getSecSkillLevel(SecondarySkill::BALLISTICS) == 0)) //catapult, hero has no ballistics if(next->getCreature()->idNumber == CreatureID::CATAPULT && (!curOwner || curOwner->getSecSkillLevel(SecondarySkill::BALLISTICS) == 0)) //catapult, hero has no ballistics
{ {
BattleAction attack; const auto & attackableBattleHexes = curB.getAttackableBattleHexes();
static const int wallHexes[] = {50, 183, 182, 130, 62, 29, 12, 95};
attack.destinationTile = wallHexes[ rand()%ARRAY_COUNT(wallHexes) ]; if(!attackableBattleHexes.empty())
{
BattleAction attack;
attack.destinationTile = attackableBattleHexes[rand() % attackableBattleHexes.size()];
attack.actionType = Battle::CATAPULT; attack.actionType = Battle::CATAPULT;
attack.additionalInfo = 0; attack.additionalInfo = 0;
attack.side = !next->attackerOwned; attack.side = !next->attackerOwned;
attack.stackNumber = next->ID; attack.stackNumber = next->ID;
makeAutomaticAction(next, attack); makeAutomaticAction(next, attack);
}
else
{
makeStackDoNothing(next);
}
continue; continue;
} }
if(next->getCreature()->idNumber == CreatureID::FIRST_AID_TENT) if(next->getCreature()->idNumber == CreatureID::FIRST_AID_TENT)
{ {
std::vector< const CStack * > possibleStacks; std::vector< const CStack * > possibleStacks;