mirror of
https://github.com/vcmi/vcmi.git
synced 2025-12-01 23:12:49 +02:00
Work in progress on BattleAI. Related changes:
* battle AIs receive ptr to CBattleCallback (not sure why it was CPlayerBattleCallback, likely mistake) * reworked some battle callback methods to be more generic and able to handle some hypothetic scenarios * for testing purposes in duel mode the first AI will be taken fro mconfig and the second will remain stupid ai * minor changes
This commit is contained in:
@@ -10,6 +10,26 @@
|
||||
|
||||
namespace SiegeStuffThatShouldBeMovedToHandlers // <=== TODO
|
||||
{
|
||||
static void retreiveTurretDamageRange(const CStack *turret, double &outMinDmg, double &outMaxDmg)
|
||||
{
|
||||
assert(turret->getCreature()->idNumber == 149); //arrow turret
|
||||
|
||||
switch(turret->position)
|
||||
{
|
||||
case -2: //keep
|
||||
outMinDmg = 15;
|
||||
outMaxDmg = 15;
|
||||
break;
|
||||
case -3: case -4: //turrets
|
||||
outMinDmg = 7.5;
|
||||
outMaxDmg = 7.5;
|
||||
break;
|
||||
default:
|
||||
tlog1 << "Unknown turret type!" << std::endl;
|
||||
outMaxDmg = outMinDmg = 1;
|
||||
}
|
||||
}
|
||||
|
||||
static int lineToWallHex(int line) //returns hex with wall in given line (y coordinate)
|
||||
{
|
||||
static const int lineToHex[] = {12, 29, 45, 62, 78, 95, 112, 130, 147, 165, 182};
|
||||
@@ -89,6 +109,11 @@ void CCallbackBase::setBattle(const BattleInfo *B)
|
||||
battle = B;
|
||||
}
|
||||
|
||||
int CCallbackBase::getPlayerID() const
|
||||
{
|
||||
return player;
|
||||
}
|
||||
|
||||
ui8 CBattleInfoEssentials::battleTerrainType() const
|
||||
{
|
||||
RETURN_IF_NOT_BATTLE(-1);
|
||||
@@ -369,21 +394,26 @@ ui8 CBattleInfoEssentials::battleGetWallState(int partOfWall) const
|
||||
}
|
||||
|
||||
si8 CBattleInfoCallback::battleHasWallPenalty( const CStack * stack, BattleHex destHex ) const
|
||||
{
|
||||
return battleHasWallPenalty(stack, stack->position, destHex);
|
||||
}
|
||||
|
||||
si8 CBattleInfoCallback::battleHasWallPenalty(const IBonusBearer *bonusBearer, BattleHex shooterPosition, BattleHex destHex) const
|
||||
{
|
||||
RETURN_IF_NOT_BATTLE(false);
|
||||
if (!battleGetSiegeLevel() || stack->hasBonusOfType(Bonus::NO_WALL_PENALTY))
|
||||
if (!battleGetSiegeLevel() || bonusBearer->hasBonusOfType(Bonus::NO_WALL_PENALTY))
|
||||
return false;
|
||||
|
||||
const int wallInStackLine = lineToWallHex(stack->position.getY());
|
||||
const int wallInStackLine = lineToWallHex(shooterPosition.getY());
|
||||
const int wallInDestLine = lineToWallHex(destHex.getY());
|
||||
|
||||
const bool stackLeft = stack->position < wallInStackLine;
|
||||
const bool stackLeft = shooterPosition < wallInStackLine;
|
||||
const bool destRight = destHex > wallInDestLine;
|
||||
|
||||
if (stackLeft && destRight) //shooting from outside to inside
|
||||
{
|
||||
int row = (stack->position + destHex) / (2 * GameConstants::BFIELD_WIDTH);
|
||||
if (stack->position > destHex && ((destHex % GameConstants::BFIELD_WIDTH - stack->position % GameConstants::BFIELD_WIDTH) < 2)) //shooting up high
|
||||
int row = (shooterPosition + destHex) / (2 * GameConstants::BFIELD_WIDTH);
|
||||
if (shooterPosition > destHex && ((destHex % GameConstants::BFIELD_WIDTH - shooterPosition % GameConstants::BFIELD_WIDTH) < 2)) //shooting up high
|
||||
row -= 2;
|
||||
const int wallPos = lineToWallHex(row);
|
||||
if (battleHexToWallPart(wallPos) != -1) //wall still exists or is indestructible
|
||||
@@ -524,7 +554,7 @@ void CBattleInfoCallback::battleGetStackQueue(std::vector<const CStack *> &out,
|
||||
const CStack *active = battleActiveStack();
|
||||
|
||||
//active stack hasn't taken any action yet - must be placed at the beginning of queue, no matter what
|
||||
if(!turn && active && active->willMove() && !vstd::contains(active->state, EBattleStackState::WAITING))
|
||||
if(!turn && active && active->willMove() && !active->waited())
|
||||
{
|
||||
out.push_back(active);
|
||||
if(out.size() == howMany)
|
||||
@@ -549,7 +579,7 @@ void CBattleInfoCallback::battleGetStackQueue(std::vector<const CStack *> &out,
|
||||
}
|
||||
|
||||
int p = -1; //in which phase this tack will move?
|
||||
if(turn <= 0 && vstd::contains(s->state, EBattleStackState::WAITING)) //consider waiting state only for ongoing round
|
||||
if(turn <= 0 && s->waited()) //consider waiting state only for ongoing round
|
||||
{
|
||||
if(vstd::contains(s->state, EBattleStackState::HAD_MORALE))
|
||||
p = 2;
|
||||
@@ -736,74 +766,66 @@ TDmgRange CBattleInfoCallback::calculateDmgRange(const CStack* attacker, const C
|
||||
return calculateDmgRange(attacker, defender, attacker->count, shooting, charge, lucky, deathBlow, ballistaDoubleDmg);
|
||||
}
|
||||
|
||||
TDmgRange CBattleInfoCallback::calculateDmgRange( const CStack* attacker, const CStack* defender, TQuantity attackerCount,
|
||||
bool shooting, ui8 charge, bool lucky, bool deathBlow, bool ballistaDoubleDmg ) const
|
||||
TDmgRange CBattleInfoCallback::calculateDmgRange(const BattleAttackInfo &info) const
|
||||
{
|
||||
double additiveBonus = 1.0, multBonus = 1.0,
|
||||
minDmg = attacker->getMinDamage() * attackerCount,
|
||||
maxDmg = attacker->getMaxDamage() * attackerCount;
|
||||
minDmg = info.attackerBonuses->getMinDamage() * info.attackerCount,
|
||||
maxDmg = info.attackerBonuses->getMaxDamage() * info.attackerCount;
|
||||
|
||||
if(attacker->getCreature()->idNumber == 149) //arrow turret
|
||||
const CCreature *attackerType = info.attacker->getCreature(),
|
||||
*defenderType = info.defender->getCreature();
|
||||
|
||||
if(attackerType->idNumber == 149) //arrow turret
|
||||
{
|
||||
switch(attacker->position)
|
||||
{
|
||||
case -2: //keep
|
||||
minDmg = 15;
|
||||
maxDmg = 15;
|
||||
break;
|
||||
case -3: case -4: //turrets
|
||||
minDmg = 7.5;
|
||||
maxDmg = 7.5;
|
||||
break;
|
||||
}
|
||||
SiegeStuffThatShouldBeMovedToHandlers::retreiveTurretDamageRange(info.attacker, minDmg, maxDmg);
|
||||
}
|
||||
|
||||
if(attacker->hasBonusOfType(Bonus::SIEGE_WEAPON) && attacker->getCreature()->idNumber != 149) //any siege weapon, but only ballista can attack (second condition - not arrow turret)
|
||||
if(info.attackerBonuses->hasBonusOfType(Bonus::SIEGE_WEAPON) && attackerType->idNumber != 149) //any siege weapon, but only ballista can attack (second condition - not arrow turret)
|
||||
{ //minDmg and maxDmg are multiplied by hero attack + 1
|
||||
auto retreivePrimSkill = [&](int skill) -> int
|
||||
auto retreiveHeroPrimSkill = [&](int skill) -> int
|
||||
{
|
||||
const Bonus *b = attacker->getBonus(Selector::sourceTypeSel(Bonus::HERO_BASE_SKILL) && Selector::typeSubtype(Bonus::PRIMARY_SKILL, skill));
|
||||
const Bonus *b = info.attackerBonuses->getBonus(Selector::sourceTypeSel(Bonus::HERO_BASE_SKILL) && Selector::typeSubtype(Bonus::PRIMARY_SKILL, skill));
|
||||
return b ? b->val : 0; //if there is no hero or no info on his primary skill, return 0
|
||||
};
|
||||
|
||||
|
||||
minDmg *= retreivePrimSkill(PrimarySkill::ATTACK) + 1;
|
||||
maxDmg *= retreivePrimSkill(PrimarySkill::ATTACK) + 1;
|
||||
minDmg *= retreiveHeroPrimSkill(PrimarySkill::ATTACK) + 1;
|
||||
maxDmg *= retreiveHeroPrimSkill(PrimarySkill::ATTACK) + 1;
|
||||
}
|
||||
|
||||
int attackDefenceDifference = 0;
|
||||
if(attacker->hasBonusOfType(Bonus::GENERAL_ATTACK_REDUCTION))
|
||||
if(info.attackerBonuses->hasBonusOfType(Bonus::GENERAL_ATTACK_REDUCTION))
|
||||
{
|
||||
double multAttackReduction = attacker->valOfBonuses(Bonus::GENERAL_ATTACK_REDUCTION, -1024) / 100.0;
|
||||
attackDefenceDifference = attacker->Attack() * multAttackReduction;
|
||||
double multAttackReduction = info.attackerBonuses->valOfBonuses(Bonus::GENERAL_ATTACK_REDUCTION, -1024) / 100.0;
|
||||
attackDefenceDifference = info.attackerBonuses->Attack() * multAttackReduction;
|
||||
}
|
||||
else
|
||||
{
|
||||
attackDefenceDifference = attacker->Attack();
|
||||
attackDefenceDifference = info.attackerBonuses->Attack();
|
||||
}
|
||||
|
||||
if(attacker->hasBonusOfType(Bonus::ENEMY_DEFENCE_REDUCTION))
|
||||
if(info.attackerBonuses->hasBonusOfType(Bonus::ENEMY_DEFENCE_REDUCTION))
|
||||
{
|
||||
double multDefenceReduction = (100 - attacker->valOfBonuses(Bonus::ENEMY_DEFENCE_REDUCTION, -1024)) / 100.0;
|
||||
attackDefenceDifference -= defender->Defense() * multDefenceReduction;
|
||||
double multDefenceReduction = (100 - info.attackerBonuses->valOfBonuses(Bonus::ENEMY_DEFENCE_REDUCTION, -1024)) / 100.0;
|
||||
attackDefenceDifference -= info.defenderBonuses->Defense() * multDefenceReduction;
|
||||
}
|
||||
else
|
||||
{
|
||||
attackDefenceDifference -= defender->Defense();
|
||||
attackDefenceDifference -= info.defenderBonuses->Defense();
|
||||
}
|
||||
|
||||
//calculating total attack/defense skills modifier
|
||||
|
||||
if(shooting) //precision handling (etc.)
|
||||
attackDefenceDifference += attacker->getBonuses(Selector::typeSubtype(Bonus::PRIMARY_SKILL, PrimarySkill::ATTACK), Selector::effectRange(Bonus::ONLY_DISTANCE_FIGHT))->totalValue();
|
||||
if(info.shooting) //precision handling (etc.)
|
||||
attackDefenceDifference += info.attackerBonuses->getBonuses(Selector::typeSubtype(Bonus::PRIMARY_SKILL, PrimarySkill::ATTACK), Selector::effectRange(Bonus::ONLY_DISTANCE_FIGHT))->totalValue();
|
||||
else //bloodlust handling (etc.)
|
||||
attackDefenceDifference += attacker->getBonuses(Selector::typeSubtype(Bonus::PRIMARY_SKILL, PrimarySkill::ATTACK), Selector::effectRange(Bonus::ONLY_MELEE_FIGHT))->totalValue();
|
||||
attackDefenceDifference += info.attackerBonuses->getBonuses(Selector::typeSubtype(Bonus::PRIMARY_SKILL, PrimarySkill::ATTACK), Selector::effectRange(Bonus::ONLY_MELEE_FIGHT))->totalValue();
|
||||
|
||||
|
||||
if(attacker->getEffect(55)) //slayer handling
|
||||
if(const Bonus *slayerEffect = info.attackerBonuses->getEffect(Spells::SLAYER)) //slayer handling
|
||||
{
|
||||
std::vector<int> affectedIds;
|
||||
int spLevel = attacker->getEffect(55)->val;
|
||||
int spLevel = slayerEffect->val;
|
||||
|
||||
for(int g = 0; g < VLC->creh->creatures.size(); ++g)
|
||||
{
|
||||
@@ -821,9 +843,9 @@ TDmgRange CBattleInfoCallback::calculateDmgRange( const CStack* attacker, const
|
||||
|
||||
for(ui32 g=0; g<affectedIds.size(); ++g)
|
||||
{
|
||||
if(defender->getCreature()->idNumber == affectedIds[g])
|
||||
if(defenderType->idNumber == affectedIds[g])
|
||||
{
|
||||
attackDefenceDifference += VLC->spellh->spells[55]->powers[attacker->getEffect(55)->val];
|
||||
attackDefenceDifference += VLC->spellh->spells[Spells::SLAYER]->powers[spLevel];
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -843,51 +865,51 @@ TDmgRange CBattleInfoCallback::calculateDmgRange( const CStack* attacker, const
|
||||
|
||||
|
||||
//applying jousting bonus
|
||||
if( attacker->hasBonusOfType(Bonus::JOUSTING) && !defender->hasBonusOfType(Bonus::CHARGE_IMMUNITY) )
|
||||
additiveBonus += charge * 0.05;
|
||||
if( info.attackerBonuses->hasBonusOfType(Bonus::JOUSTING) && !info.defenderBonuses->hasBonusOfType(Bonus::CHARGE_IMMUNITY) )
|
||||
additiveBonus += info.chargedFields * 0.05;
|
||||
|
||||
|
||||
//handling secondary abilities and artifacts giving premies to them
|
||||
if(shooting)
|
||||
additiveBonus += attacker->valOfBonuses(Bonus::SECONDARY_SKILL_PREMY, CGHeroInstance::ARCHERY) / 100.0;
|
||||
if(info.shooting)
|
||||
additiveBonus += info.attackerBonuses->valOfBonuses(Bonus::SECONDARY_SKILL_PREMY, CGHeroInstance::ARCHERY) / 100.0;
|
||||
else
|
||||
additiveBonus += attacker->valOfBonuses(Bonus::SECONDARY_SKILL_PREMY, CGHeroInstance::OFFENCE) / 100.0;
|
||||
additiveBonus += info.attackerBonuses->valOfBonuses(Bonus::SECONDARY_SKILL_PREMY, CGHeroInstance::OFFENCE) / 100.0;
|
||||
|
||||
if(defender)
|
||||
multBonus *= (std::max(0, 100 - defender->valOfBonuses(Bonus::SECONDARY_SKILL_PREMY, CGHeroInstance::ARMORER))) / 100.0;
|
||||
if(info.defenderBonuses)
|
||||
multBonus *= (std::max(0, 100 - info.defenderBonuses->valOfBonuses(Bonus::SECONDARY_SKILL_PREMY, CGHeroInstance::ARMORER))) / 100.0;
|
||||
|
||||
//handling hate effect
|
||||
additiveBonus += attacker->valOfBonuses(Bonus::HATE, defender->getCreature()->idNumber) / 100.;
|
||||
additiveBonus += info.attackerBonuses->valOfBonuses(Bonus::HATE, defenderType->idNumber) / 100.;
|
||||
|
||||
//luck bonus
|
||||
if (lucky)
|
||||
if (info.luckyHit)
|
||||
{
|
||||
additiveBonus += 1.0;
|
||||
}
|
||||
|
||||
//ballista double dmg
|
||||
if(ballistaDoubleDmg)
|
||||
if(info.ballistaDoubleDamage)
|
||||
{
|
||||
additiveBonus += 1.0;
|
||||
}
|
||||
|
||||
if (deathBlow) //Dread Knight and many WoGified creatures
|
||||
if (info.deathBlow) //Dread Knight and many WoGified creatures
|
||||
{
|
||||
additiveBonus += 1.0;
|
||||
}
|
||||
|
||||
//handling spell effects
|
||||
if(!shooting && defender->hasBonusOfType(Bonus::GENERAL_DAMAGE_REDUCTION, 0)) //eg. shield
|
||||
if(!info.shooting && info.defenderBonuses->hasBonusOfType(Bonus::GENERAL_DAMAGE_REDUCTION, 0)) //eg. shield
|
||||
{
|
||||
multBonus *= defender->valOfBonuses(Bonus::GENERAL_DAMAGE_REDUCTION, 0) / 100.0;
|
||||
multBonus *= info.defenderBonuses->valOfBonuses(Bonus::GENERAL_DAMAGE_REDUCTION, 0) / 100.0;
|
||||
}
|
||||
else if(shooting && defender->hasBonusOfType(Bonus::GENERAL_DAMAGE_REDUCTION, 1)) //eg. air shield
|
||||
else if(info.shooting && info.defenderBonuses->hasBonusOfType(Bonus::GENERAL_DAMAGE_REDUCTION, 1)) //eg. air shield
|
||||
{
|
||||
multBonus *= defender->valOfBonuses(Bonus::GENERAL_DAMAGE_REDUCTION, 1) / 100.0;
|
||||
multBonus *= info.defenderBonuses->valOfBonuses(Bonus::GENERAL_DAMAGE_REDUCTION, 1) / 100.0;
|
||||
}
|
||||
|
||||
TBonusListPtr curseEffects = attacker->getBonuses(Selector::type(Bonus::ALWAYS_MINIMUM_DAMAGE)); //attacker->getEffect(42);
|
||||
TBonusListPtr blessEffects = attacker->getBonuses(Selector::type(Bonus::ALWAYS_MAXIMUM_DAMAGE)); //attacker->getEffect(43);
|
||||
TBonusListPtr curseEffects = info.attackerBonuses->getBonuses(Selector::type(Bonus::ALWAYS_MINIMUM_DAMAGE)); //attacker->getEffect(42);
|
||||
TBonusListPtr blessEffects = info.attackerBonuses->getBonuses(Selector::type(Bonus::ALWAYS_MAXIMUM_DAMAGE)); //attacker->getEffect(43);
|
||||
int curseBlessAdditiveModifier = blessEffects->totalValue() - curseEffects->totalValue();
|
||||
double curseMultiplicativePenalty = curseEffects->size() ? (*std::max_element(curseEffects->begin(), curseEffects->end(), &Bonus::compareByAdditionalInfo))->additionalInfo : 0;
|
||||
|
||||
@@ -904,12 +926,12 @@ TDmgRange CBattleInfoCallback::calculateDmgRange( const CStack* attacker, const
|
||||
};
|
||||
|
||||
//wall / distance penalty + advanced air shield
|
||||
const bool distPenalty = !attacker->hasBonusOfType(Bonus::NO_DISTANCE_PENALTY) && battleHasDistancePenalty(attacker, defender->position);
|
||||
const bool obstaclePenalty = battleHasWallPenalty(attacker, defender->position);
|
||||
const bool distPenalty = !info.attackerBonuses->hasBonusOfType(Bonus::NO_DISTANCE_PENALTY) && battleHasDistancePenalty(info.attackerBonuses, info.attackerPosition, info.defenderPosition);
|
||||
const bool obstaclePenalty = battleHasWallPenalty(info.attackerBonuses, info.attackerPosition, info.defenderPosition);
|
||||
|
||||
if (shooting)
|
||||
if (info.shooting)
|
||||
{
|
||||
if (distPenalty || defender->hasBonus(isAdvancedAirShield))
|
||||
if (distPenalty || info.defenderBonuses->hasBonus(isAdvancedAirShield))
|
||||
{
|
||||
multBonus *= 0.5;
|
||||
}
|
||||
@@ -918,7 +940,7 @@ TDmgRange CBattleInfoCallback::calculateDmgRange( const CStack* attacker, const
|
||||
multBonus *= 0.5; //cumulative
|
||||
}
|
||||
}
|
||||
if (!shooting && attacker->hasBonusOfType(Bonus::SHOOTER) && !attacker->hasBonusOfType(Bonus::NO_MELEE_PENALTY))
|
||||
if(!info.shooting && info.attackerBonuses->hasBonusOfType(Bonus::SHOOTER) && !info.attackerBonuses->hasBonusOfType(Bonus::NO_MELEE_PENALTY))
|
||||
{
|
||||
multBonus *= 0.5;
|
||||
}
|
||||
@@ -950,18 +972,38 @@ TDmgRange CBattleInfoCallback::calculateDmgRange( const CStack* attacker, const
|
||||
return returnedVal;
|
||||
}
|
||||
|
||||
TDmgRange CBattleInfoCallback::calculateDmgRange( const CStack* attacker, const CStack* defender, TQuantity attackerCount,
|
||||
bool shooting, ui8 charge, bool lucky, bool deathBlow, bool ballistaDoubleDmg ) const
|
||||
{
|
||||
BattleAttackInfo bai(attacker, defender, shooting);
|
||||
bai.attackerCount = attackerCount;
|
||||
bai.chargedFields = charge;
|
||||
bai.luckyHit = lucky;
|
||||
bai.deathBlow = deathBlow;
|
||||
bai.ballistaDoubleDamage = ballistaDoubleDmg;
|
||||
return calculateDmgRange(bai);
|
||||
}
|
||||
|
||||
TDmgRange CBattleInfoCallback::battleEstimateDamage(const CStack * attacker, const CStack * defender, TDmgRange * retaliationDmg) const
|
||||
{
|
||||
RETURN_IF_NOT_BATTLE(std::make_pair(0, 0));
|
||||
|
||||
const bool shooting = battleCanShoot(attacker, defender->position);
|
||||
const BattleAttackInfo bai(attacker, defender, shooting);
|
||||
return battleEstimateDamage(bai, retaliationDmg);
|
||||
}
|
||||
|
||||
std::pair<ui32, ui32> CBattleInfoCallback::battleEstimateDamage(const BattleAttackInfo &bai, std::pair<ui32, ui32> * retaliationDmg /*= NULL*/) const
|
||||
{
|
||||
RETURN_IF_NOT_BATTLE(std::make_pair(0, 0));
|
||||
|
||||
const bool shooting = battleCanShoot(bai.attacker, bai.defenderPosition); //TODO handle bonus bearer
|
||||
//const ui8 mySide = !attacker->attackerOwned;
|
||||
|
||||
TDmgRange ret = calculateDmgRange(attacker, defender, shooting, 0, false, false, false);
|
||||
TDmgRange ret = calculateDmgRange(bai);
|
||||
|
||||
if(retaliationDmg)
|
||||
{
|
||||
if(shooting)
|
||||
if(bai.shooting)
|
||||
{
|
||||
retaliationDmg->first = retaliationDmg->second = 0;
|
||||
}
|
||||
@@ -972,7 +1014,11 @@ TDmgRange CBattleInfoCallback::battleEstimateDamage(const CStack * attacker, con
|
||||
{
|
||||
BattleStackAttacked bsa;
|
||||
bsa.damageAmount = ret.*pairElems[i];
|
||||
retaliationDmg->*pairElems[!i] = calculateDmgRange(defender, attacker, bsa.newAmount, false, 0, false, false, false).*pairElems[!i];
|
||||
bai.defender->prepareAttacked(bsa);
|
||||
|
||||
auto retaliationAttack = bai.reverse();
|
||||
retaliationAttack.attackerCount = bsa.newAmount;
|
||||
retaliationDmg->*pairElems[!i] = calculateDmgRange(retaliationAttack).*pairElems[!i];
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -980,7 +1026,6 @@ TDmgRange CBattleInfoCallback::battleEstimateDamage(const CStack * attacker, con
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
shared_ptr<const CObstacleInstance> CBattleInfoCallback::battleGetObstacleOnPos(BattleHex tile, bool onlyBlocking /*= true*/) const
|
||||
{
|
||||
RETURN_IF_NOT_BATTLE(shared_ptr<const CObstacleInstance>());
|
||||
@@ -1329,6 +1374,7 @@ ReachabilityInfo::TDistances CBattleInfoCallback::battleGetDistances(const CStac
|
||||
RETURN_IF_NOT_BATTLE(ret);
|
||||
|
||||
ReachabilityInfo::Parameters params(stack);
|
||||
params.perspective = battleGetMySide();
|
||||
params.startPosition = hex.isValid() ? hex : stack->position;
|
||||
auto reachability = getReachability(params);
|
||||
|
||||
@@ -1342,21 +1388,27 @@ ReachabilityInfo::TDistances CBattleInfoCallback::battleGetDistances(const CStac
|
||||
}
|
||||
|
||||
si8 CBattleInfoCallback::battleHasDistancePenalty(const CStack * stack, BattleHex destHex) const
|
||||
{
|
||||
return battleHasDistancePenalty(stack, stack->position, destHex);
|
||||
}
|
||||
|
||||
si8 CBattleInfoCallback::battleHasDistancePenalty(const IBonusBearer *bonusBearer, BattleHex shooterPosition, BattleHex destHex) const
|
||||
{
|
||||
RETURN_IF_NOT_BATTLE(false);
|
||||
if(stack->hasBonusOfType(Bonus::NO_DISTANCE_PENALTY))
|
||||
if(bonusBearer->hasBonusOfType(Bonus::NO_DISTANCE_PENALTY))
|
||||
return false;
|
||||
|
||||
if(BattleHex::getDistance(stack->position, destHex) <= 10)
|
||||
if(BattleHex::getDistance(shooterPosition, destHex) <= GameConstants::BATTLE_PENALTY_DISTANCE)
|
||||
return false;
|
||||
|
||||
const CStack * dstStack = battleGetStackByPos(destHex, false);
|
||||
if(dstStack)
|
||||
if(const CStack * dstStack = battleGetStackByPos(destHex, false))
|
||||
{
|
||||
//If on dest hex stands stack that occupies a hex within our distance
|
||||
BOOST_FOREACH(auto hex, dstStack->getHexes())
|
||||
if(BattleHex::getDistance(stack->position, hex) <= 10)
|
||||
if(BattleHex::getDistance(shooterPosition, hex) <= GameConstants::BATTLE_PENALTY_DISTANCE)
|
||||
return false;
|
||||
|
||||
//TODO what about two-hex shooters?
|
||||
}
|
||||
|
||||
return true;
|
||||
@@ -1503,7 +1555,7 @@ ESpellCastProblem::ESpellCastProblem CBattleInfoCallback::battleIsImmune(const C
|
||||
{
|
||||
if(spell->getTargetType() == CSpell::CREATURE
|
||||
|| (spell->getTargetType() == CSpell::CREATURE_EXPERT_MASSIVE
|
||||
&& mode == ECastingMode::HERO_CASTING
|
||||
&& mode == ECastingMode::HERO_CASTING //TODO why???
|
||||
&& caster
|
||||
&& caster->getSpellSchoolLevel(spell) < SecSkillLevel::EXPERT))
|
||||
{
|
||||
@@ -1590,7 +1642,7 @@ ESpellCastProblem::ESpellCastProblem CBattleInfoCallback::battleCanCastThisSpell
|
||||
switch(spell->getTargetType())
|
||||
{
|
||||
case CSpell::CREATURE:
|
||||
case CSpell::CREATURE_EXPERT_MASSIVE:
|
||||
case CSpell::CREATURE_EXPERT_MASSIVE:
|
||||
if(mode == ECastingMode::HERO_CASTING)
|
||||
{
|
||||
const CGHeroInstance * caster = battleGetFightingHero(side);
|
||||
@@ -1641,6 +1693,51 @@ ESpellCastProblem::ESpellCastProblem CBattleInfoCallback::battleCanCastThisSpell
|
||||
return ESpellCastProblem::OK;
|
||||
}
|
||||
|
||||
std::vector<BattleHex> CBattleInfoCallback::battleGetPossibleTargets(int player, const CSpell *spell) const
|
||||
{
|
||||
std::vector<BattleHex> ret;
|
||||
RETURN_IF_NOT_BATTLE(ret);
|
||||
|
||||
auto mode = ECastingMode::HERO_CASTING; //TODO get rid of this!
|
||||
|
||||
switch(spell->getTargetType())
|
||||
{
|
||||
case CSpell::CREATURE:
|
||||
case CSpell::CREATURE_EXPERT_MASSIVE:
|
||||
{
|
||||
const CGHeroInstance * caster = battleGetFightingHero(playerToSide(player)); //TODO
|
||||
|
||||
BOOST_FOREACH(const CStack * stack, battleAliveStacks())
|
||||
{
|
||||
switch (spell->positiveness)
|
||||
{
|
||||
case CSpell::POSITIVE:
|
||||
if(stack->owner == caster->getOwner())
|
||||
if(battleIsImmune(caster, spell, mode, stack->position) == ESpellCastProblem::OK)
|
||||
ret.push_back(stack->position);
|
||||
break;
|
||||
|
||||
case CSpell::NEUTRAL:
|
||||
if(battleIsImmune(caster, spell, mode, stack->position) == ESpellCastProblem::OK)
|
||||
ret.push_back(stack->position);
|
||||
break;
|
||||
|
||||
case CSpell::NEGATIVE:
|
||||
if(stack->owner != caster->getOwner())
|
||||
if(battleIsImmune(caster, spell, mode, stack->position) == ESpellCastProblem::OK)
|
||||
ret.push_back(stack->position);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
tlog1 << "FIXME " << __FUNCTION__ << " doesn't work with target type " << spell->getTargetType() << std::endl;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
ui32 CBattleInfoCallback::battleGetSpellCost(const CSpell * sp, const CGHeroInstance * caster) const
|
||||
{
|
||||
RETURN_IF_NOT_BATTLE(-1);
|
||||
@@ -1906,6 +2003,7 @@ si8 CBattleInfoCallback::battleMaxSpellLevel() const
|
||||
|
||||
return GameConstants::SPELL_LEVELS;
|
||||
}
|
||||
|
||||
bool AccessibilityInfo::accessible(BattleHex tile, const CStack *stack) const
|
||||
{
|
||||
return accessible(tile, stack->doubleWide(), stack->attackerOwned);
|
||||
@@ -2011,3 +2109,38 @@ bool CPlayerBattleCallback::battleCanCastSpell(ESpellCastProblem::ESpellCastProb
|
||||
|
||||
return problem == ESpellCastProblem::OK;
|
||||
}
|
||||
|
||||
BattleAttackInfo::BattleAttackInfo(const CStack *Attacker, const CStack *Defender, bool Shooting)
|
||||
{
|
||||
attacker = Attacker;
|
||||
defender = Defender;
|
||||
|
||||
attackerBonuses = Attacker;
|
||||
defenderBonuses = Defender;
|
||||
|
||||
attackerPosition = Attacker->position;
|
||||
defenderPosition = Defender->position;
|
||||
|
||||
attackerCount = Attacker->count;
|
||||
shooting = Shooting;
|
||||
chargedFields = 0;
|
||||
|
||||
luckyHit = false;
|
||||
deathBlow = false;
|
||||
ballistaDoubleDamage = false;
|
||||
}
|
||||
|
||||
BattleAttackInfo BattleAttackInfo::reverse() const
|
||||
{
|
||||
BattleAttackInfo ret = *this;
|
||||
std::swap(ret.attacker, ret.defender);
|
||||
std::swap(ret.attackerBonuses, ret.defenderBonuses);
|
||||
std::swap(ret.attackerPosition, ret.defenderPosition);
|
||||
|
||||
ret.attackerCount = ret.attacker->count;
|
||||
ret.shooting = false;
|
||||
ret.chargedFields = 0;
|
||||
ret.luckyHit = ret.ballistaDoubleDamage = ret.deathBlow = false;
|
||||
|
||||
return ret;
|
||||
}
|
||||
Reference in New Issue
Block a user