mirror of
https://github.com/vcmi/vcmi.git
synced 2024-12-24 22:14:36 +02:00
Fix handling damage when stack wait in moat.
Currently stack don't take any damage from moat when he stay in same place.
This commit is contained in:
parent
ce09da783a
commit
43d324b561
@ -843,6 +843,24 @@ std::vector<std::shared_ptr<const CObstacleInstance>> CBattleInfoCallback::battl
|
||||
return obstacles;
|
||||
}
|
||||
|
||||
std::vector<std::shared_ptr<const CObstacleInstance>> CBattleInfoCallback::getAllAffectedObstaclesByStack(const CStack * stack) const
|
||||
{
|
||||
std::vector<std::shared_ptr<const CObstacleInstance>> affectedObstacles = std::vector<std::shared_ptr<const CObstacleInstance>>();
|
||||
RETURN_IF_NOT_BATTLE(affectedObstacles);
|
||||
if(stack->alive())
|
||||
{
|
||||
affectedObstacles = battleGetAllObstaclesOnPos(stack->position, false);
|
||||
if(stack->doubleWide())
|
||||
{
|
||||
BattleHex otherHex = stack->occupiedHex(stack->position);
|
||||
if(otherHex.isValid())
|
||||
for(auto & i : battleGetAllObstaclesOnPos(otherHex, false))
|
||||
affectedObstacles.push_back(i);
|
||||
}
|
||||
}
|
||||
return affectedObstacles;
|
||||
}
|
||||
|
||||
AccessibilityInfo CBattleInfoCallback::getAccesibility() const
|
||||
{
|
||||
AccessibilityInfo ret;
|
||||
|
@ -41,6 +41,7 @@ public:
|
||||
boost::optional<int> battleIsFinished() const; //return none if battle is ongoing; otherwise the victorious side (0/1) or 2 if it is a draw
|
||||
|
||||
std::vector<std::shared_ptr<const CObstacleInstance>> battleGetAllObstaclesOnPos(BattleHex tile, bool onlyBlocking = true) const; //blocking obstacles makes tile inaccessible, others cause special effects (like Land Mines, Moat, Quicksands)
|
||||
std::vector<std::shared_ptr<const CObstacleInstance>> getAllAffectedObstaclesByStack(const CStack * stack) const;
|
||||
|
||||
const CStack * battleGetStackByPos(BattleHex pos, bool onlyAlive = true) const; //returns stack info by given pos
|
||||
void battleGetStackQueue(std::vector<const CStack *> &out, const int howMany, const int turn = 0, int lastMoved = -1) const;
|
||||
|
@ -1232,7 +1232,6 @@ int CGameHandler::moveStack(int stack, BattleHex dest)
|
||||
}
|
||||
else //for non-flying creatures
|
||||
{
|
||||
std::vector<std::shared_ptr<const CObstacleInstance>> obstacle, obstacle2; //obstacle that interrupted movement
|
||||
std::vector<BattleHex> tiles;
|
||||
const int tilesToMove = std::max((int)(path.first.size() - creSpeed), 0);
|
||||
int v = path.first.size()-1;
|
||||
@ -1331,16 +1330,14 @@ int CGameHandler::moveStack(int stack, BattleHex dest)
|
||||
}
|
||||
|
||||
//if we walked onto something, finalize this portion of stack movement check into obstacle
|
||||
obstacle = battleGetAllObstaclesOnPos(hex, false);
|
||||
if(!obstacle.empty())
|
||||
if(!battleGetAllObstaclesOnPos(hex, false).empty())
|
||||
obstacleHit = true;
|
||||
|
||||
if (curStack->doubleWide())
|
||||
{
|
||||
BattleHex otherHex = curStack->occupiedHex(hex);
|
||||
|
||||
//two hex creature hit obstacle by backside
|
||||
obstacle2 = battleGetAllObstaclesOnPos(otherHex, false);
|
||||
auto obstacle2 = battleGetAllObstaclesOnPos(otherHex, false);
|
||||
if(otherHex.isValid() && !obstacle2.empty())
|
||||
obstacleHit = true;
|
||||
}
|
||||
@ -1362,26 +1359,8 @@ int CGameHandler::moveStack(int stack, BattleHex dest)
|
||||
//we don't handle obstacle at the destination tile -> it's handled separately in the if at the end
|
||||
if (curStack->position != dest)
|
||||
{
|
||||
auto processObstacle = [&](std::vector<std::shared_ptr<const CObstacleInstance>> & obs)
|
||||
{
|
||||
if(!obs.empty())
|
||||
{
|
||||
for(auto & i : obs)
|
||||
{
|
||||
//if stack die in explosion or interrupted by obstacle, abort movement
|
||||
if(i->stopsMovement() || !curStack->alive())
|
||||
stackIsMoving = false;
|
||||
else if(stackIsMoving)
|
||||
handleDamageFromObstacle(*i, curStack);
|
||||
i.reset();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
processObstacle(obstacle);
|
||||
if (curStack->alive())
|
||||
processObstacle(obstacle2);
|
||||
|
||||
if(stackIsMoving && start != curStack->position)
|
||||
stackIsMoving = handleDamageFromObstacle(curStack, stackIsMoving);
|
||||
if (gateStateChanging)
|
||||
{
|
||||
if (curStack->position == openGateAtHex)
|
||||
@ -1409,26 +1388,8 @@ int CGameHandler::moveStack(int stack, BattleHex dest)
|
||||
}
|
||||
|
||||
//handling obstacle on the final field (separate, because it affects both flying and walking stacks)
|
||||
if(curStack->alive())
|
||||
{
|
||||
auto theLastObstacle = battleGetAllObstaclesOnPos(curStack->position, false);
|
||||
if(curStack->doubleWide())
|
||||
{
|
||||
BattleHex otherHex = curStack->occupiedHex(curStack->position);
|
||||
if(otherHex.isValid())
|
||||
for(auto & i : battleGetAllObstaclesOnPos(otherHex, false))
|
||||
theLastObstacle.push_back(i);
|
||||
}
|
||||
bool containDamageFromMoat = false;
|
||||
for(auto & i : theLastObstacle)
|
||||
{
|
||||
if(curStack->alive())
|
||||
if(i->obstacleType != CObstacleInstance::MOAT || !containDamageFromMoat)
|
||||
handleDamageFromObstacle(*i, curStack);
|
||||
if(i->obstacleType == CObstacleInstance::MOAT)
|
||||
containDamageFromMoat = true;
|
||||
}
|
||||
}
|
||||
handleDamageFromObstacle(curStack);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -4032,10 +3993,10 @@ bool CGameHandler::makeBattleAction(BattleAction &ba)
|
||||
|
||||
const CGHeroInstance * attackingHero = gs->curB->battleGetFightingHero(ba.side);
|
||||
|
||||
if (destinationStack->alive()
|
||||
&& (stack->getCreature()->idNumber == CreatureID::BALLISTA)
|
||||
&& (attackingHero->getSecSkillLevel(SecondarySkill::ARTILLERY) >= SecSkillLevel::ADVANCED)
|
||||
)
|
||||
if(destinationStack->alive()
|
||||
&& (stack->getCreature()->idNumber == CreatureID::BALLISTA)
|
||||
&& (attackingHero->getSecSkillLevel(SecondarySkill::ARTILLERY) >= SecSkillLevel::ADVANCED)
|
||||
)
|
||||
{
|
||||
BattleAttack bat2;
|
||||
bat2.flags |= BattleAttack::SHOT;
|
||||
@ -4133,8 +4094,8 @@ bool CGameHandler::makeBattleAction(BattleAction &ba)
|
||||
std::vector<EWallPart::EWallPart> allowedTargets;
|
||||
for (size_t i=0; i< currentHP.size(); i++)
|
||||
{
|
||||
if (currentHP.at(i) != EWallState::DESTROYED &&
|
||||
currentHP.at(i) != EWallState::NONE)
|
||||
if(currentHP.at(i) != EWallState::DESTROYED &&
|
||||
currentHP.at(i) != EWallState::NONE)
|
||||
allowedTargets.push_back(EWallPart::EWallPart(i));
|
||||
}
|
||||
if (allowedTargets.empty())
|
||||
@ -4328,7 +4289,10 @@ bool CGameHandler::makeBattleAction(BattleAction &ba)
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (ba.stackNumber == gs->curB->activeStack || battleResult.get()) //active stack has moved or battle has finished
|
||||
if(ba.actionType == Battle::DAEMON_SUMMONING || ba.actionType == Battle::WAIT || ba.actionType == Battle::DEFEND
|
||||
|| ba.actionType == Battle::SHOOT || ba.actionType == Battle::MONSTER_SPELL)
|
||||
handleDamageFromObstacle(stack);
|
||||
if(ba.stackNumber == gs->curB->activeStack || battleResult.get()) //active stack has moved or battle has finished
|
||||
battleMadeAction.setn(true);
|
||||
return ok;
|
||||
}
|
||||
@ -4620,77 +4584,88 @@ void CGameHandler::stackTurnTrigger(const CStack *st)
|
||||
}
|
||||
}
|
||||
|
||||
void CGameHandler::handleDamageFromObstacle(const CObstacleInstance &obstacle, const CStack * curStack)
|
||||
bool CGameHandler::handleDamageFromObstacle(const CStack * curStack, bool stackIsMoving)
|
||||
{
|
||||
//we want to determine following vars depending on obstacle type
|
||||
int damage = -1;
|
||||
int effect = -1;
|
||||
bool oneTimeObstacle = false;
|
||||
|
||||
//helper info
|
||||
const SpellCreatedObstacle *spellObstacle = dynamic_cast<const SpellCreatedObstacle*>(&obstacle); //not nice but we may need spell params
|
||||
|
||||
const ui8 side = curStack->side; //if enemy is defending (false = 0), side of enemy hero is 1 (true)
|
||||
const CGHeroInstance *hero = gs->curB->battleGetFightingHero(side);//FIXME: there may be no hero - landmines in Tower
|
||||
|
||||
if (obstacle.obstacleType == CObstacleInstance::MOAT)
|
||||
if(!curStack->alive())
|
||||
return false;
|
||||
bool containDamageFromMoat = false;
|
||||
for(auto & obstacle : getAllAffectedObstaclesByStack(curStack))
|
||||
{
|
||||
damage = battleGetMoatDmg();
|
||||
if(!curStack->alive() || obstacle->stopsMovement() && stackIsMoving == true)
|
||||
return false;
|
||||
//we want to determine following vars depending on obstacle type
|
||||
int damage = -1;
|
||||
int effect = -1;
|
||||
bool oneTimeObstacle = false;
|
||||
|
||||
//helper info
|
||||
const SpellCreatedObstacle * spellObstacle = dynamic_cast<const SpellCreatedObstacle *>(obstacle.get()); //not nice but we may need spell params
|
||||
|
||||
const ui8 side = curStack->side; //if enemy is defending (false = 0), side of enemy hero is 1 (true)
|
||||
const CGHeroInstance * hero = gs->curB->battleGetFightingHero(side);//FIXME: there may be no hero - landmines in Tower
|
||||
|
||||
if(obstacle->obstacleType == CObstacleInstance::MOAT)
|
||||
{
|
||||
damage = battleGetMoatDmg();
|
||||
if(!containDamageFromMoat)
|
||||
containDamageFromMoat = true;
|
||||
else
|
||||
continue;
|
||||
}
|
||||
else if(obstacle->obstacleType == CObstacleInstance::LAND_MINE)
|
||||
{
|
||||
if(!spellObstacle)
|
||||
COMPLAIN_RET("Invalid obstacle instance");
|
||||
//You don't get hit by a Mine you can see.
|
||||
if(gs->curB->battleIsObstacleVisibleForSide(*obstacle, (BattlePerspective::BattlePerspective)side))
|
||||
continue;
|
||||
oneTimeObstacle = true;
|
||||
effect = 82;
|
||||
const CSpell * sp = SpellID(SpellID::LAND_MINE).toSpell();
|
||||
|
||||
if(sp->isImmuneByStack(hero, curStack))
|
||||
continue;
|
||||
|
||||
damage = sp->calculateDamage(hero, curStack, spellObstacle->spellLevel, spellObstacle->casterSpellPower);
|
||||
//TODO even if obstacle wasn't created by hero (Tower "moat") it should deal dmg as if cast by hero,
|
||||
//if it is bigger than default dmg. Or is it just irrelevant H3 implementation quirk
|
||||
}
|
||||
else if(obstacle->obstacleType == CObstacleInstance::FIRE_WALL)
|
||||
{
|
||||
if(!spellObstacle)
|
||||
COMPLAIN_RET("Invalid obstacle instance");
|
||||
const CSpell * sp = SpellID(SpellID::FIRE_WALL).toSpell();
|
||||
|
||||
if(sp->isImmuneByStack(hero, curStack))
|
||||
continue;
|
||||
|
||||
damage = sp->calculateDamage(hero, curStack,
|
||||
spellObstacle->spellLevel, spellObstacle->casterSpellPower);
|
||||
}
|
||||
else
|
||||
continue;
|
||||
|
||||
BattleStackAttacked bsa;
|
||||
if(effect >= 0)
|
||||
{
|
||||
bsa.flags |= BattleStackAttacked::EFFECT;
|
||||
bsa.effect = effect; //makes POOF
|
||||
}
|
||||
bsa.damageAmount = damage;
|
||||
bsa.stackAttacked = curStack->ID;
|
||||
bsa.attackerID = -1;
|
||||
curStack->prepareAttacked(bsa, getRandomGenerator());
|
||||
|
||||
StacksInjured si;
|
||||
si.stacks.push_back(bsa);
|
||||
sendAndApply(&si);
|
||||
|
||||
if(oneTimeObstacle)
|
||||
removeObstacle(*obstacle);
|
||||
}
|
||||
else if (obstacle.obstacleType == CObstacleInstance::LAND_MINE)
|
||||
{
|
||||
COMPLAIN_RET_IF((!spellObstacle), "Invalid obstacle instance");
|
||||
//You don't get hit by a Mine you can see.
|
||||
if (gs->curB->battleIsObstacleVisibleForSide(obstacle, (BattlePerspective::BattlePerspective)side))
|
||||
return;
|
||||
|
||||
oneTimeObstacle = true;
|
||||
effect = 82; //makes
|
||||
|
||||
const CSpell * sp = SpellID(SpellID::LAND_MINE).toSpell();
|
||||
|
||||
if (sp->isImmuneByStack(hero, curStack))
|
||||
return;
|
||||
|
||||
damage = sp->calculateDamage(hero, curStack,
|
||||
spellObstacle->spellLevel, spellObstacle->casterSpellPower);
|
||||
//TODO even if obstacle wasn't created by hero (Tower "moat") it should deal dmg as if cast by hero,
|
||||
//if it is bigger than default dmg. Or is it just irrelevant H3 implementation quirk
|
||||
}
|
||||
else if (obstacle.obstacleType == CObstacleInstance::FIRE_WALL)
|
||||
{
|
||||
COMPLAIN_RET_IF((!spellObstacle), "Invalid obstacle instance");
|
||||
const CSpell * sp = SpellID(SpellID::FIRE_WALL).toSpell();
|
||||
|
||||
if (sp->isImmuneByStack(hero, curStack))
|
||||
return;
|
||||
|
||||
damage = sp->calculateDamage(hero, curStack,
|
||||
spellObstacle->spellLevel, spellObstacle->casterSpellPower);
|
||||
}
|
||||
else
|
||||
{
|
||||
//no other obstacle does damage to stack
|
||||
return;
|
||||
}
|
||||
|
||||
BattleStackAttacked bsa;
|
||||
if (effect >= 0)
|
||||
{
|
||||
bsa.flags |= BattleStackAttacked::EFFECT;
|
||||
bsa.effect = effect; //makes POOF
|
||||
}
|
||||
bsa.damageAmount = damage;
|
||||
bsa.stackAttacked = curStack->ID;
|
||||
bsa.attackerID = -1;
|
||||
curStack->prepareAttacked(bsa, getRandomGenerator());
|
||||
|
||||
StacksInjured si;
|
||||
si.stacks.push_back(bsa);
|
||||
sendAndApply(&si);
|
||||
|
||||
if (oneTimeObstacle)
|
||||
removeObstacle(obstacle);
|
||||
if(!curStack->alive())
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
void CGameHandler::handleTimeEvents()
|
||||
|
@ -199,7 +199,8 @@ public:
|
||||
bool makeCustomAction(BattleAction &ba);
|
||||
void stackEnchantedTrigger(const CStack * stack);
|
||||
void stackTurnTrigger(const CStack *stack);
|
||||
void handleDamageFromObstacle(const CObstacleInstance &obstacle, const CStack * curStack); //checks if obstacle is land mine and handles possible consequences
|
||||
bool handleDamageFromObstacle(const CStack * curStack, bool stackIsMoving = false); //checks if obstacle is land mine and handles possible consequences
|
||||
|
||||
void removeObstacle(const CObstacleInstance &obstacle);
|
||||
bool queryReply( QueryID qid, const JsonNode & answer, PlayerColor player );
|
||||
bool hireHero( const CGObjectInstance *obj, ui8 hid, PlayerColor player );
|
||||
|
Loading…
Reference in New Issue
Block a user