1
0
mirror of https://github.com/vcmi/vcmi.git synced 2024-11-24 08:32:34 +02:00

Make one common handler for death stare and accurate shot

This commit is contained in:
Dydzio 2024-01-06 18:26:13 +01:00
parent 97977c06dc
commit b32c7beb05

View File

@ -1208,76 +1208,64 @@ void BattleActionProcessor::handleAfterAttackCasting(const CBattleInfoCallback &
return;
}
if(attacker->hasBonusOfType(BonusType::DEATH_STARE))
if(attacker->hasBonusOfType(BonusType::DEATH_STARE) || attacker->hasBonusOfType(BonusType::ACCURATE_SHOT))
{
// mechanics of Death Stare as in H3:
// each gorgon have 10% chance to kill (counted separately in H3) -> binomial distribution
//original formula x = min(x, (gorgons_count + 9)/10);
double chanceToKill = attacker->valOfBonuses(BonusType::DEATH_STARE, BonusCustomSubtype::deathStareGorgon) / 100.0;
vstd::amin(chanceToKill, 1); //cap at 100%
std::binomial_distribution<> distribution(attacker->getCount(), chanceToKill);
int staredCreatures = distribution(gameHandler->getRandomGenerator().getStdGenerator());
double cap = 1 / std::max(chanceToKill, (double)(0.01));//don't divide by 0
int maxToKill = static_cast<int>((attacker->getCount() + cap - 1) / cap); //not much more than chance * count
vstd::amin(staredCreatures, maxToKill);
staredCreatures += (attacker->level() * attacker->valOfBonuses(BonusType::DEATH_STARE, BonusCustomSubtype::deathStareCommander)) / defender->level();
if(staredCreatures)
{
//TODO: death stare was not originally available for multiple-hex attacks, but...
const CSpell * spell = SpellID(SpellID::DEATH_STARE).toSpell();
spells::AbilityCaster caster(attacker, 0);
spells::BattleCast parameters(&battle, &caster, spells::Mode::PASSIVE, spell);
spells::Target target;
target.emplace_back(defender);
parameters.setEffectValue(staredCreatures);
parameters.cast(gameHandler->spellEnv, target);
}
}
if(attacker->hasBonusOfType(BonusType::ACCURATE_SHOT))
{
/* Intended to match HotA Sea Dogs
* The Sea Dog's Accurate Shot is triggered after a shot:
/* mechanics of Accurate Shot as in HotA:
* each creature in an attacking stack has a X% chance of killing a creature in the attacked squad,
* but the total number of killed creatures cannot be more than (number of creatures in an attacking squad) * X/100 (rounded up).
* X = 3 multiplier for shooting without penalty and X = 2 if shooting with penalty. Ability doesn't work if shooting at creatures behind walls.
*/
if(!ranged || battle.battleHasWallPenalty(attacker, attacker->getPosition(), defender->getPosition()))
return;
auto bonus = attacker->getBonus(Selector::type()(BonusType::DEATH_STARE));
if(bonus == nullptr)
bonus = attacker->getBonus(Selector::type()(BonusType::ACCURATE_SHOT));
int singleCreatureKillChancePercent = attacker->valOfBonuses(BonusType::ACCURATE_SHOT);
if(bonus->type == BonusType::ACCURATE_SHOT && (!ranged || battle.battleHasWallPenalty(attacker, attacker->getPosition(), defender->getPosition())))
return; //should not work from behind walls, except being defender or under effect of golden bow etc.
if(battle.battleHasDistancePenalty(attacker, attacker->getPosition(), defender->getPosition()))
singleCreatureKillChancePercent = (singleCreatureKillChancePercent * 2) / 3;
int singleCreatureKillChancePercent;
if(bonus->type == BonusType::ACCURATE_SHOT)
{
singleCreatureKillChancePercent = attacker->valOfBonuses(BonusType::ACCURATE_SHOT);
if(battle.battleHasDistancePenalty(attacker, attacker->getPosition(), defender->getPosition()))
singleCreatureKillChancePercent = (singleCreatureKillChancePercent * 2) / 3;
}
else //DEATH_STARE
singleCreatureKillChancePercent = attacker->valOfBonuses(BonusType::DEATH_STARE, BonusCustomSubtype::deathStareGorgon);
double chanceToKill = singleCreatureKillChancePercent / 100.0;
vstd::amin(chanceToKill, 1); //cap at 100%
std::binomial_distribution<> distribution(attacker->getCount(), chanceToKill);
int killedCreatures = distribution(gameHandler->getRandomGenerator().getStdGenerator());
bool isMaxToKillRounded = attacker->getCount() * singleCreatureKillChancePercent % 100 == 0;
int maxToKill = attacker->getCount() * singleCreatureKillChancePercent / 100 + (isMaxToKillRounded ? 0 : 1);
vstd::amin(killedCreatures, maxToKill);
if(bonus->type == BonusType::DEATH_STARE)
{
double cap = 1 / std::max(chanceToKill, (double)(0.01));//don't divide by 0
int maxToKill = static_cast<int>((attacker->getCount() + cap - 1) / cap); //not much more than chance * count
vstd::amin(killedCreatures, maxToKill);
killedCreatures += (attacker->level() * attacker->valOfBonuses(BonusType::DEATH_STARE, BonusCustomSubtype::deathStareCommander)) / defender->level();
}
else //ACCURATE_SHOT
{
bool isMaxToKillRounded = attacker->getCount() * singleCreatureKillChancePercent % 100 == 0;
int maxToKill = attacker->getCount() * singleCreatureKillChancePercent / 100 + (isMaxToKillRounded ? 0 : 1);
vstd::amin(killedCreatures, maxToKill);
}
if(killedCreatures)
{
//TODO: accurate shot was not originally available for multiple-hex attacks, but...
const auto bonus = attacker->getBonus(Selector::type()(BonusType::ACCURATE_SHOT));
//TODO: death stare or accurate shot was not originally available for multiple-hex attacks, but...
auto spellID = bonus->subtype.as<SpellID>();
if(spellID == SpellID::NONE)
spellID = SpellID(SpellID::DEATH_STARE); //fallback for spell not specified
SpellID spellID = SpellID(SpellID::DEATH_STARE); //also used as fallback spell for ACCURATE_SHOT
if(bonus->type == BonusType::ACCURATE_SHOT && bonus->subtype.as<SpellID>() != SpellID::NONE)
spellID = bonus->subtype.as<SpellID>();
const CSpell * spell = spellID.toSpell();
spells::AbilityCaster caster(attacker, 0);
spells::BattleCast parameters(&battle, &caster, spells::Mode::PASSIVE, spell);