mirror of
https://github.com/vcmi/vcmi.git
synced 2025-06-23 00:28:08 +02:00
Random with history for combat abilities
This commit is contained in:
@ -69,7 +69,7 @@ bool GameRandomizer::rollMoraleLuck(std::map<ObjectInstanceID, BiasedRandomizer>
|
|||||||
if(goodLuckChanceVector.size() == 0)
|
if(goodLuckChanceVector.size() == 0)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
return seeds.at(actor).roll(goodLuckChanceVector[chanceIndex], luckDiceSize, biasValue);
|
return seeds.at(actor).roll(goodLuckChanceVector[chanceIndex], luckDiceSize, biasValueLuckMorale);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool GameRandomizer::rollGoodMorale(ObjectInstanceID actor, int moraleValue)
|
bool GameRandomizer::rollGoodMorale(ObjectInstanceID actor, int moraleValue)
|
||||||
@ -92,11 +92,20 @@ bool GameRandomizer::rollBadLuck(ObjectInstanceID actor, int luckValue)
|
|||||||
return rollMoraleLuck(badLuckSeed, actor, luckValue, EGameSettings::COMBAT_LUCK_DICE_SIZE, EGameSettings::COMBAT_BAD_LUCK_CHANCE);
|
return rollMoraleLuck(badLuckSeed, actor, luckValue, EGameSettings::COMBAT_LUCK_DICE_SIZE, EGameSettings::COMBAT_BAD_LUCK_CHANCE);
|
||||||
}
|
}
|
||||||
|
|
||||||
//bool GameRandomizer::rollCombatAbility(ObjectInstanceID actor, int percentageChance)
|
bool GameRandomizer::rollCombatAbility(ObjectInstanceID actor, int percentageChance)
|
||||||
//{
|
{
|
||||||
//
|
if (!combatAbilitySeed.count(actor))
|
||||||
//}
|
combatAbilitySeed.emplace(actor, getDefault().nextInt());
|
||||||
//
|
|
||||||
|
if (percentageChance <= 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (percentageChance >= 100)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
return combatAbilitySeed.at(actor).roll(percentageChance, 100, biasValueAbility);
|
||||||
|
}
|
||||||
|
|
||||||
//HeroTypeID GameRandomizer::rollHero(PlayerColor player, FactionID faction)
|
//HeroTypeID GameRandomizer::rollHero(PlayerColor player, FactionID faction)
|
||||||
//{
|
//{
|
||||||
//
|
//
|
||||||
|
@ -37,7 +37,8 @@ public:
|
|||||||
|
|
||||||
class DLL_LINKAGE GameRandomizer final : public IGameRandomizer
|
class DLL_LINKAGE GameRandomizer final : public IGameRandomizer
|
||||||
{
|
{
|
||||||
static constexpr int biasValue = 10;
|
static constexpr int biasValueLuckMorale = 10;
|
||||||
|
static constexpr int biasValueAbility = 25;
|
||||||
|
|
||||||
struct HeroSkillRandomizer
|
struct HeroSkillRandomizer
|
||||||
{
|
{
|
||||||
@ -65,7 +66,6 @@ class DLL_LINKAGE GameRandomizer final : public IGameRandomizer
|
|||||||
std::map<ObjectInstanceID, BiasedRandomizer> badMoraleSeed;
|
std::map<ObjectInstanceID, BiasedRandomizer> badMoraleSeed;
|
||||||
std::map<ObjectInstanceID, BiasedRandomizer> goodLuckSeed;
|
std::map<ObjectInstanceID, BiasedRandomizer> goodLuckSeed;
|
||||||
std::map<ObjectInstanceID, BiasedRandomizer> badLuckSeed;
|
std::map<ObjectInstanceID, BiasedRandomizer> badLuckSeed;
|
||||||
|
|
||||||
std::map<ObjectInstanceID, BiasedRandomizer> combatAbilitySeed;
|
std::map<ObjectInstanceID, BiasedRandomizer> combatAbilitySeed;
|
||||||
|
|
||||||
bool rollMoraleLuck(std::map<ObjectInstanceID, BiasedRandomizer> & seeds, ObjectInstanceID actor, int moraleLuckValue, EGameSettings diceSize, EGameSettings diceWeights);
|
bool rollMoraleLuck(std::map<ObjectInstanceID, BiasedRandomizer> & seeds, ObjectInstanceID actor, int moraleLuckValue, EGameSettings diceSize, EGameSettings diceWeights);
|
||||||
@ -80,8 +80,8 @@ public:
|
|||||||
bool rollBadMorale(ObjectInstanceID actor, int moraleValue);
|
bool rollBadMorale(ObjectInstanceID actor, int moraleValue);
|
||||||
bool rollGoodLuck(ObjectInstanceID actor, int luckValue);
|
bool rollGoodLuck(ObjectInstanceID actor, int luckValue);
|
||||||
bool rollBadLuck(ObjectInstanceID actor, int luckValue);
|
bool rollBadLuck(ObjectInstanceID actor, int luckValue);
|
||||||
//
|
|
||||||
// bool rollCombatAbility(ObjectInstanceID actor, int percentageChance);
|
bool rollCombatAbility(ObjectInstanceID actor, int percentageChance);
|
||||||
|
|
||||||
// HeroTypeID rollHero(PlayerColor player, FactionID faction) override;
|
// HeroTypeID rollHero(PlayerColor player, FactionID faction) override;
|
||||||
|
|
||||||
|
@ -938,31 +938,22 @@ void BattleActionProcessor::makeAttack(const CBattleInfoCallback & battle, const
|
|||||||
bat.flags |= BattleAttack::COUNTER;
|
bat.flags |= BattleAttack::COUNTER;
|
||||||
|
|
||||||
const int attackerLuck = attacker->luckVal();
|
const int attackerLuck = attacker->luckVal();
|
||||||
|
ObjectInstanceID ownerArmy = battle.getBattle()->getSideArmy(attacker->unitSide())->id;
|
||||||
|
|
||||||
if(attackerLuck > 0)
|
if(attackerLuck > 0 && gameHandler->randomizer->rollGoodLuck(ownerArmy, attackerLuck))
|
||||||
{
|
bat.flags |= BattleAttack::LUCKY;
|
||||||
ObjectInstanceID ownerArmy = battle.getBattle()->getSideArmy(attacker->unitSide())->id;
|
|
||||||
if (gameHandler->randomizer->rollGoodLuck(ownerArmy, attackerLuck))
|
|
||||||
bat.flags |= BattleAttack::LUCKY;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(attackerLuck < 0)
|
if(attackerLuck < 0 && gameHandler->randomizer->rollBadLuck(ownerArmy, -attackerLuck))
|
||||||
{
|
bat.flags |= BattleAttack::UNLUCKY;
|
||||||
ObjectInstanceID ownerArmy = battle.getBattle()->getSideArmy(attacker->unitSide())->id;
|
|
||||||
if (gameHandler->randomizer->rollBadLuck(ownerArmy, -attackerLuck))
|
|
||||||
bat.flags |= BattleAttack::UNLUCKY;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (gameHandler->getRandomGenerator().nextInt(99) < attacker->valOfBonuses(BonusType::DOUBLE_DAMAGE_CHANCE))
|
if (gameHandler->randomizer->rollCombatAbility(ownerArmy, attacker->valOfBonuses(BonusType::DOUBLE_DAMAGE_CHANCE)))
|
||||||
{
|
|
||||||
bat.flags |= BattleAttack::DEATH_BLOW;
|
bat.flags |= BattleAttack::DEATH_BLOW;
|
||||||
}
|
|
||||||
|
|
||||||
const auto * owner = battle.battleGetFightingHero(attacker->unitSide());
|
const auto * owner = battle.battleGetFightingHero(attacker->unitSide());
|
||||||
if(owner)
|
if(owner)
|
||||||
{
|
{
|
||||||
int chance = owner->valOfBonuses(BonusType::BONUS_DAMAGE_CHANCE, BonusSubtypeID(attacker->creatureId()));
|
int chance = owner->valOfBonuses(BonusType::BONUS_DAMAGE_CHANCE, BonusSubtypeID(attacker->creatureId()));
|
||||||
if (chance > gameHandler->getRandomGenerator().nextInt(99))
|
if (gameHandler->randomizer->rollCombatAbility(ownerArmy, chance))
|
||||||
bat.flags |= BattleAttack::BALLISTA_DOUBLE_DMG;
|
bat.flags |= BattleAttack::BALLISTA_DOUBLE_DMG;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1120,6 +1111,8 @@ void BattleActionProcessor::makeAttack(const CBattleInfoCallback & battle, const
|
|||||||
|
|
||||||
void BattleActionProcessor::attackCasting(const CBattleInfoCallback & battle, bool ranged, BonusType attackMode, const battle::Unit * attacker, const CStack * defender)
|
void BattleActionProcessor::attackCasting(const CBattleInfoCallback & battle, bool ranged, BonusType attackMode, const battle::Unit * attacker, const CStack * defender)
|
||||||
{
|
{
|
||||||
|
ObjectInstanceID ownerArmy = battle.getBattle()->getSideArmy(attacker->unitSide())->id;
|
||||||
|
|
||||||
if(attacker->hasBonusOfType(attackMode))
|
if(attacker->hasBonusOfType(attackMode))
|
||||||
{
|
{
|
||||||
TConstBonusListPtr spells = attacker->getBonuses(Selector::type()(attackMode));
|
TConstBonusListPtr spells = attacker->getBonuses(Selector::type()(attackMode));
|
||||||
@ -1163,7 +1156,7 @@ void BattleActionProcessor::attackCasting(const CBattleInfoCallback & battle, bo
|
|||||||
continue;
|
continue;
|
||||||
|
|
||||||
//check if spell should be cast (probability handling)
|
//check if spell should be cast (probability handling)
|
||||||
if(gameHandler->getRandomGenerator().nextInt(99) >= chance)
|
if (!gameHandler->randomizer->rollCombatAbility(ownerArmy, chance))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
//casting
|
//casting
|
||||||
@ -1319,9 +1312,11 @@ void BattleActionProcessor::handleAfterAttackCasting(const CBattleInfoCallback &
|
|||||||
|
|
||||||
int64_t acidDamage = 0;
|
int64_t acidDamage = 0;
|
||||||
TConstBonusListPtr acidBreath = attacker->getBonuses(Selector::type()(BonusType::ACID_BREATH));
|
TConstBonusListPtr acidBreath = attacker->getBonuses(Selector::type()(BonusType::ACID_BREATH));
|
||||||
|
ObjectInstanceID ownerArmy = battle.getBattle()->getSideArmy(attacker->unitSide())->id;
|
||||||
|
|
||||||
for(const auto & b : *acidBreath)
|
for(const auto & b : *acidBreath)
|
||||||
{
|
{
|
||||||
if(b->additionalInfo[0] > gameHandler->getRandomGenerator().nextInt(99))
|
if (gameHandler->randomizer->rollCombatAbility(ownerArmy, b->additionalInfo[0]))
|
||||||
acidDamage += b->val;
|
acidDamage += b->val;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1345,10 +1340,9 @@ void BattleActionProcessor::handleAfterAttackCasting(const CBattleInfoCallback &
|
|||||||
|
|
||||||
if(attacker->hasBonusOfType(BonusType::TRANSMUTATION) && defender->isLiving()) //transmutation mechanics, similar to WoG werewolf ability
|
if(attacker->hasBonusOfType(BonusType::TRANSMUTATION) && defender->isLiving()) //transmutation mechanics, similar to WoG werewolf ability
|
||||||
{
|
{
|
||||||
double chanceToTrigger = attacker->valOfBonuses(BonusType::TRANSMUTATION) / 100.0f;
|
ObjectInstanceID ownerArmy = battle.getBattle()->getSideArmy(attacker->unitSide())->id;
|
||||||
vstd::amin(chanceToTrigger, 1); //cap at 100%
|
int chanceToTrigger = attacker->valOfBonuses(BonusType::TRANSMUTATION);
|
||||||
|
if (!gameHandler->randomizer->rollCombatAbility(ownerArmy, chanceToTrigger))
|
||||||
if(gameHandler->getRandomGenerator().nextDouble(0, 1) > chanceToTrigger)
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
int bonusAdditionalInfo = attacker->getBonus(Selector::type()(BonusType::TRANSMUTATION))->additionalInfo[0];
|
int bonusAdditionalInfo = attacker->getBonus(Selector::type()(BonusType::TRANSMUTATION))->additionalInfo[0];
|
||||||
@ -1395,24 +1389,23 @@ void BattleActionProcessor::handleAfterAttackCasting(const CBattleInfoCallback &
|
|||||||
|
|
||||||
if(attacker->hasBonusOfType(BonusType::DESTRUCTION, BonusCustomSubtype::destructionKillPercentage) || attacker->hasBonusOfType(BonusType::DESTRUCTION, BonusCustomSubtype::destructionKillAmount))
|
if(attacker->hasBonusOfType(BonusType::DESTRUCTION, BonusCustomSubtype::destructionKillPercentage) || attacker->hasBonusOfType(BonusType::DESTRUCTION, BonusCustomSubtype::destructionKillAmount))
|
||||||
{
|
{
|
||||||
double chanceToTrigger = 0;
|
int chanceToTrigger = 0;
|
||||||
int amountToDie = 0;
|
int amountToDie = 0;
|
||||||
|
|
||||||
if(attacker->hasBonusOfType(BonusType::DESTRUCTION, BonusCustomSubtype::destructionKillPercentage)) //killing by percentage
|
if(attacker->hasBonusOfType(BonusType::DESTRUCTION, BonusCustomSubtype::destructionKillPercentage)) //killing by percentage
|
||||||
{
|
{
|
||||||
chanceToTrigger = attacker->valOfBonuses(BonusType::DESTRUCTION, BonusCustomSubtype::destructionKillPercentage) / 100.0f;
|
chanceToTrigger = attacker->valOfBonuses(BonusType::DESTRUCTION, BonusCustomSubtype::destructionKillPercentage);
|
||||||
int percentageToDie = attacker->getBonus(Selector::type()(BonusType::DESTRUCTION).And(Selector::subtype()(BonusCustomSubtype::destructionKillPercentage)))->additionalInfo[0];
|
int percentageToDie = attacker->getBonus(Selector::type()(BonusType::DESTRUCTION).And(Selector::subtype()(BonusCustomSubtype::destructionKillPercentage)))->additionalInfo[0];
|
||||||
amountToDie = static_cast<int>(defender->getCount() * percentageToDie * 0.01f);
|
amountToDie = static_cast<int>(defender->getCount() * percentageToDie * 0.01f);
|
||||||
}
|
}
|
||||||
else if(attacker->hasBonusOfType(BonusType::DESTRUCTION, BonusCustomSubtype::destructionKillAmount)) //killing by count
|
else if(attacker->hasBonusOfType(BonusType::DESTRUCTION, BonusCustomSubtype::destructionKillAmount)) //killing by count
|
||||||
{
|
{
|
||||||
chanceToTrigger = attacker->valOfBonuses(BonusType::DESTRUCTION, BonusCustomSubtype::destructionKillAmount) / 100.0f;
|
chanceToTrigger = attacker->valOfBonuses(BonusType::DESTRUCTION, BonusCustomSubtype::destructionKillAmount);
|
||||||
amountToDie = attacker->getBonus(Selector::type()(BonusType::DESTRUCTION).And(Selector::subtype()(BonusCustomSubtype::destructionKillAmount)))->additionalInfo[0];
|
amountToDie = attacker->getBonus(Selector::type()(BonusType::DESTRUCTION).And(Selector::subtype()(BonusCustomSubtype::destructionKillAmount)))->additionalInfo[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
vstd::amin(chanceToTrigger, 1); //cap trigger chance at 100%
|
ObjectInstanceID ownerArmy = battle.getBattle()->getSideArmy(attacker->unitSide())->id;
|
||||||
|
if (!gameHandler->randomizer->rollCombatAbility(ownerArmy, chanceToTrigger))
|
||||||
if(gameHandler->getRandomGenerator().nextDouble(0, 1) > chanceToTrigger)
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
BattleStackAttacked bsa;
|
BattleStackAttacked bsa;
|
||||||
|
@ -390,7 +390,7 @@ bool BattleFlowProcessor::tryMakeAutomaticAction(const CBattleInfoCallback & bat
|
|||||||
const CreatureID stackCreatureId = next->unitType()->getId();
|
const CreatureID stackCreatureId = next->unitType()->getId();
|
||||||
|
|
||||||
if ((stackCreatureId == CreatureID::ARROW_TOWERS || stackCreatureId == CreatureID::BALLISTA)
|
if ((stackCreatureId == CreatureID::ARROW_TOWERS || stackCreatureId == CreatureID::BALLISTA)
|
||||||
&& (!curOwner || gameHandler->getRandomGenerator().nextInt(99) >= curOwner->valOfBonuses(BonusType::MANUAL_CONTROL, BonusSubtypeID(stackCreatureId))))
|
&& (!curOwner || !gameHandler->randomizer->rollCombatAbility(curOwner->id, curOwner->valOfBonuses(BonusType::MANUAL_CONTROL, BonusSubtypeID(stackCreatureId)))))
|
||||||
{
|
{
|
||||||
BattleAction attack;
|
BattleAction attack;
|
||||||
attack.actionType = EActionType::SHOOT;
|
attack.actionType = EActionType::SHOOT;
|
||||||
@ -462,7 +462,7 @@ bool BattleFlowProcessor::tryMakeAutomaticAction(const CBattleInfoCallback & bat
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!curOwner || gameHandler->getRandomGenerator().nextInt(99) >= curOwner->valOfBonuses(BonusType::MANUAL_CONTROL, BonusSubtypeID(CreatureID(CreatureID::CATAPULT))))
|
if (!curOwner || !gameHandler->randomizer->rollCombatAbility(curOwner->id, curOwner->valOfBonuses(BonusType::MANUAL_CONTROL, BonusSubtypeID(CreatureID(CreatureID::CATAPULT)))))
|
||||||
{
|
{
|
||||||
BattleAction attack;
|
BattleAction attack;
|
||||||
attack.actionType = EActionType::CATAPULT;
|
attack.actionType = EActionType::CATAPULT;
|
||||||
@ -487,7 +487,7 @@ bool BattleFlowProcessor::tryMakeAutomaticAction(const CBattleInfoCallback & bat
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!curOwner || gameHandler->getRandomGenerator().nextInt(99) >= curOwner->valOfBonuses(BonusType::MANUAL_CONTROL, BonusSubtypeID(CreatureID(CreatureID::FIRST_AID_TENT))))
|
if (!curOwner || !gameHandler->randomizer->rollCombatAbility(curOwner->id, curOwner->valOfBonuses(BonusType::MANUAL_CONTROL, BonusSubtypeID(CreatureID(CreatureID::FIRST_AID_TENT)))))
|
||||||
{
|
{
|
||||||
RandomGeneratorUtil::randomShuffle(possibleStacks, gameHandler->getRandomGenerator());
|
RandomGeneratorUtil::randomShuffle(possibleStacks, gameHandler->getRandomGenerator());
|
||||||
const CStack * toBeHealed = possibleStacks.front();
|
const CStack * toBeHealed = possibleStacks.front();
|
||||||
@ -738,6 +738,7 @@ void BattleFlowProcessor::stackTurnTrigger(const CBattleInfoCallback & battle, c
|
|||||||
}
|
}
|
||||||
if (st->isLiving() && !st->hasBonusOfType(BonusType::FEARLESS))
|
if (st->isLiving() && !st->hasBonusOfType(BonusType::FEARLESS))
|
||||||
{
|
{
|
||||||
|
ObjectInstanceID opponentArmyID = battle.battleGetArmyObject(battle.otherSide(st->unitSide()))->id;
|
||||||
bool fearsomeCreature = false;
|
bool fearsomeCreature = false;
|
||||||
for (const CStack * stack : battle.battleGetAllStacks(true))
|
for (const CStack * stack : battle.battleGetAllStacks(true))
|
||||||
{
|
{
|
||||||
@ -749,7 +750,7 @@ void BattleFlowProcessor::stackTurnTrigger(const CBattleInfoCallback & battle, c
|
|||||||
}
|
}
|
||||||
if (fearsomeCreature)
|
if (fearsomeCreature)
|
||||||
{
|
{
|
||||||
if (gameHandler->getRandomGenerator().nextInt(99) < 10) //fixed 10%
|
if (gameHandler->randomizer->rollCombatAbility(opponentArmyID, 10)) //fixed 10%
|
||||||
{
|
{
|
||||||
bte.effect = vstd::to_underlying(BonusType::FEAR);
|
bte.effect = vstd::to_underlying(BonusType::FEAR);
|
||||||
gameHandler->sendAndApply(bte);
|
gameHandler->sendAndApply(bte);
|
||||||
|
Reference in New Issue
Block a user