mirror of
https://github.com/vcmi/vcmi.git
synced 2024-12-28 23:06:24 +02:00
Initial version of ACCURATE_SHOT implementation
This commit is contained in:
parent
fbd988df42
commit
7283a4861e
@ -184,6 +184,8 @@
|
||||
"vcmi.battleWindow.damageEstimation.kills" : "%d will perish",
|
||||
"vcmi.battleWindow.damageEstimation.kills.1" : "%d will perish",
|
||||
"vcmi.battleWindow.killed" : "Killed",
|
||||
"vcmi.battleWindow.accurateShot.resultDescription" : "%d %s were killed by accurate shots!",
|
||||
"vcmi.battleWindow.accurateShot.resultDescription.1" : "1 %s was killed with an accurate shot!",
|
||||
|
||||
"vcmi.battleResultsWindow.applyResultsLabel" : "Apply battle result",
|
||||
|
||||
@ -328,7 +330,9 @@
|
||||
"vcmi.stackExperience.rank.8" : "Elite",
|
||||
"vcmi.stackExperience.rank.9" : "Master",
|
||||
"vcmi.stackExperience.rank.10" : "Ace",
|
||||
|
||||
|
||||
"core.bonus.ACCURATE_SHOT.name": "Accurate Shot",
|
||||
"core.bonus.ACCURATE_SHOT.description": "Has (${val}% - penalties) extra kills chance",
|
||||
"core.bonus.ADDITIONAL_ATTACK.name": "Double Strike",
|
||||
"core.bonus.ADDITIONAL_ATTACK.description": "Attacks twice",
|
||||
"core.bonus.ADDITIONAL_RETALIATION.name": "Additional retaliations",
|
||||
|
@ -3,6 +3,13 @@
|
||||
// LEVEL_SPELL_IMMUNITY
|
||||
|
||||
{
|
||||
"ACCURATE_SHOT":
|
||||
{
|
||||
"graphics":
|
||||
{
|
||||
"icon": "zvs/Lib1.res/E_DIST"
|
||||
}
|
||||
},
|
||||
"ADDITIONAL_ATTACK":
|
||||
{
|
||||
"graphics":
|
||||
|
@ -732,6 +732,15 @@ If player has affected unit under his control in any army, he will receive addit
|
||||
|
||||
Affected unit will not use spellcast as default attack option
|
||||
|
||||
### ACCURATE_SHOT
|
||||
|
||||
Affected unit will kill additional units after attack, similar to death stare - works only for ranged attack
|
||||
|
||||
- subtype:
|
||||
spell identifier for spell that receives value that should be killed on input, spell.deathStare is used by default, use 'accurateShot' as part of spell name to allow detection for proper battle log description
|
||||
- val:
|
||||
chance to kill, counted separately for each unit in attacking stack, percentage. Chance gets lessened by 2/3 with range penalty and effect won't trigger with wall penalty. At most (stack size \* chance / 100 **[rounded up]**) units can be killed at once. TODO: recheck formula
|
||||
|
||||
## Creature spellcasting and activated abilities
|
||||
|
||||
### SPELLCASTER
|
||||
|
@ -515,6 +515,7 @@ static void loadBonusSubtype(BonusSubtypeID & subtype, BonusType type, const Jso
|
||||
case BonusType::SPECIFIC_SPELL_POWER:
|
||||
case BonusType::ENCHANTED:
|
||||
case BonusType::MORE_DAMAGE_FROM_SPELL:
|
||||
case BonusType::ACCURATE_SHOT:
|
||||
case BonusType::NOT_ACTIVE:
|
||||
{
|
||||
VLC->identifiers()->requestIdentifier( "spell", node, [&subtype](int32_t identifier)
|
||||
|
@ -174,6 +174,7 @@ class JsonNode;
|
||||
BONUS_NAME(MAX_MORALE) /*cheat bonus*/ \
|
||||
BONUS_NAME(MAX_LUCK) /*cheat bonus*/ \
|
||||
BONUS_NAME(FEROCITY) /*extra attacks, only if at least some creatures killed while attacking target unit, val = amount of additional attacks, additional info = amount of creatures killed to trigger (default 1)*/ \
|
||||
BONUS_NAME(ACCURATE_SHOT) /*HotA Sea Dog-like ability - ranged only, val = full arrow trigger percent, subtype = spell identifier that killed value goes through (death stare by default) - use 'accurateShot' as part of spell name for proper battle log description*/ \
|
||||
/* end of list */
|
||||
|
||||
|
||||
|
@ -152,6 +152,22 @@ void Damage::describeEffect(std::vector<MetaString> & log, const Mechanics * m,
|
||||
m->caster->getCasterName(line);
|
||||
log.push_back(line);
|
||||
}
|
||||
else if(m->getSpell()->getJsonKey().find("accurateShot") != std::string::npos && !multiple)
|
||||
{
|
||||
MetaString line;
|
||||
if(kills > 1)
|
||||
{
|
||||
line.appendTextID("vcmi.battleWindow.accurateShot.resultDescription"); //(number) (unit type) was killed with an accurate shot!
|
||||
line.replaceNumber(kills);
|
||||
firstTarget->addNameReplacement(line, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
line.appendTextID("vcmi.battleWindow.accurateShot.resultDescription.1"); //1 (unit type) were killed by accurate shots!
|
||||
firstTarget->addNameReplacement(line, false);
|
||||
}
|
||||
log.push_back(line);
|
||||
}
|
||||
else if(m->getSpellIndex() == SpellID::THUNDERBOLT && !multiple)
|
||||
{
|
||||
{
|
||||
|
@ -1214,7 +1214,7 @@ void BattleActionProcessor::handleAfterAttackCasting(const CBattleInfoCallback &
|
||||
// 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.0f;
|
||||
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);
|
||||
@ -1241,6 +1241,53 @@ void BattleActionProcessor::handleAfterAttackCasting(const CBattleInfoCallback &
|
||||
}
|
||||
}
|
||||
|
||||
if(attacker->hasBonusOfType(BonusType::ACCURATE_SHOT))
|
||||
{
|
||||
/* Intended to match HotA Sea Dogs
|
||||
* The Sea Dog's Accurate Shot is triggered after a shot:
|
||||
* 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;
|
||||
|
||||
int singleCreatureKillChancePercent = attacker->valOfBonuses(BonusType::ACCURATE_SHOT);
|
||||
|
||||
if(battle.battleHasDistancePenalty(attacker, attacker->getPosition(), defender->getPosition()))
|
||||
singleCreatureKillChancePercent = (singleCreatureKillChancePercent * 2) / 3;
|
||||
|
||||
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(killedCreatures)
|
||||
{
|
||||
//TODO: accurate shot was not originally available for multiple-hex attacks, but...
|
||||
const auto bonus = attacker->getBonus(Selector::type()(BonusType::ACCURATE_SHOT));
|
||||
|
||||
auto spellID = bonus->subtype.as<SpellID>();
|
||||
if(spellID == SpellID::NONE)
|
||||
spellID = SpellID(SpellID::DEATH_STARE); //fallback for spell not specified
|
||||
|
||||
const CSpell * spell = spellID.toSpell();
|
||||
|
||||
spells::AbilityCaster caster(attacker, 0);
|
||||
|
||||
spells::BattleCast parameters(&battle, &caster, spells::Mode::PASSIVE, spell);
|
||||
spells::Target target;
|
||||
target.emplace_back(defender);
|
||||
parameters.setEffectValue(killedCreatures);
|
||||
parameters.cast(gameHandler->spellEnv, target);
|
||||
}
|
||||
}
|
||||
|
||||
if(!defender->alive())
|
||||
return;
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user