1
0
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:
ArseniyShestakov 2017-07-15 00:09:37 +03:00 committed by GitHub
commit 006fa6c6df
4 changed files with 115 additions and 119 deletions

View File

@ -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;

View File

@ -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;

View File

@ -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()

View File

@ -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 );