mirror of
https://github.com/vcmi/vcmi.git
synced 2025-11-25 22:42:04 +02:00
Fix serialization, add new game settings
This commit is contained in:
@@ -340,16 +340,32 @@
|
|||||||
|
|
||||||
"combat":
|
"combat":
|
||||||
{
|
{
|
||||||
|
// defines bias used for percentage-based ability rolls, such Death Blow of Dread Knight
|
||||||
|
// If bias is set to 0, then all rolls will be completely independent - it is possible to get lucky and roll ability with 10% chance
|
||||||
|
// multiple times in a row, or be unlucky and not roll 50% ability multiple times in a row
|
||||||
|
// If bias is non-zero, game will adjust probability based on previous rolls while keeping average chance at desired value
|
||||||
|
// So matter what value is used for bias, actual probability for large (1000+) number of rolls is same as stated in description
|
||||||
|
// However, non-zero bias allows to prevent long streaks of "bad" rolls, and enforce actual probabilities even for small (10-20) number of rolls
|
||||||
|
// Recommended value is ~10-25. Excessively large values, like 100 can make rolls very predictable, for example rolling 20% ability every 5th roll
|
||||||
|
"abilityBias" : 25
|
||||||
|
|
||||||
// defines dice chance and 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 chanceValue / diceSize. If list contains 0 values, option will be disabled
|
// Resulting chance is chanceValue / diceSize. If list contains 0 values, option will be disabled
|
||||||
"goodMoraleChance" : [ 1, 2, 3 ],
|
"goodMoraleChance" : [ 1, 2, 3 ],
|
||||||
"badMoraleChance" : [ 2, 4, 6],
|
"badMoraleChance" : [ 2, 4, 6],
|
||||||
"moraleDiceSize" : 24,
|
"moraleDiceSize" : 24,
|
||||||
|
// Bias for morale rolls. See abilityBias for detailed description
|
||||||
|
// Recommended value is around moraleDiceSize / 4
|
||||||
|
"moraleBias" : 8
|
||||||
|
|
||||||
// defines dice chance and 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
|
||||||
"goodLuckChance" : [ 1, 2, 3 ],
|
"goodLuckChance" : [ 1, 2, 3 ],
|
||||||
"badLuckChance" : [],
|
"badLuckChance" : [],
|
||||||
"luckDiceSize" : 24,
|
"luckDiceSize" : 24,
|
||||||
|
// Bias for luck rolls. See abilityBias for detailed description
|
||||||
|
// Recommended value is around luckDiceSize / 4
|
||||||
|
"luckBias" : 8
|
||||||
|
|
||||||
|
|
||||||
// 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,
|
||||||
|
|||||||
@@ -45,6 +45,7 @@ const std::vector<GameSettings::SettingOption> GameSettings::settingProperties =
|
|||||||
{EGameSettings::BANKS_SHOW_GUARDS_COMPOSITION, "banks", "showGuardsComposition" },
|
{EGameSettings::BANKS_SHOW_GUARDS_COMPOSITION, "banks", "showGuardsComposition" },
|
||||||
{EGameSettings::BONUSES_GLOBAL, "bonuses", "global" },
|
{EGameSettings::BONUSES_GLOBAL, "bonuses", "global" },
|
||||||
{EGameSettings::BONUSES_PER_HERO, "bonuses", "perHero" },
|
{EGameSettings::BONUSES_PER_HERO, "bonuses", "perHero" },
|
||||||
|
{EGameSettings::COMBAT_ABILITY_BIAS, "combat", "abilityBias" },
|
||||||
{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" },
|
||||||
@@ -53,9 +54,11 @@ const std::vector<GameSettings::SettingOption> GameSettings::settingProperties =
|
|||||||
{EGameSettings::COMBAT_GOOD_MORALE_CHANCE, "combat", "goodMoraleChance" },
|
{EGameSettings::COMBAT_GOOD_MORALE_CHANCE, "combat", "goodMoraleChance" },
|
||||||
{EGameSettings::COMBAT_BAD_MORALE_CHANCE, "combat", "badMoraleChance" },
|
{EGameSettings::COMBAT_BAD_MORALE_CHANCE, "combat", "badMoraleChance" },
|
||||||
{EGameSettings::COMBAT_MORALE_DICE_SIZE, "combat", "moraleDiceSize" },
|
{EGameSettings::COMBAT_MORALE_DICE_SIZE, "combat", "moraleDiceSize" },
|
||||||
|
{EGameSettings::COMBAT_MORALE_BIAS, "combat", "moraleBias" },
|
||||||
{EGameSettings::COMBAT_GOOD_LUCK_CHANCE, "combat", "goodLuckChance" },
|
{EGameSettings::COMBAT_GOOD_LUCK_CHANCE, "combat", "goodLuckChance" },
|
||||||
{EGameSettings::COMBAT_BAD_LUCK_CHANCE, "combat", "badLuckChance" },
|
{EGameSettings::COMBAT_BAD_LUCK_CHANCE, "combat", "badLuckChance" },
|
||||||
{EGameSettings::COMBAT_LUCK_DICE_SIZE, "combat", "luckDiceSize" },
|
{EGameSettings::COMBAT_LUCK_DICE_SIZE, "combat", "luckDiceSize" },
|
||||||
|
{EGameSettings::COMBAT_LUCK_BIAS, "combat", "luckBias" },
|
||||||
{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" },
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ enum class EGameSettings
|
|||||||
BANKS_SHOW_GUARDS_COMPOSITION,
|
BANKS_SHOW_GUARDS_COMPOSITION,
|
||||||
BONUSES_GLOBAL,
|
BONUSES_GLOBAL,
|
||||||
BONUSES_PER_HERO,
|
BONUSES_PER_HERO,
|
||||||
|
COMBAT_ABILITY_BIAS,
|
||||||
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,
|
||||||
@@ -26,9 +27,11 @@ enum class EGameSettings
|
|||||||
COMBAT_GOOD_MORALE_CHANCE,
|
COMBAT_GOOD_MORALE_CHANCE,
|
||||||
COMBAT_BAD_MORALE_CHANCE,
|
COMBAT_BAD_MORALE_CHANCE,
|
||||||
COMBAT_MORALE_DICE_SIZE,
|
COMBAT_MORALE_DICE_SIZE,
|
||||||
|
COMBAT_MORALE_BIAS,
|
||||||
COMBAT_GOOD_LUCK_CHANCE,
|
COMBAT_GOOD_LUCK_CHANCE,
|
||||||
COMBAT_BAD_LUCK_CHANCE,
|
COMBAT_BAD_LUCK_CHANCE,
|
||||||
COMBAT_LUCK_DICE_SIZE,
|
COMBAT_LUCK_DICE_SIZE,
|
||||||
|
COMBAT_LUCK_BIAS,
|
||||||
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,
|
||||||
|
|||||||
@@ -61,11 +61,12 @@ GameRandomizer::GameRandomizer(const IGameInfoCallback & gameInfo)
|
|||||||
GameRandomizer::~GameRandomizer() = default;
|
GameRandomizer::~GameRandomizer() = default;
|
||||||
|
|
||||||
|
|
||||||
bool GameRandomizer::rollMoraleLuck(std::map<ObjectInstanceID, RandomGeneratorWithBias> & seeds, ObjectInstanceID actor, int moraleLuckValue, EGameSettings diceSize, EGameSettings diceWeights)
|
bool GameRandomizer::rollMoraleLuck(std::map<ObjectInstanceID, RandomGeneratorWithBias> & seeds, ObjectInstanceID actor, int moraleLuckValue, EGameSettings biasValueSetting, EGameSettings diceSize, EGameSettings diceWeights)
|
||||||
{
|
{
|
||||||
assert(moraleLuckValue > 0);
|
assert(moraleLuckValue > 0);
|
||||||
auto goodLuckChanceVector = gameInfo.getSettings().getVector(diceWeights);
|
auto goodLuckChanceVector = gameInfo.getSettings().getVector(diceWeights);
|
||||||
int luckDiceSize = gameInfo.getSettings().getInteger(diceSize);
|
int luckDiceSize = gameInfo.getSettings().getInteger(diceSize);
|
||||||
|
int biasValue = gameInfo.getSettings().getInteger(biasValueSetting);
|
||||||
size_t chanceIndex = std::min<size_t>(goodLuckChanceVector.size(), moraleLuckValue) - 1; // array index, so 0-indexed
|
size_t chanceIndex = std::min<size_t>(goodLuckChanceVector.size(), moraleLuckValue) - 1; // array index, so 0-indexed
|
||||||
|
|
||||||
if(!seeds.count(actor))
|
if(!seeds.count(actor))
|
||||||
@@ -74,27 +75,27 @@ bool GameRandomizer::rollMoraleLuck(std::map<ObjectInstanceID, RandomGeneratorWi
|
|||||||
if(goodLuckChanceVector.size() == 0)
|
if(goodLuckChanceVector.size() == 0)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
return seeds.at(actor).roll(goodLuckChanceVector[chanceIndex], luckDiceSize, biasValueLuckMorale);
|
return seeds.at(actor).roll(goodLuckChanceVector[chanceIndex], luckDiceSize, biasValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool GameRandomizer::rollGoodMorale(ObjectInstanceID actor, int moraleValue)
|
bool GameRandomizer::rollGoodMorale(ObjectInstanceID actor, int moraleValue)
|
||||||
{
|
{
|
||||||
return rollMoraleLuck(goodMoraleSeed, actor, moraleValue, EGameSettings::COMBAT_MORALE_DICE_SIZE, EGameSettings::COMBAT_GOOD_MORALE_CHANCE);
|
return rollMoraleLuck(goodMoraleSeed, actor, moraleValue, EGameSettings::COMBAT_MORALE_BIAS, EGameSettings::COMBAT_MORALE_DICE_SIZE, EGameSettings::COMBAT_GOOD_MORALE_CHANCE);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool GameRandomizer::rollBadMorale(ObjectInstanceID actor, int moraleValue)
|
bool GameRandomizer::rollBadMorale(ObjectInstanceID actor, int moraleValue)
|
||||||
{
|
{
|
||||||
return rollMoraleLuck(badMoraleSeed, actor, moraleValue, EGameSettings::COMBAT_MORALE_DICE_SIZE, EGameSettings::COMBAT_BAD_MORALE_CHANCE);
|
return rollMoraleLuck(badMoraleSeed, actor, moraleValue, EGameSettings::COMBAT_MORALE_BIAS, EGameSettings::COMBAT_MORALE_DICE_SIZE, EGameSettings::COMBAT_BAD_MORALE_CHANCE);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool GameRandomizer::rollGoodLuck(ObjectInstanceID actor, int luckValue)
|
bool GameRandomizer::rollGoodLuck(ObjectInstanceID actor, int luckValue)
|
||||||
{
|
{
|
||||||
return rollMoraleLuck(goodLuckSeed, actor, luckValue, EGameSettings::COMBAT_LUCK_DICE_SIZE, EGameSettings::COMBAT_GOOD_LUCK_CHANCE);
|
return rollMoraleLuck(goodLuckSeed, actor, luckValue, EGameSettings::COMBAT_LUCK_BIAS, EGameSettings::COMBAT_LUCK_DICE_SIZE, EGameSettings::COMBAT_GOOD_LUCK_CHANCE);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool GameRandomizer::rollBadLuck(ObjectInstanceID actor, int luckValue)
|
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_BIAS, EGameSettings::COMBAT_LUCK_DICE_SIZE, EGameSettings::COMBAT_BAD_LUCK_CHANCE);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool GameRandomizer::rollCombatAbility(ObjectInstanceID actor, int percentageChance)
|
bool GameRandomizer::rollCombatAbility(ObjectInstanceID actor, int percentageChance)
|
||||||
@@ -108,7 +109,9 @@ bool GameRandomizer::rollCombatAbility(ObjectInstanceID actor, int percentageCha
|
|||||||
if(percentageChance >= 100)
|
if(percentageChance >= 100)
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
return combatAbilitySeed.at(actor).roll(percentageChance, 100, biasValueAbility);
|
int biasValue = gameInfo.getSettings().getInteger(EGameSettings::COMBAT_ABILITY_BIAS);
|
||||||
|
|
||||||
|
return combatAbilitySeed.at(actor).roll(percentageChance, 100, biasValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
CreatureID GameRandomizer::rollCreature()
|
CreatureID GameRandomizer::rollCreature()
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ enum class EGameSettings;
|
|||||||
|
|
||||||
class CGHeroInstance;
|
class CGHeroInstance;
|
||||||
|
|
||||||
class RandomizationBias
|
class DLL_LINKAGE RandomizationBias
|
||||||
{
|
{
|
||||||
int32_t accumulatedBias = 0;
|
int32_t accumulatedBias = 0;
|
||||||
|
|
||||||
@@ -26,6 +26,12 @@ public:
|
|||||||
/// Performs coin flip with specified success chance
|
/// Performs coin flip with specified success chance
|
||||||
/// Returns true with probability successChance percents, and false with probability totalWeight-successChance percents
|
/// Returns true with probability successChance percents, and false with probability totalWeight-successChance percents
|
||||||
bool roll(vstd::RNG & generator, int successChance, int totalWeight, int biasValue);
|
bool roll(vstd::RNG & generator, int successChance, int totalWeight, int biasValue);
|
||||||
|
|
||||||
|
template<typename Handler>
|
||||||
|
void serialize(Handler & h)
|
||||||
|
{
|
||||||
|
h & accumulatedBias;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Biased randomizer that has following properties:
|
/// Biased randomizer that has following properties:
|
||||||
@@ -34,32 +40,44 @@ public:
|
|||||||
/// - at bias value between 1..99 similar guarantee is also provided, but with larger number of rolls
|
/// - at bias value between 1..99 similar guarantee is also provided, but with larger number of rolls
|
||||||
/// No matter what bias is, statistical probability on large number of rolls remains the same
|
/// No matter what bias is, statistical probability on large number of rolls remains the same
|
||||||
/// Its goal is to simulate human expectations of random distributions and reduce frustration from "bad" rolls
|
/// Its goal is to simulate human expectations of random distributions and reduce frustration from "bad" rolls
|
||||||
class RandomGeneratorWithBias
|
class DLL_LINKAGE RandomGeneratorWithBias
|
||||||
{
|
{
|
||||||
CRandomGenerator generator;
|
CRandomGenerator generator;
|
||||||
RandomizationBias bias;
|
RandomizationBias bias;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit RandomGeneratorWithBias(int seed);
|
explicit RandomGeneratorWithBias(int seed = 0);
|
||||||
/// Performs coin flip with specified success chance
|
/// Performs coin flip with specified success chance
|
||||||
/// Returns true with probability successChance percents, and false with probability 100-successChance percents
|
/// Returns true with probability successChance percents, and false with probability 100-successChance percents
|
||||||
bool roll(int successChance, int totalWeight, int biasValue);
|
bool roll(int successChance, int totalWeight, int biasValue);
|
||||||
|
|
||||||
|
template<typename Handler>
|
||||||
|
void serialize(Handler & h)
|
||||||
|
{
|
||||||
|
h & generator;
|
||||||
|
h & bias;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
class DLL_LINKAGE GameRandomizer final : public IGameRandomizer
|
class DLL_LINKAGE GameRandomizer final : public IGameRandomizer
|
||||||
{
|
{
|
||||||
static constexpr int biasValueLuckMorale = 10;
|
|
||||||
static constexpr int biasValueAbility = 25;
|
|
||||||
|
|
||||||
struct HeroSkillRandomizer
|
struct HeroSkillRandomizer
|
||||||
{
|
{
|
||||||
explicit HeroSkillRandomizer(int seed)
|
explicit HeroSkillRandomizer(int seed = 0)
|
||||||
: seed(seed)
|
: seed(seed)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
CRandomGenerator seed;
|
CRandomGenerator seed;
|
||||||
int8_t magicSchoolCounter = 1;
|
int8_t magicSchoolCounter = 1;
|
||||||
int8_t wisdomCounter = 1;
|
int8_t wisdomCounter = 1;
|
||||||
|
|
||||||
|
template<typename Handler>
|
||||||
|
void serialize(Handler & h)
|
||||||
|
{
|
||||||
|
h & seed;
|
||||||
|
h & magicSchoolCounter;
|
||||||
|
h & wisdomCounter;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const IGameInfoCallback & gameInfo;
|
const IGameInfoCallback & gameInfo;
|
||||||
@@ -71,7 +89,6 @@ class DLL_LINKAGE GameRandomizer final : public IGameRandomizer
|
|||||||
std::map<ArtifactID, int> allocatedArtifacts;
|
std::map<ArtifactID, int> allocatedArtifacts;
|
||||||
|
|
||||||
std::map<HeroTypeID, HeroSkillRandomizer> heroSkillSeed;
|
std::map<HeroTypeID, HeroSkillRandomizer> heroSkillSeed;
|
||||||
std::map<PlayerColor, CRandomGenerator> playerTavern;
|
|
||||||
|
|
||||||
std::map<ObjectInstanceID, RandomGeneratorWithBias> goodMoraleSeed;
|
std::map<ObjectInstanceID, RandomGeneratorWithBias> goodMoraleSeed;
|
||||||
std::map<ObjectInstanceID, RandomGeneratorWithBias> badMoraleSeed;
|
std::map<ObjectInstanceID, RandomGeneratorWithBias> badMoraleSeed;
|
||||||
@@ -79,7 +96,7 @@ class DLL_LINKAGE GameRandomizer final : public IGameRandomizer
|
|||||||
std::map<ObjectInstanceID, RandomGeneratorWithBias> badLuckSeed;
|
std::map<ObjectInstanceID, RandomGeneratorWithBias> badLuckSeed;
|
||||||
std::map<ObjectInstanceID, RandomGeneratorWithBias> combatAbilitySeed;
|
std::map<ObjectInstanceID, RandomGeneratorWithBias> combatAbilitySeed;
|
||||||
|
|
||||||
bool rollMoraleLuck(std::map<ObjectInstanceID, RandomGeneratorWithBias> & seeds, ObjectInstanceID actor, int moraleLuckValue, EGameSettings diceSize, EGameSettings diceWeights);
|
bool rollMoraleLuck(std::map<ObjectInstanceID, RandomGeneratorWithBias> & seeds, ObjectInstanceID actor, int moraleLuckValue, EGameSettings biasValue, EGameSettings diceSize, EGameSettings diceWeights);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit GameRandomizer(const IGameInfoCallback & gameInfo);
|
explicit GameRandomizer(const IGameInfoCallback & gameInfo);
|
||||||
@@ -111,6 +128,17 @@ public:
|
|||||||
void serialize(Handler & h)
|
void serialize(Handler & h)
|
||||||
{
|
{
|
||||||
h & globalRandomNumberGenerator;
|
h & globalRandomNumberGenerator;
|
||||||
|
|
||||||
|
if (h.hasFeature(Handler::Version::RANDOMIZATION_REWORK))
|
||||||
|
{
|
||||||
|
h & allocatedArtifacts;
|
||||||
|
h & heroSkillSeed;
|
||||||
|
h & goodMoraleSeed;
|
||||||
|
h & badMoraleSeed;
|
||||||
|
h & goodLuckSeed;
|
||||||
|
h & badLuckSeed;
|
||||||
|
h & combatAbilitySeed;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user