1
0
mirror of https://github.com/vcmi/vcmi.git synced 2025-02-03 13:01:33 +02:00

Merge pull request #1777 from rilian-la-te/beta-from-moats

Fixes for various issues extracted from moats branch
This commit is contained in:
Ivan Savenko 2023-03-27 17:16:59 +03:00 committed by GitHub
commit 6bce23e5e8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 58 additions and 39 deletions

View File

@ -78,6 +78,11 @@ void BattleObstacleController::obstaclePlaced(const std::vector<std::shared_ptr<
{
for (auto const & oi : obstacles)
{
auto side = owner.curInt->cb->playerToSide(owner.curInt->playerID);
if(!oi->visibleForSide(side.get(),owner.curInt->cb->battleHasNativeStack(side.get())))
continue;
auto spellObstacle = dynamic_cast<const SpellCreatedObstacle*>(oi.get());
if (!spellObstacle)

View File

@ -90,8 +90,7 @@
"damage":{
"type":"core:damage",
"optional":false,
"indirect":true,
"customEffectId" : 82
"indirect":true
}
}
},

View File

@ -798,19 +798,21 @@ std::vector<std::shared_ptr<const CObstacleInstance>> CBattleInfoCallback::battl
return obstacles;
}
std::vector<std::shared_ptr<const CObstacleInstance>> CBattleInfoCallback::getAllAffectedObstaclesByStack(const battle::Unit * unit) const
std::vector<std::shared_ptr<const CObstacleInstance>> CBattleInfoCallback::getAllAffectedObstaclesByStack(const battle::Unit * unit, const std::set<BattleHex> & passed) const
{
std::vector<std::shared_ptr<const CObstacleInstance>> affectedObstacles = std::vector<std::shared_ptr<const CObstacleInstance>>();
auto affectedObstacles = std::vector<std::shared_ptr<const CObstacleInstance>>();
RETURN_IF_NOT_BATTLE(affectedObstacles);
if(unit->alive())
{
affectedObstacles = battleGetAllObstaclesOnPos(unit->getPosition(), false);
if(!passed.count(unit->getPosition()))
affectedObstacles = battleGetAllObstaclesOnPos(unit->getPosition(), false);
if(unit->doubleWide())
{
BattleHex otherHex = unit->occupiedHex(unit->getPosition());
if(otherHex.isValid())
BattleHex otherHex = unit->occupiedHex();
if(otherHex.isValid() && !passed.count(otherHex))
for(auto & i : battleGetAllObstaclesOnPos(otherHex, false))
affectedObstacles.push_back(i);
if(!vstd::contains(affectedObstacles, i))
affectedObstacles.push_back(i);
}
for(auto hex : unit->getHexes())
if(hex == ESiegeHex::GATE_BRIDGE)
@ -932,6 +934,8 @@ ReachabilityInfo CBattleInfoCallback::makeBFS(const AccessibilityInfo &accessibi
return ret;
const std::set<BattleHex> obstacles = getStoppers(params.perspective);
auto checkParams = params;
checkParams.ignoreKnownAccessible = true; //Ignore starting hexes obstacles
std::queue<BattleHex> hexq; //bfs queue
@ -949,7 +953,7 @@ ReachabilityInfo CBattleInfoCallback::makeBFS(const AccessibilityInfo &accessibi
hexq.pop();
//walking stack can't step past the obstacles
if(curHex != params.startPosition && isInObstacle(curHex, obstacles, params))
if(isInObstacle(curHex, obstacles, checkParams))
continue;
const int costToNeighbour = ret.distances[curHex.hex] + 1;
@ -982,6 +986,9 @@ bool CBattleInfoCallback::isInObstacle(
for(auto occupiedHex : occupiedHexes)
{
if(params.ignoreKnownAccessible && vstd::contains(params.knownAccessible, occupiedHex))
continue;
if(vstd::contains(obstacles, occupiedHex))
{
if(occupiedHex == ESiegeHex::GATE_BRIDGE)

View File

@ -60,7 +60,7 @@ public:
boost::optional<int> battleIsFinished() const override; //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 override;
std::vector<std::shared_ptr<const CObstacleInstance>> getAllAffectedObstaclesByStack(const battle::Unit * unit) const override;
std::vector<std::shared_ptr<const CObstacleInstance>> getAllAffectedObstaclesByStack(const battle::Unit * unit, const std::set<BattleHex> & passed) const override;
const CStack * battleGetStackByPos(BattleHex pos, bool onlyAlive = true) const;

View File

@ -47,10 +47,7 @@ std::vector<std::shared_ptr<const CObstacleInstance>> CBattleInfoEssentials::bat
else
{
if(!!player && *perspective != battleGetMySide())
{
logGlobal->error("Unauthorized obstacles access attempt!");
return ret;
}
logGlobal->warn("Unauthorized obstacles access attempt, assuming massive spell");
}
for(const auto & obstacle : getBattle()->getAllObstacles())

View File

@ -72,7 +72,7 @@ public:
//blocking obstacles makes tile inaccessible, others cause special effects (like Land Mines, Moat, Quicksands)
virtual std::vector<std::shared_ptr<const CObstacleInstance>> battleGetAllObstaclesOnPos(BattleHex tile, bool onlyBlocking = true) const = 0;
virtual std::vector<std::shared_ptr<const CObstacleInstance>> getAllAffectedObstaclesByStack(const battle::Unit * unit) const = 0;
virtual std::vector<std::shared_ptr<const CObstacleInstance>> getAllAffectedObstaclesByStack(const battle::Unit * unit, const std::set<BattleHex> & passed) const = 0;
};

View File

@ -28,6 +28,7 @@ struct DLL_LINKAGE ReachabilityInfo
ui8 side = 0;
bool doubleWide = false;
bool flying = false;
bool ignoreKnownAccessible = false; //Ignore obstacles if it is in accessible hexes
std::vector<BattleHex> knownAccessible; //hexes that will be treated as accessible, even if they're occupied by stack (by default - tiles occupied by stack we do reachability for, so it doesn't block itself)
BattleHex startPosition; //assumed position of stack

View File

@ -121,6 +121,9 @@ void Obstacle::adjustAffectedHexes(std::set<BattleHex> & hexes, const Mechanics
bool Obstacle::applicable(Problem & problem, const Mechanics * m) const
{
if(hidden && m->battle()->battleHasNativeStack(m->battle()->otherSide(m->casterSide)))
return m->adaptProblem(ESpellCastProblem::NO_APPROPRIATE_TARGET, problem);
return LocationEffect::applicable(problem, m);
}
@ -270,12 +273,7 @@ void Obstacle::placeObstacles(ServerCallback * server, const Mechanics * m, cons
BattleObstaclesChanged pack;
boost::optional<BattlePerspective::BattlePerspective> perspective;
if(!m->battle()->getPlayerID())
perspective = boost::make_optional(BattlePerspective::ALL_KNOWING);
auto all = m->battle()->battleGetAllObstacles(perspective);
auto all = m->battle()->battleGetAllObstacles(BattlePerspective::ALL_KNOWING);
int obstacleIdToGive = 1;
for(auto & one : all)

View File

@ -72,7 +72,7 @@ bool RemoveObstacle::canRemove(const CObstacleInstance * obstacle) const
return true;
const auto *spellObstacle = dynamic_cast<const SpellCreatedObstacle *>(obstacle);
if(removeAllSpells && spellObstacle)
if(removeAllSpells && obstacle->obstacleType == CObstacleInstance::SPELL_CREATED)
return true;
if(spellObstacle && !removeSpells.empty())
@ -89,7 +89,7 @@ std::set<const CObstacleInstance *> RemoveObstacle::getTargets(const Mechanics *
std::set<const CObstacleInstance *> possibleTargets;
if(m->isMassive() || alwaysMassive)
{
for(const auto & obstacle : m->battle()->battleGetAllObstacles())
for(const auto & obstacle : m->battle()->battleGetAllObstacles(BattlePerspective::ALL_KNOWING))
if(canRemove(obstacle.get()))
possibleTargets.insert(obstacle.get());
}

View File

@ -40,7 +40,7 @@ bool UnitEffect::applicable(Problem & problem, const Mechanics * m) const
{
//stack effect is applicable in general if there is at least one smart target
auto mainFilter = std::bind(&UnitEffect::getStackFilter, this, m, true, _1);
auto mainFilter = std::bind(&UnitEffect::getStackFilter, this, m, false, _1);
auto predicate = std::bind(&UnitEffect::eraseByImmunityFilter, this, m, _1);
auto targets = m->battle()->battleGetUnitsIf(mainFilter);
@ -59,12 +59,12 @@ bool UnitEffect::applicable(Problem & problem, const Mechanics * m) const
bool UnitEffect::applicable(Problem & problem, const Mechanics * m, const EffectTarget & target) const
{
//stack effect is applicable if it affects at least one smart target
//assume target correctly transformed, just reapply smart filter
//stack effect is applicable if it affects at least one target (smartness should not be checked)
//assume target correctly transformed, just reapply filter
for(const auto & item : target)
if(item.unitValue)
if(getStackFilter(m, true, item.unitValue))
if(getStackFilter(m, false, item.unitValue))
return true;
return false;

View File

@ -1398,6 +1398,11 @@ int CGameHandler::moveStack(int stack, BattleHex dest)
//initing necessary tables
auto accessibility = getAccesibility(curStack);
std::set<BattleHex> passed;
//Ignore obstacles on starting position
passed.insert(curStack->getPosition());
if(curStack->doubleWide())
passed.insert(curStack->occupiedHex());
//shifting destination (if we have double wide stack and we can occupy dest but not be exactly there)
if(!stackAtEnd && curStack->doubleWide() && !accessibility.accessible(dest, curStack))
@ -1590,10 +1595,12 @@ int CGameHandler::moveStack(int stack, BattleHex dest)
if(otherHex.isValid() && !obstacle2.empty())
obstacleHit = true;
}
if(!obstacleHit)
passed.insert(hex);
}
}
if (tiles.size() > 0)
if (!tiles.empty())
{
//commit movement
BattleStackMoved sm;
@ -1609,7 +1616,12 @@ int CGameHandler::moveStack(int stack, BattleHex dest)
if (curStack->getPosition() != dest)
{
if(stackIsMoving && start != curStack->getPosition())
stackIsMoving = handleDamageFromObstacle(curStack, stackIsMoving);
{
stackIsMoving = handleDamageFromObstacle(curStack, stackIsMoving, passed);
passed.insert(curStack->getPosition());
if(curStack->doubleWide())
passed.insert(curStack->occupiedHex());
}
if (gateStateChanging)
{
if (curStack->getPosition() == openGateAtHex)
@ -1637,7 +1649,7 @@ int CGameHandler::moveStack(int stack, BattleHex dest)
}
//handling obstacle on the final field (separate, because it affects both flying and walking stacks)
handleDamageFromObstacle(curStack);
handleDamageFromObstacle(curStack, false, passed);
return ret;
}
@ -5288,13 +5300,13 @@ void CGameHandler::stackTurnTrigger(const CStack *st)
}
}
bool CGameHandler::handleDamageFromObstacle(const CStack * curStack, bool stackIsMoving)
bool CGameHandler::handleDamageFromObstacle(const CStack * curStack, bool stackIsMoving, const std::set<BattleHex> & passed)
{
if(!curStack->alive())
return false;
bool containDamageFromMoat = false;
bool movementStoped = false;
for(auto & obstacle : getAllAffectedObstaclesByStack(curStack))
bool movementStopped = false;
for(auto & obstacle : getAllAffectedObstaclesByStack(curStack, passed))
{
if(obstacle->obstacleType == CObstacleInstance::SPELL_CREATED)
{
@ -5305,7 +5317,7 @@ bool CGameHandler::handleDamageFromObstacle(const CStack * curStack, bool stackI
if(!spellObstacle)
COMPLAIN_RET("Invalid obstacle instance");
if(spellObstacle->trigger)
if(spellObstacle->triggersEffects())
{
const bool oneTimeObstacle = spellObstacle->removeOnTrigger;
@ -5369,13 +5381,13 @@ bool CGameHandler::handleDamageFromObstacle(const CStack * curStack, bool stackI
return false;
if((obstacle->stopsMovement() && stackIsMoving))
movementStoped = true;
movementStopped = true;
}
if(stackIsMoving)
return curStack->alive() && !movementStoped;
else
return curStack->alive();
return curStack->alive() && !movementStopped;
return curStack->alive();
}
void CGameHandler::handleTimeEvents()

View File

@ -232,7 +232,7 @@ public:
bool makeCustomAction(BattleAction &ba);
void stackEnchantedTrigger(const CStack * stack);
void stackTurnTrigger(const CStack *stack);
bool handleDamageFromObstacle(const CStack * curStack, bool stackIsMoving = false); //checks if obstacle is land mine and handles possible consequences
bool handleDamageFromObstacle(const CStack * curStack, bool stackIsMoving = false, const std::set<BattleHex> & passed = {}); //checks if obstacle is land mine and handles possible consequences
void removeObstacle(const CObstacleInstance &obstacle);
bool queryReply( QueryID qid, const JsonNode & answer, PlayerColor player );