1
0
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:
Ivan Savenko
2025-05-18 22:12:39 +03:00
parent 87323f08d9
commit 157b4fea74
4 changed files with 45 additions and 42 deletions

View File

@ -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)
//{ //{
// //

View File

@ -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;

View File

@ -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;

View File

@ -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);