mirror of
https://github.com/vcmi/vcmi.git
synced 2024-12-26 22:57:00 +02:00
Merge pull request #332 from FeniksFire/SmallBugFix
Fix handling damage from obstacles
This commit is contained in:
commit
006fa6c6df
@ -843,6 +843,24 @@ std::vector<std::shared_ptr<const CObstacleInstance>> CBattleInfoCallback::battl
|
|||||||
return obstacles;
|
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 CBattleInfoCallback::getAccesibility() const
|
||||||
{
|
{
|
||||||
AccessibilityInfo ret;
|
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
|
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>> 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
|
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;
|
void battleGetStackQueue(std::vector<const CStack *> &out, const int howMany, const int turn = 0, int lastMoved = -1) const;
|
||||||
|
@ -1230,7 +1230,6 @@ int CGameHandler::moveStack(int stack, BattleHex dest)
|
|||||||
}
|
}
|
||||||
else //for non-flying creatures
|
else //for non-flying creatures
|
||||||
{
|
{
|
||||||
std::vector<std::shared_ptr<const CObstacleInstance>> obstacle, obstacle2; //obstacle that interrupted movement
|
|
||||||
std::vector<BattleHex> tiles;
|
std::vector<BattleHex> tiles;
|
||||||
const int tilesToMove = std::max((int)(path.first.size() - creSpeed), 0);
|
const int tilesToMove = std::max((int)(path.first.size() - creSpeed), 0);
|
||||||
int v = path.first.size()-1;
|
int v = path.first.size()-1;
|
||||||
@ -1329,16 +1328,14 @@ int CGameHandler::moveStack(int stack, BattleHex dest)
|
|||||||
}
|
}
|
||||||
|
|
||||||
//if we walked onto something, finalize this portion of stack movement check into obstacle
|
//if we walked onto something, finalize this portion of stack movement check into obstacle
|
||||||
obstacle = battleGetAllObstaclesOnPos(hex, false);
|
if(!battleGetAllObstaclesOnPos(hex, false).empty())
|
||||||
if(!obstacle.empty())
|
|
||||||
obstacleHit = true;
|
obstacleHit = true;
|
||||||
|
|
||||||
if (curStack->doubleWide())
|
if (curStack->doubleWide())
|
||||||
{
|
{
|
||||||
BattleHex otherHex = curStack->occupiedHex(hex);
|
BattleHex otherHex = curStack->occupiedHex(hex);
|
||||||
|
|
||||||
//two hex creature hit obstacle by backside
|
//two hex creature hit obstacle by backside
|
||||||
obstacle2 = battleGetAllObstaclesOnPos(otherHex, false);
|
auto obstacle2 = battleGetAllObstaclesOnPos(otherHex, false);
|
||||||
if(otherHex.isValid() && !obstacle2.empty())
|
if(otherHex.isValid() && !obstacle2.empty())
|
||||||
obstacleHit = true;
|
obstacleHit = true;
|
||||||
}
|
}
|
||||||
@ -1360,25 +1357,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
|
//we don't handle obstacle at the destination tile -> it's handled separately in the if at the end
|
||||||
if (curStack->position != dest)
|
if (curStack->position != dest)
|
||||||
{
|
{
|
||||||
auto processObstacle = [&](std::vector<std::shared_ptr<const CObstacleInstance>> & obs)
|
if(stackIsMoving && start != curStack->position)
|
||||||
{
|
stackIsMoving = handleDamageFromObstacle(curStack, stackIsMoving);
|
||||||
if(!obs.empty())
|
|
||||||
{
|
|
||||||
for(auto & i : obs)
|
|
||||||
{
|
|
||||||
handleDamageFromObstacle(*i, curStack);
|
|
||||||
//if stack die in explosion or interrupted by obstacle, abort movement
|
|
||||||
if(i->stopsMovement() || !curStack->alive())
|
|
||||||
stackIsMoving = false;
|
|
||||||
i.reset();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
processObstacle(obstacle);
|
|
||||||
if (curStack->alive())
|
|
||||||
processObstacle(obstacle2);
|
|
||||||
|
|
||||||
if (gateStateChanging)
|
if (gateStateChanging)
|
||||||
{
|
{
|
||||||
if (curStack->position == openGateAtHex)
|
if (curStack->position == openGateAtHex)
|
||||||
@ -1406,26 +1386,8 @@ int CGameHandler::moveStack(int stack, BattleHex dest)
|
|||||||
}
|
}
|
||||||
|
|
||||||
//handling obstacle on the final field (separate, because it affects both flying and walking stacks)
|
//handling obstacle on the final field (separate, because it affects both flying and walking stacks)
|
||||||
if (curStack->alive())
|
handleDamageFromObstacle(curStack);
|
||||||
{
|
|
||||||
auto theLastObstacle = battleGetAllObstaclesOnPos(curStack->position, false);
|
|
||||||
for(auto & i : theLastObstacle)
|
|
||||||
if(curStack->alive())
|
|
||||||
handleDamageFromObstacle(*i, curStack);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (curStack->alive() && curStack->doubleWide())
|
|
||||||
{
|
|
||||||
BattleHex otherHex = curStack->occupiedHex(curStack->position);
|
|
||||||
if (otherHex.isValid())
|
|
||||||
{
|
|
||||||
//two hex creature hit obstacle by backside
|
|
||||||
auto theLastObstacle = battleGetAllObstaclesOnPos(otherHex, false);
|
|
||||||
for(auto & i : theLastObstacle)
|
|
||||||
if(curStack->alive())
|
|
||||||
handleDamageFromObstacle(*i, curStack);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -4029,10 +3991,10 @@ bool CGameHandler::makeBattleAction(BattleAction &ba)
|
|||||||
|
|
||||||
const CGHeroInstance * attackingHero = gs->curB->battleGetFightingHero(ba.side);
|
const CGHeroInstance * attackingHero = gs->curB->battleGetFightingHero(ba.side);
|
||||||
|
|
||||||
if (destinationStack->alive()
|
if(destinationStack->alive()
|
||||||
&& (stack->getCreature()->idNumber == CreatureID::BALLISTA)
|
&& (stack->getCreature()->idNumber == CreatureID::BALLISTA)
|
||||||
&& (attackingHero->getSecSkillLevel(SecondarySkill::ARTILLERY) >= SecSkillLevel::ADVANCED)
|
&& (attackingHero->getSecSkillLevel(SecondarySkill::ARTILLERY) >= SecSkillLevel::ADVANCED)
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
BattleAttack bat2;
|
BattleAttack bat2;
|
||||||
bat2.flags |= BattleAttack::SHOT;
|
bat2.flags |= BattleAttack::SHOT;
|
||||||
@ -4130,8 +4092,8 @@ bool CGameHandler::makeBattleAction(BattleAction &ba)
|
|||||||
std::vector<EWallPart::EWallPart> 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(EWallPart::EWallPart(i));
|
allowedTargets.push_back(EWallPart::EWallPart(i));
|
||||||
}
|
}
|
||||||
if (allowedTargets.empty())
|
if (allowedTargets.empty())
|
||||||
@ -4325,7 +4287,10 @@ bool CGameHandler::makeBattleAction(BattleAction &ba)
|
|||||||
break;
|
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);
|
battleMadeAction.setn(true);
|
||||||
return ok;
|
return ok;
|
||||||
}
|
}
|
||||||
@ -4617,77 +4582,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
|
if(!curStack->alive())
|
||||||
int damage = -1;
|
return false;
|
||||||
int effect = -1;
|
bool containDamageFromMoat = false;
|
||||||
bool oneTimeObstacle = false;
|
for(auto & obstacle : getAllAffectedObstaclesByStack(curStack))
|
||||||
|
|
||||||
//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)
|
|
||||||
{
|
{
|
||||||
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)
|
if(!curStack->alive())
|
||||||
{
|
return false;
|
||||||
COMPLAIN_RET_IF((!spellObstacle), "Invalid obstacle instance");
|
return true;
|
||||||
//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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void CGameHandler::handleTimeEvents()
|
void CGameHandler::handleTimeEvents()
|
||||||
|
@ -196,7 +196,8 @@ public:
|
|||||||
bool makeCustomAction(BattleAction &ba);
|
bool makeCustomAction(BattleAction &ba);
|
||||||
void stackEnchantedTrigger(const CStack * stack);
|
void stackEnchantedTrigger(const CStack * stack);
|
||||||
void stackTurnTrigger(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);
|
void removeObstacle(const CObstacleInstance &obstacle);
|
||||||
bool queryReply( QueryID qid, const JsonNode & answer, PlayerColor player );
|
bool queryReply( QueryID qid, const JsonNode & answer, PlayerColor player );
|
||||||
bool hireHero( const CGObjectInstance *obj, ui8 hid, PlayerColor player );
|
bool hireHero( const CGObjectInstance *obj, ui8 hid, PlayerColor player );
|
||||||
|
Loading…
Reference in New Issue
Block a user