1
0
mirror of https://github.com/vcmi/vcmi.git synced 2024-12-24 22:14:36 +02:00

Merge pull request #3500 from IvanSavenko/fix_dendroid_bind

[1.4.3] Fix handling of Dendroid's Bind ability
This commit is contained in:
Ivan Savenko 2024-01-15 12:02:06 +02:00 committed by GitHub
commit a582cb554e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 32 additions and 41 deletions

View File

@ -147,7 +147,7 @@ BattleAction BattleEvaluator::selectStackAction(const CStack * stack)
(int)bestAttack.from,
(int)bestAttack.attack.attacker->getPosition().hex,
bestAttack.attack.chargeDistance,
bestAttack.attack.attacker->speed(0, true),
bestAttack.attack.attacker->getMovementRange(0),
bestAttack.defenderDamageReduce,
bestAttack.attackerDamageReduce,
score
@ -553,7 +553,7 @@ bool BattleEvaluator::attemptCastingSpell(const CStack * activeStack)
auto needFullEval = vstd::contains_if(allUnits, [&](const battle::Unit * u) -> bool
{
auto original = cb->getBattle(battleID)->battleGetUnitByID(u->unitId());
return !original || u->speed() != original->speed();
return !original || u->getMovementRange() != original->getMovementRange();
});
DamageCache safeCopy = damageCache;

View File

@ -297,7 +297,7 @@ MoveTarget BattleExchangeEvaluator::findMoveTowardsUnreachable(
if(targets.unreachableEnemies.empty())
return result;
auto speed = activeStack->speed();
auto speed = activeStack->getMovementRange();
if(speed == 0)
return result;
@ -324,7 +324,7 @@ MoveTarget BattleExchangeEvaluator::findMoveTowardsUnreachable(
auto turnsToRich = (distance - 1) / speed + 1;
auto hexes = closestStack->getSurroundingHexes();
auto enemySpeed = closestStack->speed();
auto enemySpeed = closestStack->getMovementRange();
auto speedRatio = speed / static_cast<float>(enemySpeed);
auto multiplier = speedRatio > 1 ? 1 : speedRatio;
@ -753,7 +753,7 @@ std::vector<const battle::Unit *> BattleExchangeEvaluator::getOneTurnReachableUn
continue;
}
auto unitSpeed = unit->speed(turn);
auto unitSpeed = unit->getMovementRange(turn);
auto radius = unitSpeed * (turn + 1);
ReachabilityInfo unitReachability = vstd::getOrCompute(
@ -819,7 +819,7 @@ bool BattleExchangeEvaluator::checkPositionBlocksOurStacks(HypotheticBattle & hb
float ratio = blockedUnitDamage / (float)(blockedUnitDamage + activeUnitDamage + 0.01);
auto unitReachability = turnBattle.getReachability(unit);
auto unitSpeed = unit->speed(turn); // Cached value, to avoid performance hit
auto unitSpeed = unit->getMovementRange(turn); // Cached value, to avoid performance hit
for(BattleHex hex = BattleHex::TOP_LEFT; hex.isValid(); hex = hex + 1)
{

View File

@ -117,7 +117,7 @@ std::vector<SlotInfo>::iterator ArmyManager::getWeakestCreature(std::vector<Slot
if(left.creature->getLevel() != right.creature->getLevel())
return left.creature->getLevel() < right.creature->getLevel();
return left.creature->speed() > right.creature->speed();
return left.creature->getMovementRange() > right.creature->getMovementRange();
});
return weakest;

View File

@ -63,7 +63,7 @@ std::vector<SlotInfo>::iterator ArmyManager::getWeakestCreature(std::vector<Slot
if(left.creature->getLevel() != right.creature->getLevel())
return left.creature->getLevel() < right.creature->getLevel();
return left.creature->speed() > right.creature->speed();
return left.creature->getMovementRange() > right.creature->getMovementRange();
});
return weakest;

View File

@ -568,7 +568,7 @@ bool BattleActionsController::actionIsLegal(PossiblePlayerBattleAction action, B
switch (action.get())
{
case PossiblePlayerBattleAction::CHOOSE_TACTICS_STACK:
return (targetStack && targetStackOwned && targetStack->speed() > 0);
return (targetStack && targetStackOwned && targetStack->getMovementRange() > 0);
case PossiblePlayerBattleAction::CREATURE_INFO:
return (targetStack && targetStackOwned && targetStack->alive());

View File

@ -363,7 +363,7 @@ bool MovementAnimation::init()
Point begPosition = owner.stacksController->getStackPositionAtHex(prevHex, stack);
Point endPosition = owner.stacksController->getStackPositionAtHex(nextHex, stack);
progressPerSecond = AnimationControls::getMovementDistance(stack->unitType());
progressPerSecond = AnimationControls::getMovementRange(stack->unitType());
begX = begPosition.x;
begY = begPosition.y;

View File

@ -640,7 +640,7 @@ void BattleInterface::tacticPhaseEnd()
static bool immobile(const CStack *s)
{
return !s->speed(0, true); //should bound stacks be immobile?
return s->getMovementRange() == 0; //should bound stacks be immobile?
}
void BattleInterface::tacticNextStack(const CStack * current)

View File

@ -148,7 +148,7 @@ float AnimationControls::getSpellEffectSpeed()
return static_cast<float>(getAnimationSpeedFactor() * 10);
}
float AnimationControls::getMovementDistance(const CCreature * creature)
float AnimationControls::getMovementRange(const CCreature * creature)
{
// H3 speed: 2/4/6 tiles per second
return static_cast<float>( 2.0 * getAnimationSpeedFactor() / creature->animation.walkAnimationTime);

View File

@ -50,7 +50,7 @@ namespace AnimationControls
float getSpellEffectSpeed();
/// returns speed of movement animation across the screen, in tiles per second
float getMovementDistance(const CCreature * creature);
float getMovementRange(const CCreature * creature);
/// returns speed of movement animation across the screen, in pixels per seconds
float getFlightDistance(const CCreature * creature);

View File

@ -543,7 +543,7 @@ CStackWindow::MainSection::MainSection(CStackWindow * owner, int yOffset, bool s
addStatLabel(EStat::DEFENCE, parent->info->creature->getDefense(battleStack->isShooter()), battleStack->getDefense(battleStack->isShooter()));
addStatLabel(EStat::DAMAGE, parent->info->stackNode->getMinDamage(battleStack->isShooter()) * dmgMultiply, battleStack->getMaxDamage(battleStack->isShooter()) * dmgMultiply);
addStatLabel(EStat::HEALTH, parent->info->creature->getMaxHealth(), battleStack->getMaxHealth());
addStatLabel(EStat::SPEED, parent->info->creature->speed(), battleStack->speed());
addStatLabel(EStat::SPEED, parent->info->creature->getMovementRange(), battleStack->getMovementRange());
if(battleStack->isShooter())
addStatLabel(EStat::SHOTS, battleStack->shots.total(), battleStack->shots.available());
@ -563,7 +563,7 @@ CStackWindow::MainSection::MainSection(CStackWindow * owner, int yOffset, bool s
addStatLabel(EStat::DEFENCE, parent->info->creature->getDefense(shooter), parent->info->stackNode->getDefense(shooter));
addStatLabel(EStat::DAMAGE, parent->info->stackNode->getMinDamage(shooter) * dmgMultiply, parent->info->stackNode->getMaxDamage(shooter) * dmgMultiply);
addStatLabel(EStat::HEALTH, parent->info->creature->getMaxHealth(), parent->info->stackNode->getMaxHealth());
addStatLabel(EStat::SPEED, parent->info->creature->speed(), parent->info->stackNode->speed());
addStatLabel(EStat::SPEED, parent->info->creature->getMovementRange(), parent->info->stackNode->getMovementRange());
if(shooter)
addStatLabel(EStat::SHOTS, parent->info->stackNode->valOfBonuses(BonusType::SHOTS));

View File

@ -788,7 +788,7 @@ Determines how many times per combat affected creature can cast its targeted spe
- subtype - spell id, eg. spell.iceBolt
- value - chance (percent)
- additional info - \[X, Y, Z\]
- X - spell level
- X - spell mastery level (1 - Basic, 3 - Expert)
- Y = 0 - all attacks, 1 - shot only, 2 - melee only
- Z (optional) - layer for multiple SPELL_AFTER_ATTACK bonuses and multi-turn casting. Empty or value less than 0 = not participating in layering.
When enabled - spells from specific layer will not be cast until target has all spells from previous layer on him. Spell from last layer is on repeat if none of spells on lower layers expired.
@ -798,7 +798,7 @@ Determines how many times per combat affected creature can cast its targeted spe
- subtype - spell id
- value - chance %
- additional info - \[X, Y, Z\]
- X - spell level
- X - spell mastery level (1 - Basic, 3 - Expert)
- Y = 0 - all attacks, 1 - shot only, 2 - melee only
- Z (optional) - layer for multiple SPELL_BEFORE_ATTACK bonuses and multi-turn casting. Empty or value less than 0 = not participating in layering.
When enabled - spells from specific layer will not be cast until target has all spells from previous layer on him. Spell from last layer is on repeat if none of spells on lower layers expired.

View File

@ -23,7 +23,7 @@ class DLL_LINKAGE ACreature: public AFactionMember
{
public:
bool isLiving() const; //non-undead, non-non living or alive
ui32 speed(int turn = 0, bool useBind = false) const; //get speed (in moving tiles) of creature with all modificators
ui32 getMovementRange(int turn = 0) const; //get speed (in moving tiles) of creature with all modificators
virtual ui32 getMaxHealth() const; //get max HP of stack with all modifiers
};

View File

@ -168,15 +168,14 @@ ui32 ACreature::getMaxHealth() const
return std::max(1, value); //never 0
}
ui32 ACreature::speed(int turn, bool useBind) const
ui32 ACreature::getMovementRange(int turn) const
{
//war machines cannot move
if(getBonusBearer()->hasBonus(Selector::type()(BonusType::SIEGE_WEAPON).And(Selector::turns(turn))))
{
return 0;
}
//bind effect check - doesn't influence stack initiative
if(useBind && getBonusBearer()->hasBonus(Selector::type()(BonusType::BIND_EFFECT).And(Selector::turns(turn))))
if(getBonusBearer()->hasBonus(Selector::type()(BonusType::BIND_EFFECT).And(Selector::turns(turn))))
{
return 0;
}

View File

@ -267,7 +267,7 @@ std::vector<PossiblePlayerBattleAction> CBattleInfoCallback::getClientActionsFor
allowedActionList.push_back(PossiblePlayerBattleAction::ATTACK); //all active stacks can attack
allowedActionList.push_back(PossiblePlayerBattleAction::WALK_AND_ATTACK); //not all stacks can always walk, but we will check this elsewhere
if(stack->canMove() && stack->speed(0, true)) //probably no reason to try move war machines or bound stacks
if(stack->canMove() && stack->getMovementRange(0)) //probably no reason to try move war machines or bound stacks
allowedActionList.push_back(PossiblePlayerBattleAction::MOVE_STACK);
const auto * siegedTown = battleGetDefendedTown();
@ -570,7 +570,7 @@ std::vector<BattleHex> CBattleInfoCallback::battleGetAvailableHexes(const Reacha
if(!unit->getPosition().isValid()) //turrets
return ret;
auto unitSpeed = unit->speed(0, true);
auto unitSpeed = unit->getMovementRange(0);
const bool tacticsPhase = battleTacticDist() && battleGetTacticsSide() == unit->unitSide();
@ -741,15 +741,15 @@ DamageEstimation CBattleInfoCallback::battleEstimateDamage(const battle::Unit *
{
RETURN_IF_NOT_BATTLE({});
auto reachability = battleGetDistances(attacker, attacker->getPosition());
int movementDistance = reachability[attackerPosition];
return battleEstimateDamage(attacker, defender, movementDistance, retaliationDmg);
int getMovementRange = reachability[attackerPosition];
return battleEstimateDamage(attacker, defender, getMovementRange, retaliationDmg);
}
DamageEstimation CBattleInfoCallback::battleEstimateDamage(const battle::Unit * attacker, const battle::Unit * defender, int movementDistance, DamageEstimation * retaliationDmg) const
DamageEstimation CBattleInfoCallback::battleEstimateDamage(const battle::Unit * attacker, const battle::Unit * defender, int getMovementRange, DamageEstimation * retaliationDmg) const
{
RETURN_IF_NOT_BATTLE({});
const bool shooting = battleCanShoot(attacker, defender->getPosition());
const BattleAttackInfo bai(attacker, defender, movementDistance, shooting);
const BattleAttackInfo bai(attacker, defender, getMovementRange, shooting);
return battleEstimateDamage(bai, retaliationDmg);
}

View File

@ -98,7 +98,7 @@ public:
/// returns pair <min dmg, max dmg>
DamageEstimation battleEstimateDamage(const BattleAttackInfo & bai, DamageEstimation * retaliationDmg = nullptr) const;
DamageEstimation battleEstimateDamage(const battle::Unit * attacker, const battle::Unit * defender, BattleHex attackerPosition, DamageEstimation * retaliationDmg = nullptr) const;
DamageEstimation battleEstimateDamage(const battle::Unit * attacker, const battle::Unit * defender, int movementDistance, DamageEstimation * retaliationDmg = nullptr) const;
DamageEstimation battleEstimateDamage(const battle::Unit * attacker, const battle::Unit * defender, int getMovementRange, DamageEstimation * retaliationDmg = nullptr) const;
bool battleHasPenaltyOnLine(BattleHex from, BattleHex dest, bool checkWall, bool checkMoat) const;
bool battleHasDistancePenalty(const IBonusBearer * shooter, BattleHex shooterPosition, BattleHex destHex) const;

View File

@ -659,7 +659,7 @@ int BattleActionProcessor::moveStack(const CBattleInfoCallback & battle, int sta
ret = path.second;
int creSpeed = curStack->speed(0, true);
int creSpeed = curStack->getMovementRange(0);
if (battle.battleGetTacticDist() > 0 && creSpeed > 0)
creSpeed = GameConstants::BFIELD_SIZE;
@ -1139,18 +1139,10 @@ void BattleActionProcessor::attackCasting(const CBattleInfoCallback & battle, bo
for(const auto & sf : *spellsByType)
{
int meleeRanged;
if(sf->additionalInfo.size() < 2)
{
// legacy format
vstd::amax(spellLevel, sf->additionalInfo[0] % 1000);
meleeRanged = sf->additionalInfo[0] / 1000;
}
else
{
vstd::amax(spellLevel, sf->additionalInfo[0]);
meleeRanged = sf->additionalInfo[1];
}
if (meleeRanged == 0 || (meleeRanged == 1 && ranged) || (meleeRanged == 2 && !ranged))
if (meleeRanged == CAddInfo::NONE || meleeRanged == 0 || (meleeRanged == 1 && ranged) || (meleeRanged == 2 && !ranged))
castMe = true;
}
int chance = attacker->valOfBonuses((Selector::typeSubtype(attackMode, BonusSubtypeID(spellID))));