1
0
mirror of https://github.com/vcmi/vcmi.git synced 2025-08-10 22:31:40 +02:00

Merge pull request #5565 from kdmcser/new_morale_luck

support setting dice molecule for rolling morale and luck
This commit is contained in:
Ivan Savenko
2025-03-26 16:53:13 +02:00
committed by GitHub
11 changed files with 67 additions and 54 deletions

View File

@@ -232,18 +232,19 @@ std::vector<SlotInfo> ArmyManager::getBestArmy(const IBonusBearer * armyCarrier,
auto morale = slot.second->moraleVal(); auto morale = slot.second->moraleVal();
auto multiplier = 1.0f; auto multiplier = 1.0f;
const auto & badMoraleDice = cb->getSettings().getVector(EGameSettings::COMBAT_BAD_MORALE_DICE); const auto & badMoraleChance = cb->getSettings().getVector(EGameSettings::COMBAT_BAD_MORALE_CHANCE);
const auto & highMoraleDice = cb->getSettings().getVector(EGameSettings::COMBAT_GOOD_MORALE_DICE); const auto & highMoraleChance = cb->getSettings().getVector(EGameSettings::COMBAT_GOOD_MORALE_CHANCE);
int moraleDiceSize = cb->getSettings().getInteger(EGameSettings::COMBAT_MORALE_DICE_SIZE);
if(morale < 0 && !badMoraleDice.empty()) if(morale < 0 && !badMoraleChance.empty())
{ {
size_t diceIndex = std::min<size_t>(badMoraleDice.size(), -morale) - 1; size_t chanceIndex = std::min<size_t>(badMoraleChance.size(), -morale) - 1;
multiplier -= 1.0 / badMoraleDice.at(diceIndex); multiplier -= 1.0 / moraleDiceSize * badMoraleChance.at(chanceIndex);
} }
else if(morale > 0 && !highMoraleDice.empty()) else if(morale > 0 && !highMoraleChance.empty())
{ {
size_t diceIndex = std::min<size_t>(highMoraleDice.size(), morale) - 1; size_t chanceIndex = std::min<size_t>(highMoraleChance.size(), morale) - 1;
multiplier += 1.0 / highMoraleDice.at(diceIndex); multiplier += 1.0 / moraleDiceSize * highMoraleChance.at(chanceIndex);
} }
newValue += multiplier * slot.second->getPower(); newValue += multiplier * slot.second->getPower();

View File

@@ -586,8 +586,8 @@ void HeroInfoBasicPanel::initializeData(const InfoAboutHero & hero)
labels.push_back(std::make_shared<CLabel>(9, 131, EFonts::FONT_TINY, ETextAlignment::TOPLEFT, Colors::WHITE, LIBRARY->generaltexth->allTexts[384] + ":")); labels.push_back(std::make_shared<CLabel>(9, 131, EFonts::FONT_TINY, ETextAlignment::TOPLEFT, Colors::WHITE, LIBRARY->generaltexth->allTexts[384] + ":"));
labels.push_back(std::make_shared<CLabel>(9, 143, EFonts::FONT_TINY, ETextAlignment::TOPLEFT, Colors::WHITE, LIBRARY->generaltexth->allTexts[385] + ":")); labels.push_back(std::make_shared<CLabel>(9, 143, EFonts::FONT_TINY, ETextAlignment::TOPLEFT, Colors::WHITE, LIBRARY->generaltexth->allTexts[385] + ":"));
icons.push_back(std::make_shared<CAnimImage>(AnimationPath::builtin("IMRL22"), morale + 3, 0, 47, 131)); icons.push_back(std::make_shared<CAnimImage>(AnimationPath::builtin("IMRL22"), std::clamp(morale + 3, 0, 6), 0, 47, 131));
icons.push_back(std::make_shared<CAnimImage>(AnimationPath::builtin("ILCK22"), luck + 3, 0, 47, 143)); icons.push_back(std::make_shared<CAnimImage>(AnimationPath::builtin("ILCK22"), std::clamp(luck + 3, 0, 6), 0, 47, 143));
//spell points //spell points
labels.push_back(std::make_shared<CLabel>(39, 174, EFonts::FONT_TINY, ETextAlignment::CENTER, Colors::WHITE, LIBRARY->generaltexth->allTexts[387])); labels.push_back(std::make_shared<CLabel>(39, 174, EFonts::FONT_TINY, ETextAlignment::CENTER, Colors::WHITE, LIBRARY->generaltexth->allTexts[387]));
@@ -658,8 +658,8 @@ void StackInfoBasicPanel::initializeData(const CStack * stack)
labels.push_back(std::make_shared<CLabel>(9, 131, EFonts::FONT_TINY, ETextAlignment::TOPLEFT, Colors::WHITE, LIBRARY->generaltexth->allTexts[384] + ":")); labels.push_back(std::make_shared<CLabel>(9, 131, EFonts::FONT_TINY, ETextAlignment::TOPLEFT, Colors::WHITE, LIBRARY->generaltexth->allTexts[384] + ":"));
labels.push_back(std::make_shared<CLabel>(9, 143, EFonts::FONT_TINY, ETextAlignment::TOPLEFT, Colors::WHITE, LIBRARY->generaltexth->allTexts[385] + ":")); labels.push_back(std::make_shared<CLabel>(9, 143, EFonts::FONT_TINY, ETextAlignment::TOPLEFT, Colors::WHITE, LIBRARY->generaltexth->allTexts[385] + ":"));
icons.push_back(std::make_shared<CAnimImage>(AnimationPath::builtin("IMRL22"), morale + 3, 0, 47, 131)); icons.push_back(std::make_shared<CAnimImage>(AnimationPath::builtin("IMRL22"), std::clamp(morale + 3, 0, 6), 0, 47, 131));
icons.push_back(std::make_shared<CAnimImage>(AnimationPath::builtin("ILCK22"), luck + 3, 0, 47, 143)); icons.push_back(std::make_shared<CAnimImage>(AnimationPath::builtin("ILCK22"), std::clamp(luck + 3, 0, 6), 0, 47, 143));
//extra information //extra information
labels.push_back(std::make_shared<CLabel>(9, 168, EFonts::FONT_TINY, ETextAlignment::TOPLEFT, Colors::WHITE, LIBRARY->generaltexth->translate("vcmi.battleWindow.killed") + ":")); labels.push_back(std::make_shared<CLabel>(9, 168, EFonts::FONT_TINY, ETextAlignment::TOPLEFT, Colors::WHITE, LIBRARY->generaltexth->translate("vcmi.battleWindow.killed") + ":"));

View File

@@ -195,9 +195,9 @@ size_t CComponent::getIndex() const
case ComponentType::SPELL: case ComponentType::SPELL:
return (size < large) ? data.subType.getNum() + 1 : data.subType.getNum(); return (size < large) ? data.subType.getNum() + 1 : data.subType.getNum();
case ComponentType::MORALE: case ComponentType::MORALE:
return data.value.value_or(0) + 3; return std::clamp(data.value.value_or(0) + 3, 0, 6);
case ComponentType::LUCK: case ComponentType::LUCK:
return data.value.value_or(0) + 3; return std::clamp(data.value.value_or(0) + 3, 0, 6);
case ComponentType::BUILDING: case ComponentType::BUILDING:
return data.subType.as<BuildingTypeUniqueID>().getBuilding(); return data.subType.as<BuildingTypeUniqueID>().getBuilding();
case ComponentType::HERO_PORTRAIT: case ComponentType::HERO_PORTRAIT:

View File

@@ -333,8 +333,8 @@ void CHeroTooltip::init(const InfoAboutHero & hero)
labels.push_back(std::make_shared<CLabel>(158, 98, FONT_TINY, ETextAlignment::CENTER, Colors::WHITE, std::to_string(hero.details->mana))); labels.push_back(std::make_shared<CLabel>(158, 98, FONT_TINY, ETextAlignment::CENTER, Colors::WHITE, std::to_string(hero.details->mana)));
morale = std::make_shared<CAnimImage>(AnimationPath::builtin("IMRL22"), hero.details->morale + 3, 0, 5, 74); morale = std::make_shared<CAnimImage>(AnimationPath::builtin("IMRL22"), std::clamp(hero.details->morale + 3, 0 , 6), 0, 5, 74);
luck = std::make_shared<CAnimImage>(AnimationPath::builtin("ILCK22"), hero.details->luck + 3, 0, 5, 91); luck = std::make_shared<CAnimImage>(AnimationPath::builtin("ILCK22"), std::clamp(hero.details->luck + 3, 0, 6), 0, 5, 91);
} }
} }
@@ -372,8 +372,8 @@ void CInteractableHeroTooltip::init(const InfoAboutHero & hero)
labels.push_back(std::make_shared<CLabel>(158, 98, FONT_TINY, ETextAlignment::CENTER, Colors::WHITE, std::to_string(hero.details->mana))); labels.push_back(std::make_shared<CLabel>(158, 98, FONT_TINY, ETextAlignment::CENTER, Colors::WHITE, std::to_string(hero.details->mana)));
morale = std::make_shared<CAnimImage>(AnimationPath::builtin("IMRL22"), hero.details->morale + 3, 0, 5, 74); morale = std::make_shared<CAnimImage>(AnimationPath::builtin("IMRL22"), std::clamp(hero.details->morale + 3, 0 ,6), 0, 5, 74);
luck = std::make_shared<CAnimImage>(AnimationPath::builtin("ILCK22"), hero.details->luck + 3, 0, 5, 91); luck = std::make_shared<CAnimImage>(AnimationPath::builtin("ILCK22"), std::clamp(hero.details->luck + 3, 0, 6), 0, 5, 91);
} }
} }
@@ -623,7 +623,7 @@ void MoraleLuckBox::set(const AFactionMember * node)
else else
imageName = morale ? "IMRL42" : "ILCK42"; imageName = morale ? "IMRL42" : "ILCK42";
image = std::make_shared<CAnimImage>(AnimationPath::builtin(imageName), *component.value + 3); image = std::make_shared<CAnimImage>(AnimationPath::builtin(imageName), std::clamp(*component.value + 3, 0, 6));
image->moveBy(Point(pos.w/2 - image->pos.w/2, pos.h/2 - image->pos.h/2)); //center icon image->moveBy(Point(pos.w/2 - image->pos.w/2, pos.h/2 - image->pos.h/2)); //center icon
if(settings["general"]["enableUiEnhancements"].Bool()) if(settings["general"]["enableUiEnhancements"].Bool())
label = std::make_shared<CLabel>((image->pos.topLeft() - pos.topLeft()).x + (small ? 28 : 40), (image->pos.topLeft() - pos.topLeft()).y + (small ? 20 : 38), EFonts::FONT_TINY, ETextAlignment::BOTTOMRIGHT, Colors::WHITE, std::to_string(modifierList->totalValue())); label = std::make_shared<CLabel>((image->pos.topLeft() - pos.topLeft()).x + (small ? 28 : 40), (image->pos.topLeft() - pos.topLeft()).y + (small ? 20 : 38), EFonts::FONT_TINY, ETextAlignment::BOTTOMRIGHT, Colors::WHITE, std::to_string(modifierList->totalValue()));

View File

@@ -340,14 +340,16 @@
"combat": "combat":
{ {
// defines dice size of a morale roll, based on creature's morale. // defines dice chance and dice size of a morale roll, based on creature's morale.
// Resulting chance is 1/(value). If list contains 0 values, option will be disabled // Resulting chance is chanceValue / diceSize. If list contains 0 values, option will be disabled
"goodMoraleDice" : [ 24, 12, 8 ], "goodMoraleChance" : [ 1, 2, 3 ],
"badMoraleDice" : [ 12, 6, 4], "badMoraleChance" : [ 2, 4, 6],
"moraleDiceSize" : 24,
// defines dice size of a luck roll, based on creature's luck // defines dice chance and dice size of a luck roll, based on creature's luck
"goodLuckDice" : [ 24, 12, 8 ], "goodLuckChance" : [ 1, 2, 3 ],
"badLuckDice" : [], "badLuckChance" : [],
"luckDiceSize" : 24,
// every 1 attack point damage influence in battle when attack points > defense points during creature attack // every 1 attack point damage influence in battle when attack points > defense points during creature attack
"attackPointDamageFactor": 0.05, "attackPointDamageFactor": 0.05,

View File

@@ -66,10 +66,12 @@
"type" : "object", "type" : "object",
"additionalProperties" : false, "additionalProperties" : false,
"properties" : { "properties" : {
"goodMoraleDice" : { "type" : "array" }, "goodMoraleChance" : { "type" : "array" },
"badMoraleDice" : { "type" : "array" }, "badMoraleChance" : { "type" : "array" },
"goodLuckDice" : { "type" : "array" }, "moraleDiceSize" : { "type" : "number" },
"badLuckDice" : { "type" : "array" }, "goodLuckChance" : { "type" : "array" },
"badLuckChance" : { "type" : "array" },
"luckDiceSize" : { "type" : "number" },
"backpackSize" : { "type" : "number" }, "backpackSize" : { "type" : "number" },
"attackPointDamageFactor" : { "type" : "number" }, "attackPointDamageFactor" : { "type" : "number" },
"attackPointDamageFactorCap" : { "type" : "number" }, "attackPointDamageFactorCap" : { "type" : "number" },

View File

@@ -71,8 +71,8 @@ int AFactionMember::getMaxDamage(bool ranged) const
int AFactionMember::moraleValAndBonusList(TConstBonusListPtr & bonusList) const int AFactionMember::moraleValAndBonusList(TConstBonusListPtr & bonusList) const
{ {
int32_t maxGoodMorale = LIBRARY->engineSettings()->getVector(EGameSettings::COMBAT_GOOD_MORALE_DICE).size(); int32_t maxGoodMorale = LIBRARY->engineSettings()->getVector(EGameSettings::COMBAT_GOOD_MORALE_CHANCE).size();
int32_t maxBadMorale = - (int32_t) LIBRARY->engineSettings()->getVector(EGameSettings::COMBAT_BAD_MORALE_DICE).size(); int32_t maxBadMorale = - (int32_t) LIBRARY->engineSettings()->getVector(EGameSettings::COMBAT_BAD_MORALE_CHANCE).size();
if(getBonusBearer()->hasBonusOfType(BonusType::MAX_MORALE)) if(getBonusBearer()->hasBonusOfType(BonusType::MAX_MORALE))
{ {
@@ -100,8 +100,8 @@ int AFactionMember::moraleValAndBonusList(TConstBonusListPtr & bonusList) const
int AFactionMember::luckValAndBonusList(TConstBonusListPtr & bonusList) const int AFactionMember::luckValAndBonusList(TConstBonusListPtr & bonusList) const
{ {
int32_t maxGoodLuck = LIBRARY->engineSettings()->getVector(EGameSettings::COMBAT_GOOD_LUCK_DICE).size(); int32_t maxGoodLuck = LIBRARY->engineSettings()->getVector(EGameSettings::COMBAT_GOOD_LUCK_CHANCE).size();
int32_t maxBadLuck = - (int32_t) LIBRARY->engineSettings()->getVector(EGameSettings::COMBAT_BAD_LUCK_DICE).size(); int32_t maxBadLuck = - (int32_t) LIBRARY->engineSettings()->getVector(EGameSettings::COMBAT_BAD_LUCK_CHANCE).size();
if(getBonusBearer()->hasBonusOfType(BonusType::MAX_LUCK)) if(getBonusBearer()->hasBonusOfType(BonusType::MAX_LUCK))
{ {

View File

@@ -48,12 +48,14 @@ const std::vector<GameSettings::SettingOption> GameSettings::settingProperties =
{EGameSettings::COMBAT_AREA_SHOT_CAN_TARGET_EMPTY_HEX, "combat", "areaShotCanTargetEmptyHex" }, {EGameSettings::COMBAT_AREA_SHOT_CAN_TARGET_EMPTY_HEX, "combat", "areaShotCanTargetEmptyHex" },
{EGameSettings::COMBAT_ATTACK_POINT_DAMAGE_FACTOR, "combat", "attackPointDamageFactor" }, {EGameSettings::COMBAT_ATTACK_POINT_DAMAGE_FACTOR, "combat", "attackPointDamageFactor" },
{EGameSettings::COMBAT_ATTACK_POINT_DAMAGE_FACTOR_CAP, "combat", "attackPointDamageFactorCap" }, {EGameSettings::COMBAT_ATTACK_POINT_DAMAGE_FACTOR_CAP, "combat", "attackPointDamageFactorCap" },
{EGameSettings::COMBAT_BAD_LUCK_DICE, "combat", "badLuckDice" },
{EGameSettings::COMBAT_BAD_MORALE_DICE, "combat", "badMoraleDice" },
{EGameSettings::COMBAT_DEFENSE_POINT_DAMAGE_FACTOR, "combat", "defensePointDamageFactor" }, {EGameSettings::COMBAT_DEFENSE_POINT_DAMAGE_FACTOR, "combat", "defensePointDamageFactor" },
{EGameSettings::COMBAT_DEFENSE_POINT_DAMAGE_FACTOR_CAP, "combat", "defensePointDamageFactorCap" }, {EGameSettings::COMBAT_DEFENSE_POINT_DAMAGE_FACTOR_CAP, "combat", "defensePointDamageFactorCap" },
{EGameSettings::COMBAT_GOOD_LUCK_DICE, "combat", "goodLuckDice" }, {EGameSettings::COMBAT_GOOD_MORALE_CHANCE, "combat", "goodMoraleChance" },
{EGameSettings::COMBAT_GOOD_MORALE_DICE, "combat", "goodMoraleDice" }, {EGameSettings::COMBAT_BAD_MORALE_CHANCE, "combat", "badMoraleChance" },
{EGameSettings::COMBAT_MORALE_DICE_SIZE, "combat", "moraleDiceSize" },
{EGameSettings::COMBAT_GOOD_LUCK_CHANCE, "combat", "goodLuckChance" },
{EGameSettings::COMBAT_BAD_LUCK_CHANCE, "combat", "badLuckChance" },
{EGameSettings::COMBAT_LUCK_DICE_SIZE, "combat", "luckDiceSize" },
{EGameSettings::COMBAT_LAYOUTS, "combat", "layouts" }, {EGameSettings::COMBAT_LAYOUTS, "combat", "layouts" },
{EGameSettings::COMBAT_ONE_HEX_TRIGGERS_OBSTACLES, "combat", "oneHexTriggersObstacles" }, {EGameSettings::COMBAT_ONE_HEX_TRIGGERS_OBSTACLES, "combat", "oneHexTriggersObstacles" },
{EGameSettings::CREATURES_ALLOW_ALL_FOR_DOUBLE_MONTH, "creatures", "allowAllForDoubleMonth" }, {EGameSettings::CREATURES_ALLOW_ALL_FOR_DOUBLE_MONTH, "creatures", "allowAllForDoubleMonth" },

View File

@@ -21,12 +21,14 @@ enum class EGameSettings
COMBAT_AREA_SHOT_CAN_TARGET_EMPTY_HEX, COMBAT_AREA_SHOT_CAN_TARGET_EMPTY_HEX,
COMBAT_ATTACK_POINT_DAMAGE_FACTOR, COMBAT_ATTACK_POINT_DAMAGE_FACTOR,
COMBAT_ATTACK_POINT_DAMAGE_FACTOR_CAP, COMBAT_ATTACK_POINT_DAMAGE_FACTOR_CAP,
COMBAT_BAD_LUCK_DICE,
COMBAT_BAD_MORALE_DICE,
COMBAT_DEFENSE_POINT_DAMAGE_FACTOR, COMBAT_DEFENSE_POINT_DAMAGE_FACTOR,
COMBAT_DEFENSE_POINT_DAMAGE_FACTOR_CAP, COMBAT_DEFENSE_POINT_DAMAGE_FACTOR_CAP,
COMBAT_GOOD_LUCK_DICE, COMBAT_GOOD_MORALE_CHANCE,
COMBAT_GOOD_MORALE_DICE, COMBAT_BAD_MORALE_CHANCE,
COMBAT_MORALE_DICE_SIZE,
COMBAT_GOOD_LUCK_CHANCE,
COMBAT_BAD_LUCK_CHANCE,
COMBAT_LUCK_DICE_SIZE,
COMBAT_LAYOUTS, COMBAT_LAYOUTS,
COMBAT_ONE_HEX_TRIGGERS_OBSTACLES, COMBAT_ONE_HEX_TRIGGERS_OBSTACLES,
CREATURES_ALLOW_ALL_FOR_DOUBLE_MONTH, CREATURES_ALLOW_ALL_FOR_DOUBLE_MONTH,

View File

@@ -940,19 +940,21 @@ void BattleActionProcessor::makeAttack(const CBattleInfoCallback & battle, const
if(attackerLuck > 0) if(attackerLuck > 0)
{ {
auto diceSize = gameHandler->getSettings().getVector(EGameSettings::COMBAT_GOOD_LUCK_DICE); auto goodLuckChanceVector = gameHandler->getSettings().getVector(EGameSettings::COMBAT_GOOD_LUCK_CHANCE);
size_t diceIndex = std::min<size_t>(diceSize.size(), attackerLuck) - 1; // array index, so 0-indexed int luckDiceSize = gameHandler->getSettings().getInteger(EGameSettings::COMBAT_LUCK_DICE_SIZE);
size_t chanceIndex = std::min<size_t>(goodLuckChanceVector.size(), attackerLuck) - 1; // array index, so 0-indexed
if(diceSize.size() > 0 && gameHandler->getRandomGenerator().nextInt(1, diceSize[diceIndex]) == 1) if(goodLuckChanceVector.size() > 0 && gameHandler->getRandomGenerator().nextInt(1, luckDiceSize) <= goodLuckChanceVector[chanceIndex])
bat.flags |= BattleAttack::LUCKY; bat.flags |= BattleAttack::LUCKY;
} }
if(attackerLuck < 0) if(attackerLuck < 0)
{ {
auto diceSize = gameHandler->getSettings().getVector(EGameSettings::COMBAT_BAD_LUCK_DICE); auto badLuckChanceVector = gameHandler->getSettings().getVector(EGameSettings::COMBAT_BAD_LUCK_CHANCE);
size_t diceIndex = std::min<size_t>(diceSize.size(), -attackerLuck) - 1; // array index, so 0-indexed int luckDiceSize = gameHandler->getSettings().getInteger(EGameSettings::COMBAT_LUCK_DICE_SIZE);
size_t chanceIndex = std::min<size_t>(badLuckChanceVector.size(), -attackerLuck) - 1; // array index, so 0-indexed
if(diceSize.size() > 0 && gameHandler->getRandomGenerator().nextInt(1, diceSize[diceIndex]) == 1) if(badLuckChanceVector.size() > 0 && gameHandler->getRandomGenerator().nextInt(1, luckDiceSize) <= badLuckChanceVector[chanceIndex])
bat.flags |= BattleAttack::UNLUCKY; bat.flags |= BattleAttack::UNLUCKY;
} }

View File

@@ -347,10 +347,11 @@ bool BattleFlowProcessor::tryMakeAutomaticAction(const CBattleInfoCallback & bat
int nextStackMorale = next->moraleVal(); int nextStackMorale = next->moraleVal();
if(!next->hadMorale && !next->waited() && nextStackMorale < 0) if(!next->hadMorale && !next->waited() && nextStackMorale < 0)
{ {
auto diceSize = gameHandler->getSettings().getVector(EGameSettings::COMBAT_BAD_MORALE_DICE); auto badMoraleChanceVector = gameHandler->getSettings().getVector(EGameSettings::COMBAT_BAD_MORALE_CHANCE);
size_t diceIndex = std::min<size_t>(diceSize.size(), -nextStackMorale) - 1; // array index, so 0-indexed int moraleDiceSize = gameHandler->getSettings().getInteger(EGameSettings::COMBAT_MORALE_DICE_SIZE);
size_t chanceIndex = std::min<size_t>(badMoraleChanceVector.size(), -nextStackMorale) - 1; // array index, so 0-indexed
if(diceSize.size() > 0 && gameHandler->getRandomGenerator().nextInt(1, diceSize[diceIndex]) == 1) if(badMoraleChanceVector.size() > 0 && gameHandler->getRandomGenerator().nextInt(1, moraleDiceSize) <= badMoraleChanceVector[chanceIndex])
{ {
//unit loses its turn - empty freeze action //unit loses its turn - empty freeze action
BattleAction ba; BattleAction ba;
@@ -526,10 +527,11 @@ bool BattleFlowProcessor::rollGoodMorale(const CBattleInfoCallback & battle, con
&& next->canMove() && next->canMove()
&& nextStackMorale > 0) && nextStackMorale > 0)
{ {
auto diceSize = gameHandler->getSettings().getVector(EGameSettings::COMBAT_GOOD_MORALE_DICE); auto goodMoraleChanceVector = gameHandler->getSettings().getVector(EGameSettings::COMBAT_GOOD_MORALE_CHANCE);
size_t diceIndex = std::min<size_t>(diceSize.size(), nextStackMorale) - 1; // array index, so 0-indexed int moraleDiceSize = gameHandler->getSettings().getInteger(EGameSettings::COMBAT_MORALE_DICE_SIZE);
size_t chanceIndex = std::min<size_t>(goodMoraleChanceVector.size(), nextStackMorale) - 1; // array index, so 0-indexed
if(diceSize.size() > 0 && gameHandler->getRandomGenerator().nextInt(1, diceSize[diceIndex]) == 1) if(goodMoraleChanceVector.size() > 0 && gameHandler->getRandomGenerator().nextInt(1, moraleDiceSize) <= goodMoraleChanceVector[chanceIndex])
{ {
BattleTriggerEffect bte; BattleTriggerEffect bte;
bte.battleID = battle.getBattle()->getBattleID(); bte.battleID = battle.getBattle()->getBattleID();