mirror of
https://github.com/vcmi/vcmi.git
synced 2025-06-21 00:19:29 +02:00
Merge pull request #5724 from IvanSavenko/randomization
Better randomization logic
This commit is contained in:
@ -12,7 +12,7 @@
|
|||||||
|
|
||||||
#include "../Engine/Nullkiller.h"
|
#include "../Engine/Nullkiller.h"
|
||||||
#include "../pforeach.h"
|
#include "../pforeach.h"
|
||||||
#include "../../../lib/CRandomGenerator.h"
|
#include "../../../lib/callback/GameRandomizer.h"
|
||||||
#include "../../../lib/logging/VisualLogger.h"
|
#include "../../../lib/logging/VisualLogger.h"
|
||||||
|
|
||||||
namespace NKAI
|
namespace NKAI
|
||||||
@ -211,14 +211,14 @@ void DangerHitMapAnalyzer::calculateTileOwners()
|
|||||||
auto addTownHero = [&](const CGTownInstance * town)
|
auto addTownHero = [&](const CGTownInstance * town)
|
||||||
{
|
{
|
||||||
auto townHero = temporaryHeroes.emplace_back(std::make_unique<CGHeroInstance>(town->cb)).get();
|
auto townHero = temporaryHeroes.emplace_back(std::make_unique<CGHeroInstance>(town->cb)).get();
|
||||||
CRandomGenerator rng;
|
GameRandomizer randomizer(*town->cb);
|
||||||
auto visitablePos = town->visitablePos();
|
auto visitablePos = town->visitablePos();
|
||||||
|
|
||||||
townHero->id = town->id;
|
townHero->id = town->id;
|
||||||
townHero->setOwner(ai->playerID); // lets avoid having multiple colors
|
townHero->setOwner(ai->playerID); // lets avoid having multiple colors
|
||||||
townHero->initHero(rng, static_cast<HeroTypeID>(0));
|
townHero->initHero(randomizer, static_cast<HeroTypeID>(0));
|
||||||
townHero->pos = townHero->convertFromVisitablePos(visitablePos);
|
townHero->pos = townHero->convertFromVisitablePos(visitablePos);
|
||||||
townHero->initObj(rng);
|
townHero->initObj(randomizer);
|
||||||
|
|
||||||
heroTownMap[townHero] = town;
|
heroTownMap[townHero] = town;
|
||||||
townHeroes[townHero] = HeroRole::MAIN;
|
townHeroes[townHero] = HeroRole::MAIN;
|
||||||
|
@ -10,7 +10,7 @@
|
|||||||
#include "StdInc.h"
|
#include "StdInc.h"
|
||||||
#include "ObjectGraphCalculator.h"
|
#include "ObjectGraphCalculator.h"
|
||||||
#include "AIPathfinderConfig.h"
|
#include "AIPathfinderConfig.h"
|
||||||
#include "../../../lib/CRandomGenerator.h"
|
#include "../../../lib/callback/GameRandomizer.h"
|
||||||
#include "../../../lib/mapping/CMap.h"
|
#include "../../../lib/mapping/CMap.h"
|
||||||
#include "../Engine/Nullkiller.h"
|
#include "../Engine/Nullkiller.h"
|
||||||
#include "../../../lib/logging/VisualLogger.h"
|
#include "../../../lib/logging/VisualLogger.h"
|
||||||
@ -287,13 +287,13 @@ void ObjectGraphCalculator::addObjectActor(const CGObjectInstance * obj)
|
|||||||
{
|
{
|
||||||
auto objectActor = temporaryActorHeroes.emplace_back(std::make_unique<CGHeroInstance>(obj->cb)).get();
|
auto objectActor = temporaryActorHeroes.emplace_back(std::make_unique<CGHeroInstance>(obj->cb)).get();
|
||||||
|
|
||||||
CRandomGenerator rng;
|
GameRandomizer randomizer(*obj->cb);
|
||||||
auto visitablePos = obj->visitablePos();
|
auto visitablePos = obj->visitablePos();
|
||||||
|
|
||||||
objectActor->setOwner(ai->playerID); // lets avoid having multiple colors
|
objectActor->setOwner(ai->playerID); // lets avoid having multiple colors
|
||||||
objectActor->initHero(rng, static_cast<HeroTypeID>(0));
|
objectActor->initHero(randomizer, static_cast<HeroTypeID>(0));
|
||||||
objectActor->pos = objectActor->convertFromVisitablePos(visitablePos);
|
objectActor->pos = objectActor->convertFromVisitablePos(visitablePos);
|
||||||
objectActor->initObj(rng);
|
objectActor->initObj(randomizer);
|
||||||
|
|
||||||
if(cb->getTile(visitablePos)->isWater())
|
if(cb->getTile(visitablePos)->isWater())
|
||||||
{
|
{
|
||||||
@ -325,12 +325,12 @@ void ObjectGraphCalculator::addJunctionActor(const int3 & visitablePos, bool isV
|
|||||||
auto internalCb = temporaryActorHeroes.front()->cb;
|
auto internalCb = temporaryActorHeroes.front()->cb;
|
||||||
auto objectActor = temporaryActorHeroes.emplace_back(std::make_unique<CGHeroInstance>(internalCb)).get();
|
auto objectActor = temporaryActorHeroes.emplace_back(std::make_unique<CGHeroInstance>(internalCb)).get();
|
||||||
|
|
||||||
CRandomGenerator rng;
|
GameRandomizer randomizer(*internalCb);
|
||||||
|
|
||||||
objectActor->setOwner(ai->playerID); // lets avoid having multiple colors
|
objectActor->setOwner(ai->playerID); // lets avoid having multiple colors
|
||||||
objectActor->initHero(rng, static_cast<HeroTypeID>(0));
|
objectActor->initHero(randomizer, static_cast<HeroTypeID>(0));
|
||||||
objectActor->pos = objectActor->convertFromVisitablePos(visitablePos);
|
objectActor->pos = objectActor->convertFromVisitablePos(visitablePos);
|
||||||
objectActor->initObj(rng);
|
objectActor->initObj(randomizer);
|
||||||
|
|
||||||
if(isVirtualBoat || ai->cb->getTile(visitablePos)->isWater())
|
if(isVirtualBoat || ai->cb->getTile(visitablePos)->isWater())
|
||||||
{
|
{
|
||||||
|
@ -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,
|
||||||
|
@ -1357,34 +1357,20 @@ CCreatureHandler::~CCreatureHandler()
|
|||||||
p.first.clear();
|
p.first.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
CreatureID CCreatureHandler::pickRandomMonster(vstd::RNG & rand, int tier) const
|
|
||||||
{
|
|
||||||
std::vector<CreatureID> allowed;
|
|
||||||
for(const auto & creature : objects)
|
|
||||||
{
|
|
||||||
if(creature->special)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if(creature->excludeFromRandomization)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (creature->level == tier || tier == -1)
|
|
||||||
allowed.push_back(creature->getId());
|
|
||||||
}
|
|
||||||
|
|
||||||
if(allowed.empty())
|
|
||||||
{
|
|
||||||
logGlobal->warn("Cannot pick a random creature of tier %d!", tier);
|
|
||||||
return CreatureID::NONE;
|
|
||||||
}
|
|
||||||
|
|
||||||
return *RandomGeneratorUtil::nextItem(allowed, rand);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void CCreatureHandler::afterLoadFinalization()
|
void CCreatureHandler::afterLoadFinalization()
|
||||||
{
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::set<CreatureID> CCreatureHandler::getDefaultAllowed() const
|
||||||
|
{
|
||||||
|
std::set<CreatureID> result;
|
||||||
|
|
||||||
|
for(auto & creature : objects)
|
||||||
|
if (creature && !creature->special)
|
||||||
|
result.insert(creature->getId());
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
VCMI_LIB_NAMESPACE_END
|
VCMI_LIB_NAMESPACE_END
|
||||||
|
@ -223,7 +223,6 @@ public:
|
|||||||
std::vector< std::vector <ui8> > skillLevels; //how much of a bonus will be given to commander with every level. SPELL_POWER also gives CASTS and RESISTANCE
|
std::vector< std::vector <ui8> > skillLevels; //how much of a bonus will be given to commander with every level. SPELL_POWER also gives CASTS and RESISTANCE
|
||||||
std::vector <std::pair <std::vector<std::shared_ptr<Bonus> >, std::pair <ui8, ui8> > > skillRequirements; // first - Bonus, second - which two skills are needed to use it
|
std::vector <std::pair <std::vector<std::shared_ptr<Bonus> >, std::pair <ui8, ui8> > > skillRequirements; // first - Bonus, second - which two skills are needed to use it
|
||||||
|
|
||||||
CreatureID pickRandomMonster(vstd::RNG & rand, int tier = -1) const; //tier <1 - CREATURES_PER_TOWN> or -1 for any
|
|
||||||
|
|
||||||
CCreatureHandler();
|
CCreatureHandler();
|
||||||
~CCreatureHandler();
|
~CCreatureHandler();
|
||||||
@ -238,6 +237,8 @@ public:
|
|||||||
|
|
||||||
std::vector<JsonNode> loadLegacyData() override;
|
std::vector<JsonNode> loadLegacyData() override;
|
||||||
|
|
||||||
|
std::set<CreatureID> getDefaultAllowed() const;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
VCMI_LIB_NAMESPACE_END
|
VCMI_LIB_NAMESPACE_END
|
||||||
|
@ -85,6 +85,7 @@ set(lib_MAIN_SRCS
|
|||||||
callback/CGameInfoCallback.cpp
|
callback/CGameInfoCallback.cpp
|
||||||
callback/CNonConstInfoCallback.cpp
|
callback/CNonConstInfoCallback.cpp
|
||||||
callback/CPlayerSpecificInfoCallback.cpp
|
callback/CPlayerSpecificInfoCallback.cpp
|
||||||
|
callback/GameRandomizer.cpp
|
||||||
|
|
||||||
campaign/CampaignHandler.cpp
|
campaign/CampaignHandler.cpp
|
||||||
campaign/CampaignState.cpp
|
campaign/CampaignState.cpp
|
||||||
@ -477,6 +478,9 @@ set(lib_MAIN_HEADERS
|
|||||||
callback/IGameEventCallback.h
|
callback/IGameEventCallback.h
|
||||||
callback/IGameEventsReceiver.h
|
callback/IGameEventsReceiver.h
|
||||||
callback/IGameInfoCallback.h
|
callback/IGameInfoCallback.h
|
||||||
|
callback/IGameRandomizer.h
|
||||||
|
callback/GameRandomizer.h
|
||||||
|
|
||||||
|
|
||||||
campaign/CampaignConstants.h
|
campaign/CampaignConstants.h
|
||||||
campaign/CampaignHandler.h
|
campaign/CampaignHandler.h
|
||||||
|
@ -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,
|
||||||
|
@ -947,16 +947,6 @@ void CGameInfoCallback::getAllTiles(std::unordered_set<int3> & tiles, std::optio
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void CGameInfoCallback::pickAllowedArtsSet(std::vector<ArtifactID> & out, vstd::RNG & rand)
|
|
||||||
{
|
|
||||||
for (int j = 0; j < 3 ; j++)
|
|
||||||
out.push_back(gameState().pickRandomArtifact(rand, EArtifactClass::ART_TREASURE));
|
|
||||||
for (int j = 0; j < 3 ; j++)
|
|
||||||
out.push_back(gameState().pickRandomArtifact(rand, EArtifactClass::ART_MINOR));
|
|
||||||
|
|
||||||
out.push_back(gameState().pickRandomArtifact(rand, EArtifactClass::ART_MAJOR));
|
|
||||||
}
|
|
||||||
|
|
||||||
void CGameInfoCallback::getAllowedSpells(std::vector<SpellID> & out, std::optional<ui16> level)
|
void CGameInfoCallback::getAllowedSpells(std::vector<SpellID> & out, std::optional<ui16> level)
|
||||||
{
|
{
|
||||||
for (auto const & spellID : gameState().getMap().allowedSpells)
|
for (auto const & spellID : gameState().getMap().allowedSpells)
|
||||||
|
@ -115,7 +115,6 @@ public:
|
|||||||
void getTilesInRange(std::unordered_set<int3> & tiles, const int3 & pos, int radius, ETileVisibility mode, std::optional<PlayerColor> player = std::optional<PlayerColor>(), int3::EDistanceFormula formula = int3::DIST_2D) const override;
|
void getTilesInRange(std::unordered_set<int3> & tiles, const int3 & pos, int radius, ETileVisibility mode, std::optional<PlayerColor> player = std::optional<PlayerColor>(), int3::EDistanceFormula formula = int3::DIST_2D) const override;
|
||||||
void getAllTiles(std::unordered_set<int3> &tiles, std::optional<PlayerColor> player, int level, std::function<bool(const TerrainTile *)> filter) const override;
|
void getAllTiles(std::unordered_set<int3> &tiles, std::optional<PlayerColor> player, int level, std::function<bool(const TerrainTile *)> filter) const override;
|
||||||
|
|
||||||
void pickAllowedArtsSet(std::vector<ArtifactID> & out, vstd::RNG & rand) override;
|
|
||||||
void getAllowedSpells(std::vector<SpellID> &out, std::optional<ui16> level = std::nullopt);
|
void getAllowedSpells(std::vector<SpellID> &out, std::optional<ui16> level = std::nullopt);
|
||||||
|
|
||||||
#if SCRIPTING_ENABLED
|
#if SCRIPTING_ENABLED
|
||||||
|
333
lib/callback/GameRandomizer.cpp
Normal file
333
lib/callback/GameRandomizer.cpp
Normal file
@ -0,0 +1,333 @@
|
|||||||
|
/*
|
||||||
|
* GameRandomizer.cpp, part of VCMI engine
|
||||||
|
*
|
||||||
|
* Authors: listed in file AUTHORS in main folder
|
||||||
|
*
|
||||||
|
* License: GNU General Public License v2.0 or later
|
||||||
|
* Full text of license available in license.txt file, in main folder
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
#include "StdInc.h"
|
||||||
|
#include "GameRandomizer.h"
|
||||||
|
|
||||||
|
#include "IGameInfoCallback.h"
|
||||||
|
|
||||||
|
#include "../../lib/CRandomGenerator.h"
|
||||||
|
#include "../../lib/GameLibrary.h"
|
||||||
|
#include "../../lib/CCreatureHandler.h"
|
||||||
|
#include "../../lib/CSkillHandler.h"
|
||||||
|
#include "../../lib/IGameSettings.h"
|
||||||
|
#include "../../lib/entities/artifact/CArtHandler.h"
|
||||||
|
#include "../../lib/entities/artifact/EArtifactClass.h"
|
||||||
|
#include "../../lib/entities/hero/CHeroClass.h"
|
||||||
|
#include "../../lib/mapObjects/CGHeroInstance.h"
|
||||||
|
|
||||||
|
VCMI_LIB_NAMESPACE_BEGIN
|
||||||
|
|
||||||
|
bool RandomizationBias::roll(vstd::RNG & generator, int successChance, int totalWeight, int biasValue)
|
||||||
|
{
|
||||||
|
assert(successChance > 0);
|
||||||
|
assert(totalWeight >= successChance);
|
||||||
|
|
||||||
|
int failChance = totalWeight - successChance;
|
||||||
|
int newRoll = generator.nextInt(1, totalWeight);
|
||||||
|
// accumulated bias is stored as premultiplied to avoid precision loss on division
|
||||||
|
// so multiply everything else in equation to compensate
|
||||||
|
// precision loss is small, and generally insignificant, but better to play it safe
|
||||||
|
bool success = newRoll * totalWeight - accumulatedBias <= successChance * totalWeight;
|
||||||
|
if(success)
|
||||||
|
accumulatedBias -= failChance * biasValue;
|
||||||
|
else
|
||||||
|
accumulatedBias += successChance * biasValue;
|
||||||
|
|
||||||
|
return success;
|
||||||
|
}
|
||||||
|
|
||||||
|
RandomGeneratorWithBias::RandomGeneratorWithBias(int seed)
|
||||||
|
: generator(seed)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RandomGeneratorWithBias::roll(int successChance, int totalWeight, int biasValue)
|
||||||
|
{
|
||||||
|
return bias.roll(generator, successChance, totalWeight, biasValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
GameRandomizer::GameRandomizer(const IGameInfoCallback & gameInfo)
|
||||||
|
: gameInfo(gameInfo)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
GameRandomizer::~GameRandomizer() = default;
|
||||||
|
|
||||||
|
|
||||||
|
bool GameRandomizer::rollMoraleLuck(std::map<ObjectInstanceID, RandomGeneratorWithBias> & seeds, ObjectInstanceID actor, int moraleLuckValue, EGameSettings biasValueSetting, EGameSettings diceSizeSetting, EGameSettings chanceVectorSetting)
|
||||||
|
{
|
||||||
|
assert(moraleLuckValue > 0);
|
||||||
|
auto chanceVector = gameInfo.getSettings().getVector(chanceVectorSetting);
|
||||||
|
int diceSize = gameInfo.getSettings().getInteger(diceSizeSetting);
|
||||||
|
int biasValue = gameInfo.getSettings().getInteger(biasValueSetting);
|
||||||
|
size_t chanceIndex = std::min<size_t>(chanceVector.size(), moraleLuckValue) - 1; // array index, so 0-indexed
|
||||||
|
|
||||||
|
if(!seeds.count(actor))
|
||||||
|
seeds.try_emplace(actor, getDefault().nextInt());
|
||||||
|
|
||||||
|
if(chanceVector.empty())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return seeds.at(actor).roll(chanceVector[chanceIndex], diceSize, biasValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool GameRandomizer::rollGoodMorale(ObjectInstanceID actor, int moraleValue)
|
||||||
|
{
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
if(!combatAbilitySeed.count(actor))
|
||||||
|
combatAbilitySeed.try_emplace(actor, getDefault().nextInt());
|
||||||
|
|
||||||
|
if(percentageChance <= 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if(percentageChance >= 100)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
int biasValue = gameInfo.getSettings().getInteger(EGameSettings::COMBAT_ABILITY_BIAS);
|
||||||
|
|
||||||
|
return combatAbilitySeed.at(actor).roll(percentageChance, 100, biasValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
CreatureID GameRandomizer::rollCreature()
|
||||||
|
{
|
||||||
|
std::vector<CreatureID> allowed;
|
||||||
|
for(const auto & creatureID : LIBRARY->creh->getDefaultAllowed())
|
||||||
|
{
|
||||||
|
const auto * creaturePtr = creatureID.toCreature();
|
||||||
|
if(!creaturePtr->excludeFromRandomization)
|
||||||
|
allowed.push_back(creaturePtr->getId());
|
||||||
|
}
|
||||||
|
|
||||||
|
if(allowed.empty())
|
||||||
|
throw std::runtime_error("Cannot pick a random creature!");
|
||||||
|
|
||||||
|
return *RandomGeneratorUtil::nextItem(allowed, getDefault());
|
||||||
|
}
|
||||||
|
|
||||||
|
CreatureID GameRandomizer::rollCreature(int tier)
|
||||||
|
{
|
||||||
|
std::vector<CreatureID> allowed;
|
||||||
|
for(const auto & creatureID : LIBRARY->creh->getDefaultAllowed())
|
||||||
|
{
|
||||||
|
const auto * creaturePtr = creatureID.toCreature();
|
||||||
|
if(creaturePtr->excludeFromRandomization)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if(creaturePtr->getLevel() == tier)
|
||||||
|
allowed.push_back(creaturePtr->getId());
|
||||||
|
}
|
||||||
|
|
||||||
|
if(allowed.empty())
|
||||||
|
throw std::runtime_error("Cannot pick a random creature!");
|
||||||
|
|
||||||
|
return *RandomGeneratorUtil::nextItem(allowed, getDefault());
|
||||||
|
}
|
||||||
|
|
||||||
|
ArtifactID GameRandomizer::rollArtifact()
|
||||||
|
{
|
||||||
|
std::set<ArtifactID> potentialPicks;
|
||||||
|
|
||||||
|
for(const auto & artifactID : LIBRARY->arth->getDefaultAllowed())
|
||||||
|
{
|
||||||
|
if(!LIBRARY->arth->legalArtifact(artifactID))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
potentialPicks.insert(artifactID);
|
||||||
|
}
|
||||||
|
|
||||||
|
return rollArtifact(potentialPicks);
|
||||||
|
}
|
||||||
|
|
||||||
|
ArtifactID GameRandomizer::rollArtifact(EArtifactClass type)
|
||||||
|
{
|
||||||
|
std::set<ArtifactID> potentialPicks;
|
||||||
|
|
||||||
|
for(const auto & artifactID : LIBRARY->arth->getDefaultAllowed())
|
||||||
|
{
|
||||||
|
if(!LIBRARY->arth->legalArtifact(artifactID))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if(!gameInfo.isAllowed(artifactID))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
const auto * artifact = artifactID.toArtifact();
|
||||||
|
|
||||||
|
if(type != artifact->aClass)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
potentialPicks.insert(artifactID);
|
||||||
|
}
|
||||||
|
|
||||||
|
return rollArtifact(potentialPicks);
|
||||||
|
}
|
||||||
|
|
||||||
|
ArtifactID GameRandomizer::rollArtifact(std::set<ArtifactID> potentialPicks)
|
||||||
|
{
|
||||||
|
// No allowed artifacts at all - give Grail - this can't be banned (hopefully)
|
||||||
|
// FIXME: investigate how such cases are handled by H3 - some heavily customized user-made maps likely rely on H3 behavior
|
||||||
|
if(potentialPicks.empty())
|
||||||
|
{
|
||||||
|
logGlobal->warn("Failed to find artifact that matches requested parameters!");
|
||||||
|
return ArtifactID::GRAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find how many times least used artifacts were picked by randomizer
|
||||||
|
int leastUsedTimes = std::numeric_limits<int>::max();
|
||||||
|
for(const auto & artifact : potentialPicks)
|
||||||
|
if(allocatedArtifacts[artifact] < leastUsedTimes)
|
||||||
|
leastUsedTimes = allocatedArtifacts[artifact];
|
||||||
|
|
||||||
|
// Pick all artifacts that were used least number of times
|
||||||
|
std::set<ArtifactID> preferredPicks;
|
||||||
|
for(const auto & artifact : potentialPicks)
|
||||||
|
if(allocatedArtifacts[artifact] == leastUsedTimes)
|
||||||
|
preferredPicks.insert(artifact);
|
||||||
|
|
||||||
|
assert(!preferredPicks.empty());
|
||||||
|
|
||||||
|
ArtifactID artID = *RandomGeneratorUtil::nextItem(preferredPicks, getDefault());
|
||||||
|
allocatedArtifacts[artID] += 1; // record +1 more usage
|
||||||
|
return artID;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<ArtifactID> GameRandomizer::rollMarketArtifactSet()
|
||||||
|
{
|
||||||
|
return {
|
||||||
|
rollArtifact(EArtifactClass::ART_TREASURE),
|
||||||
|
rollArtifact(EArtifactClass::ART_TREASURE),
|
||||||
|
rollArtifact(EArtifactClass::ART_TREASURE),
|
||||||
|
rollArtifact(EArtifactClass::ART_MINOR),
|
||||||
|
rollArtifact(EArtifactClass::ART_MINOR),
|
||||||
|
rollArtifact(EArtifactClass::ART_MINOR),
|
||||||
|
rollArtifact(EArtifactClass::ART_MAJOR)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
vstd::RNG & GameRandomizer::getDefault()
|
||||||
|
{
|
||||||
|
return globalRandomNumberGenerator;
|
||||||
|
}
|
||||||
|
|
||||||
|
void GameRandomizer::setSeed(int newSeed)
|
||||||
|
{
|
||||||
|
globalRandomNumberGenerator.setSeed(newSeed);
|
||||||
|
}
|
||||||
|
|
||||||
|
PrimarySkill GameRandomizer::rollPrimarySkillForLevelup(const CGHeroInstance * hero)
|
||||||
|
{
|
||||||
|
if(!heroSkillSeed.count(hero->getHeroTypeID()))
|
||||||
|
heroSkillSeed.try_emplace(hero->getHeroTypeID(), getDefault().nextInt());
|
||||||
|
|
||||||
|
const bool isLowLevelHero = hero->level < GameConstants::HERO_HIGH_LEVEL;
|
||||||
|
const auto & skillChances = isLowLevelHero ? hero->getHeroClass()->primarySkillLowLevel : hero->getHeroClass()->primarySkillHighLevel;
|
||||||
|
auto & heroRng = heroSkillSeed.at(hero->getHeroTypeID());
|
||||||
|
|
||||||
|
if(hero->isCampaignYog())
|
||||||
|
{
|
||||||
|
// Yog can only receive Attack or Defence on level-up
|
||||||
|
std::vector<int> yogChances = {skillChances[0], skillChances[1]};
|
||||||
|
return static_cast<PrimarySkill>(RandomGeneratorUtil::nextItemWeighted(yogChances, heroRng.seed));
|
||||||
|
}
|
||||||
|
return static_cast<PrimarySkill>(RandomGeneratorUtil::nextItemWeighted(skillChances, heroRng.seed));
|
||||||
|
}
|
||||||
|
|
||||||
|
SecondarySkill GameRandomizer::rollSecondarySkillForLevelup(const CGHeroInstance * hero, const std::set<SecondarySkill> & options)
|
||||||
|
{
|
||||||
|
if(!heroSkillSeed.count(hero->getHeroTypeID()))
|
||||||
|
heroSkillSeed.try_emplace(hero->getHeroTypeID(), getDefault().nextInt());
|
||||||
|
|
||||||
|
auto & heroRng = heroSkillSeed.at(hero->getHeroTypeID());
|
||||||
|
|
||||||
|
auto getObligatorySkills = [](CSkill::Obligatory obl)
|
||||||
|
{
|
||||||
|
std::set<SecondarySkill> obligatory;
|
||||||
|
for(auto i = 0; i < LIBRARY->skillh->size(); i++)
|
||||||
|
if((*LIBRARY->skillh)[SecondarySkill(i)]->obligatory(obl))
|
||||||
|
obligatory.insert(i); //Always return all obligatory skills
|
||||||
|
|
||||||
|
return obligatory;
|
||||||
|
};
|
||||||
|
|
||||||
|
auto intersect = [](const std::set<SecondarySkill> & left, const std::set<SecondarySkill> & right)
|
||||||
|
{
|
||||||
|
std::set<SecondarySkill> intersection;
|
||||||
|
std::set_intersection(left.begin(), left.end(), right.begin(), right.end(), std::inserter(intersection, intersection.begin()));
|
||||||
|
return intersection;
|
||||||
|
};
|
||||||
|
|
||||||
|
std::set<SecondarySkill> wisdomList = getObligatorySkills(CSkill::Obligatory::MAJOR);
|
||||||
|
std::set<SecondarySkill> schoolList = getObligatorySkills(CSkill::Obligatory::MINOR);
|
||||||
|
|
||||||
|
bool wantsWisdom = heroRng.wisdomCounter + 1 >= hero->maxlevelsToWisdom();
|
||||||
|
bool wantsSchool = heroRng.magicSchoolCounter + 1 >= hero->maxlevelsToMagicSchool();
|
||||||
|
bool selectWisdom = wantsWisdom && !intersect(options, wisdomList).empty();
|
||||||
|
bool selectSchool = wantsSchool && !intersect(options, schoolList).empty();
|
||||||
|
|
||||||
|
std::set<SecondarySkill> actualCandidates;
|
||||||
|
|
||||||
|
if(selectWisdom)
|
||||||
|
actualCandidates = intersect(options, wisdomList);
|
||||||
|
else if(selectSchool)
|
||||||
|
actualCandidates = intersect(options, schoolList);
|
||||||
|
else
|
||||||
|
actualCandidates = options;
|
||||||
|
|
||||||
|
assert(!actualCandidates.empty());
|
||||||
|
|
||||||
|
std::vector<int> weights;
|
||||||
|
std::vector<SecondarySkill> skills;
|
||||||
|
|
||||||
|
for(const auto & possible : actualCandidates)
|
||||||
|
{
|
||||||
|
skills.push_back(possible);
|
||||||
|
if(hero->getHeroClass()->secSkillProbability.count(possible) != 0)
|
||||||
|
{
|
||||||
|
int weight = hero->getHeroClass()->secSkillProbability.at(possible);
|
||||||
|
weights.push_back(std::max(1, weight));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
weights.push_back(1); // H3 behavior - banned skills have minimal (1) chance to be picked
|
||||||
|
}
|
||||||
|
|
||||||
|
int selectedIndex = RandomGeneratorUtil::nextItemWeighted(weights, heroRng.seed);
|
||||||
|
SecondarySkill selectedSkill = skills.at(selectedIndex);
|
||||||
|
|
||||||
|
//deterministic secondary skills
|
||||||
|
++heroRng.magicSchoolCounter;
|
||||||
|
++heroRng.wisdomCounter;
|
||||||
|
|
||||||
|
if((*LIBRARY->skillh)[selectedSkill]->obligatory(CSkill::Obligatory::MAJOR))
|
||||||
|
heroRng.wisdomCounter = 0;
|
||||||
|
if((*LIBRARY->skillh)[selectedSkill]->obligatory(CSkill::Obligatory::MINOR))
|
||||||
|
heroRng.magicSchoolCounter = 0;
|
||||||
|
|
||||||
|
return selectedSkill;
|
||||||
|
}
|
||||||
|
|
||||||
|
VCMI_LIB_NAMESPACE_END
|
145
lib/callback/GameRandomizer.h
Normal file
145
lib/callback/GameRandomizer.h
Normal file
@ -0,0 +1,145 @@
|
|||||||
|
/*
|
||||||
|
* GameRandomizer.h, part of VCMI engine
|
||||||
|
*
|
||||||
|
* Authors: listed in file AUTHORS in main folder
|
||||||
|
*
|
||||||
|
* License: GNU General Public License v2.0 or later
|
||||||
|
* Full text of license available in license.txt file, in main folder
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "callback/IGameRandomizer.h"
|
||||||
|
#include "CRandomGenerator.h"
|
||||||
|
|
||||||
|
VCMI_LIB_NAMESPACE_BEGIN
|
||||||
|
|
||||||
|
enum class EGameSettings;
|
||||||
|
|
||||||
|
class CGHeroInstance;
|
||||||
|
|
||||||
|
class DLL_LINKAGE RandomizationBias
|
||||||
|
{
|
||||||
|
int32_t accumulatedBias = 0;
|
||||||
|
|
||||||
|
public:
|
||||||
|
/// Performs coin flip with specified success chance
|
||||||
|
/// Returns true with probability successChance percents, and false with probability totalWeight-successChance percents
|
||||||
|
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:
|
||||||
|
/// - at bias value of 0 it acts as statistical random generator, just like vstd::RNG
|
||||||
|
/// - at bias value of 100 it guarantees that it will take at most 100/chance rolls till succesfull roll
|
||||||
|
/// - 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
|
||||||
|
/// Its goal is to simulate human expectations of random distributions and reduce frustration from "bad" rolls
|
||||||
|
class DLL_LINKAGE RandomGeneratorWithBias
|
||||||
|
{
|
||||||
|
CRandomGenerator generator;
|
||||||
|
RandomizationBias bias;
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit RandomGeneratorWithBias(int seed = 0);
|
||||||
|
/// Performs coin flip with specified success chance
|
||||||
|
/// Returns true with probability successChance percents, and false with probability 100-successChance percents
|
||||||
|
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
|
||||||
|
{
|
||||||
|
struct HeroSkillRandomizer
|
||||||
|
{
|
||||||
|
explicit HeroSkillRandomizer(int seed = 0)
|
||||||
|
: seed(seed)
|
||||||
|
{}
|
||||||
|
|
||||||
|
CRandomGenerator seed;
|
||||||
|
int8_t magicSchoolCounter = 1;
|
||||||
|
int8_t wisdomCounter = 1;
|
||||||
|
|
||||||
|
template<typename Handler>
|
||||||
|
void serialize(Handler & h)
|
||||||
|
{
|
||||||
|
h & seed;
|
||||||
|
h & magicSchoolCounter;
|
||||||
|
h & wisdomCounter;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const IGameInfoCallback & gameInfo;
|
||||||
|
|
||||||
|
/// Global RNG, for use when there is no specialized instance
|
||||||
|
CRandomGenerator globalRandomNumberGenerator;
|
||||||
|
|
||||||
|
/// Stores number of times each artifact was placed on map via randomization
|
||||||
|
std::map<ArtifactID, int> allocatedArtifacts;
|
||||||
|
|
||||||
|
std::map<HeroTypeID, HeroSkillRandomizer> heroSkillSeed;
|
||||||
|
|
||||||
|
std::map<ObjectInstanceID, RandomGeneratorWithBias> goodMoraleSeed;
|
||||||
|
std::map<ObjectInstanceID, RandomGeneratorWithBias> badMoraleSeed;
|
||||||
|
std::map<ObjectInstanceID, RandomGeneratorWithBias> goodLuckSeed;
|
||||||
|
std::map<ObjectInstanceID, RandomGeneratorWithBias> badLuckSeed;
|
||||||
|
std::map<ObjectInstanceID, RandomGeneratorWithBias> combatAbilitySeed;
|
||||||
|
|
||||||
|
bool rollMoraleLuck(std::map<ObjectInstanceID, RandomGeneratorWithBias> & seeds, ObjectInstanceID actor, int moraleLuckValue, EGameSettings biasValue, EGameSettings diceSize, EGameSettings diceWeights);
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit GameRandomizer(const IGameInfoCallback & gameInfo);
|
||||||
|
~GameRandomizer();
|
||||||
|
|
||||||
|
PrimarySkill rollPrimarySkillForLevelup(const CGHeroInstance * hero) override;
|
||||||
|
SecondarySkill rollSecondarySkillForLevelup(const CGHeroInstance * hero, const std::set<SecondarySkill> & candidates) override;
|
||||||
|
|
||||||
|
bool rollGoodMorale(ObjectInstanceID actor, int moraleValue);
|
||||||
|
bool rollBadMorale(ObjectInstanceID actor, int moraleValue);
|
||||||
|
bool rollGoodLuck(ObjectInstanceID actor, int luckValue);
|
||||||
|
bool rollBadLuck(ObjectInstanceID actor, int luckValue);
|
||||||
|
|
||||||
|
bool rollCombatAbility(ObjectInstanceID actor, int percentageChance);
|
||||||
|
|
||||||
|
CreatureID rollCreature() override;
|
||||||
|
CreatureID rollCreature(int tier) override;
|
||||||
|
|
||||||
|
ArtifactID rollArtifact() override;
|
||||||
|
ArtifactID rollArtifact(EArtifactClass type) override;
|
||||||
|
ArtifactID rollArtifact(std::set<ArtifactID> filtered) override;
|
||||||
|
std::vector<ArtifactID> rollMarketArtifactSet() override;
|
||||||
|
|
||||||
|
vstd::RNG & getDefault() override;
|
||||||
|
|
||||||
|
void setSeed(int newSeed);
|
||||||
|
|
||||||
|
template<typename Handler>
|
||||||
|
void serialize(Handler & h)
|
||||||
|
{
|
||||||
|
h & globalRandomNumberGenerator;
|
||||||
|
|
||||||
|
if (h.hasFeature(Handler::Version::RANDOMIZATION_REWORK))
|
||||||
|
{
|
||||||
|
h & allocatedArtifacts;
|
||||||
|
h & heroSkillSeed;
|
||||||
|
h & goodMoraleSeed;
|
||||||
|
h & badMoraleSeed;
|
||||||
|
h & goodLuckSeed;
|
||||||
|
h & badLuckSeed;
|
||||||
|
h & combatAbilitySeed;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
VCMI_LIB_NAMESPACE_END
|
@ -117,6 +117,7 @@ public:
|
|||||||
|
|
||||||
virtual bool isVisitCoveredByAnotherQuery(const CGObjectInstance *obj, const CGHeroInstance *hero) = 0;
|
virtual bool isVisitCoveredByAnotherQuery(const CGObjectInstance *obj, const CGHeroInstance *hero) = 0;
|
||||||
|
|
||||||
|
/// Returns global random generator. TODO: remove, replace with IGameRanndomizer as separate parameter to such methods
|
||||||
virtual vstd::RNG & getRandomGenerator() = 0;
|
virtual vstd::RNG & getRandomGenerator() = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -162,10 +162,6 @@ public:
|
|||||||
virtual bool isTeleportChannelUnidirectional(TeleportChannelID id, PlayerColor player = PlayerColor::UNFLAGGABLE) const = 0;
|
virtual bool isTeleportChannelUnidirectional(TeleportChannelID id, PlayerColor player = PlayerColor::UNFLAGGABLE) const = 0;
|
||||||
virtual bool isTeleportEntrancePassable(const CGTeleport * obj, PlayerColor player) const = 0;
|
virtual bool isTeleportEntrancePassable(const CGTeleport * obj, PlayerColor player) const = 0;
|
||||||
|
|
||||||
/// gives 3 treasures, 3 minors, 1 major -> used by Black Market and Artifact Merchant
|
|
||||||
/// TODO: remove non-const method from this interface
|
|
||||||
virtual void pickAllowedArtsSet(std::vector<ArtifactID> & out, vstd::RNG & rand) = 0;
|
|
||||||
|
|
||||||
#if SCRIPTING_ENABLED
|
#if SCRIPTING_ENABLED
|
||||||
virtual scripting::Pool * getGlobalContextPool() const = 0;
|
virtual scripting::Pool * getGlobalContextPool() const = 0;
|
||||||
#endif
|
#endif
|
||||||
|
47
lib/callback/IGameRandomizer.h
Normal file
47
lib/callback/IGameRandomizer.h
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
/*
|
||||||
|
* IGameRandomizer.h, part of VCMI engine
|
||||||
|
*
|
||||||
|
* Authors: listed in file AUTHORS in main folder
|
||||||
|
*
|
||||||
|
* License: GNU General Public License v2.0 or later
|
||||||
|
* Full text of license available in license.txt file, in main folder
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "../constants/EntityIdentifiers.h"
|
||||||
|
|
||||||
|
VCMI_LIB_NAMESPACE_BEGIN
|
||||||
|
|
||||||
|
class CGHeroInstance;
|
||||||
|
|
||||||
|
enum class EArtifactClass;
|
||||||
|
|
||||||
|
namespace vstd
|
||||||
|
{
|
||||||
|
class RNG;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Provides source of random rolls for game entities
|
||||||
|
/// Instance of this interface only exists on server
|
||||||
|
class DLL_LINKAGE IGameRandomizer : boost::noncopyable
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual ~IGameRandomizer() = default;
|
||||||
|
|
||||||
|
virtual ArtifactID rollArtifact() = 0;
|
||||||
|
virtual ArtifactID rollArtifact(EArtifactClass type) = 0;
|
||||||
|
virtual ArtifactID rollArtifact(std::set<ArtifactID> filtered) = 0;
|
||||||
|
|
||||||
|
virtual std::vector<ArtifactID> rollMarketArtifactSet() = 0;
|
||||||
|
|
||||||
|
virtual CreatureID rollCreature() = 0;
|
||||||
|
virtual CreatureID rollCreature(int tier) = 0;
|
||||||
|
|
||||||
|
virtual PrimarySkill rollPrimarySkillForLevelup(const CGHeroInstance * hero) = 0;
|
||||||
|
virtual SecondarySkill rollSecondarySkillForLevelup(const CGHeroInstance * hero, const std::set<SecondarySkill> & candidates) = 0;
|
||||||
|
|
||||||
|
virtual vstd::RNG & getDefault() = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
VCMI_LIB_NAMESPACE_END
|
@ -19,29 +19,6 @@
|
|||||||
|
|
||||||
VCMI_LIB_NAMESPACE_BEGIN
|
VCMI_LIB_NAMESPACE_BEGIN
|
||||||
|
|
||||||
SecondarySkill CHeroClass::chooseSecSkill(const std::set<SecondarySkill> & possibles, vstd::RNG & rand) const //picks secondary skill out from given possibilities
|
|
||||||
{
|
|
||||||
assert(!possibles.empty());
|
|
||||||
|
|
||||||
std::vector<int> weights;
|
|
||||||
std::vector<SecondarySkill> skills;
|
|
||||||
|
|
||||||
for(const auto & possible : possibles)
|
|
||||||
{
|
|
||||||
skills.push_back(possible);
|
|
||||||
if (secSkillProbability.count(possible) != 0)
|
|
||||||
{
|
|
||||||
int weight = secSkillProbability.at(possible);
|
|
||||||
weights.push_back(std::max(1, weight));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
weights.push_back(1); // H3 behavior - banned skills have minimal (1) chance to be picked
|
|
||||||
}
|
|
||||||
|
|
||||||
int selectedIndex = RandomGeneratorUtil::nextItemWeighted(weights, rand);
|
|
||||||
return skills.at(selectedIndex);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool CHeroClass::isMagicHero() const
|
bool CHeroClass::isMagicHero() const
|
||||||
{
|
{
|
||||||
return affinity == MAGIC;
|
return affinity == MAGIC;
|
||||||
|
@ -72,7 +72,6 @@ public:
|
|||||||
std::string getNameTextID() const override;
|
std::string getNameTextID() const override;
|
||||||
|
|
||||||
bool isMagicHero() const;
|
bool isMagicHero() const;
|
||||||
SecondarySkill chooseSecSkill(const std::set<SecondarySkill> & possibles, vstd::RNG & rand) const; //picks secondary skill out from given possibilities
|
|
||||||
|
|
||||||
void updateFrom(const JsonNode & data);
|
void updateFrom(const JsonNode & data);
|
||||||
void serializeJson(JsonSerializeFormat & handler);
|
void serializeJson(JsonSerializeFormat & handler);
|
||||||
|
@ -32,6 +32,7 @@
|
|||||||
#include "../bonuses/Updaters.h"
|
#include "../bonuses/Updaters.h"
|
||||||
#include "../battle/BattleInfo.h"
|
#include "../battle/BattleInfo.h"
|
||||||
#include "../callback/IGameInfoCallback.h"
|
#include "../callback/IGameInfoCallback.h"
|
||||||
|
#include "../callback/IGameRandomizer.h"
|
||||||
#include "../campaign/CampaignState.h"
|
#include "../campaign/CampaignState.h"
|
||||||
#include "../constants/StringConstants.h"
|
#include "../constants/StringConstants.h"
|
||||||
#include "../entities/artifact/ArtifactUtils.h"
|
#include "../entities/artifact/ArtifactUtils.h"
|
||||||
@ -173,13 +174,14 @@ void CGameState::preInit(Services * newServices)
|
|||||||
services = newServices;
|
services = newServices;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CGameState::init(const IMapService * mapService, StartInfo * si, vstd::RNG & randomGenerator, Load::ProgressAccumulator & progressTracking, bool allowSavingRandomMap)
|
void CGameState::init(const IMapService * mapService, StartInfo * si, IGameRandomizer & gameRandomizer, Load::ProgressAccumulator & progressTracking, bool allowSavingRandomMap)
|
||||||
{
|
{
|
||||||
assert(services);
|
assert(services);
|
||||||
assert(cb);
|
assert(cb);
|
||||||
scenarioOps = CMemorySerializer::deepCopy(*si);
|
scenarioOps = CMemorySerializer::deepCopy(*si);
|
||||||
initialOpts = CMemorySerializer::deepCopy(*si);
|
initialOpts = CMemorySerializer::deepCopy(*si);
|
||||||
si = nullptr;
|
si = nullptr;
|
||||||
|
auto & randomGenerator = gameRandomizer.getDefault();
|
||||||
|
|
||||||
switch(scenarioOps->mode)
|
switch(scenarioOps->mode)
|
||||||
{
|
{
|
||||||
@ -206,16 +208,16 @@ void CGameState::init(const IMapService * mapService, StartInfo * si, vstd::RNG
|
|||||||
removeHeroPlaceholders();
|
removeHeroPlaceholders();
|
||||||
initGrailPosition(randomGenerator);
|
initGrailPosition(randomGenerator);
|
||||||
initRandomFactionsForPlayers(randomGenerator);
|
initRandomFactionsForPlayers(randomGenerator);
|
||||||
randomizeMapObjects(randomGenerator);
|
randomizeMapObjects(gameRandomizer);
|
||||||
placeStartingHeroes(randomGenerator);
|
placeStartingHeroes(randomGenerator);
|
||||||
initOwnedObjects();
|
initOwnedObjects();
|
||||||
initDifficulty();
|
initDifficulty();
|
||||||
initHeroes(randomGenerator);
|
initHeroes(gameRandomizer);
|
||||||
initStartingBonus(randomGenerator);
|
initStartingBonus(gameRandomizer);
|
||||||
initTowns(randomGenerator);
|
initTowns(randomGenerator);
|
||||||
initTownNames(randomGenerator);
|
initTownNames(randomGenerator);
|
||||||
placeHeroesInTowns();
|
placeHeroesInTowns();
|
||||||
initMapObjects(randomGenerator);
|
initMapObjects(gameRandomizer);
|
||||||
buildBonusSystemTree();
|
buildBonusSystemTree();
|
||||||
initVisitingAndGarrisonedHeroes();
|
initVisitingAndGarrisonedHeroes();
|
||||||
initFogOfWar();
|
initFogOfWar();
|
||||||
@ -488,12 +490,12 @@ void CGameState::initRandomFactionsForPlayers(vstd::RNG & randomGenerator)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void CGameState::randomizeMapObjects(vstd::RNG & randomGenerator)
|
void CGameState::randomizeMapObjects(IGameRandomizer & gameRandomizer)
|
||||||
{
|
{
|
||||||
logGlobal->debug("\tRandomizing objects");
|
logGlobal->debug("\tRandomizing objects");
|
||||||
for(const auto & object : map->getObjects())
|
for(const auto & object : map->getObjects())
|
||||||
{
|
{
|
||||||
object->pickRandomObject(randomGenerator);
|
object->pickRandomObject(gameRandomizer);
|
||||||
|
|
||||||
//handle Favouring Winds - mark tiles under it
|
//handle Favouring Winds - mark tiles under it
|
||||||
if(object->ID == Obj::FAVORABLE_WINDS)
|
if(object->ID == Obj::FAVORABLE_WINDS)
|
||||||
@ -594,7 +596,7 @@ void CGameState::removeHeroPlaceholders()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void CGameState::initHeroes(vstd::RNG & randomGenerator)
|
void CGameState::initHeroes(IGameRandomizer & gameRandomizer)
|
||||||
{
|
{
|
||||||
//heroes instances initialization
|
//heroes instances initialization
|
||||||
for (auto heroID : map->getHeroesOnMap())
|
for (auto heroID : map->getHeroesOnMap())
|
||||||
@ -605,7 +607,7 @@ void CGameState::initHeroes(vstd::RNG & randomGenerator)
|
|||||||
logGlobal->warn("Hero with uninitialized owner!");
|
logGlobal->warn("Hero with uninitialized owner!");
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
hero->initHero(randomGenerator);
|
hero->initHero(gameRandomizer);
|
||||||
}
|
}
|
||||||
|
|
||||||
// generate boats for all heroes on water
|
// generate boats for all heroes on water
|
||||||
@ -622,7 +624,7 @@ void CGameState::initHeroes(vstd::RNG & randomGenerator)
|
|||||||
{
|
{
|
||||||
auto handler = LIBRARY->objtypeh->getHandlerFor(Obj::BOAT, hero->getBoatType().getNum());
|
auto handler = LIBRARY->objtypeh->getHandlerFor(Obj::BOAT, hero->getBoatType().getNum());
|
||||||
auto boat = std::dynamic_pointer_cast<CGBoat>(handler->create(cb, nullptr));
|
auto boat = std::dynamic_pointer_cast<CGBoat>(handler->create(cb, nullptr));
|
||||||
handler->configureObject(boat.get(), randomGenerator);
|
handler->configureObject(boat.get(), gameRandomizer);
|
||||||
|
|
||||||
boat->setAnchorPos(hero->anchorPos());
|
boat->setAnchorPos(hero->anchorPos());
|
||||||
boat->appearance = handler->getTemplates().front();
|
boat->appearance = handler->getTemplates().front();
|
||||||
@ -648,7 +650,7 @@ void CGameState::initHeroes(vstd::RNG & randomGenerator)
|
|||||||
heroInPool = newHeroPtr.get();
|
heroInPool = newHeroPtr.get();
|
||||||
}
|
}
|
||||||
map->generateUniqueInstanceName(heroInPool);
|
map->generateUniqueInstanceName(heroInPool);
|
||||||
heroInPool->initHero(randomGenerator);
|
heroInPool->initHero(gameRandomizer);
|
||||||
heroesPool->addHeroToPool(htype);
|
heroesPool->addHeroToPool(htype);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -685,7 +687,7 @@ void CGameState::initFogOfWar()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void CGameState::initStartingBonus(vstd::RNG & randomGenerator)
|
void CGameState::initStartingBonus(IGameRandomizer & gameRandomizer)
|
||||||
{
|
{
|
||||||
if (scenarioOps->mode == EStartMode::CAMPAIGN)
|
if (scenarioOps->mode == EStartMode::CAMPAIGN)
|
||||||
return;
|
return;
|
||||||
@ -697,25 +699,25 @@ void CGameState::initStartingBonus(vstd::RNG & randomGenerator)
|
|||||||
{
|
{
|
||||||
//starting bonus
|
//starting bonus
|
||||||
if(scenarioOps->playerInfos[elem.first].bonus == PlayerStartingBonus::RANDOM)
|
if(scenarioOps->playerInfos[elem.first].bonus == PlayerStartingBonus::RANDOM)
|
||||||
scenarioOps->playerInfos[elem.first].bonus = static_cast<PlayerStartingBonus>(randomGenerator.nextInt(2));
|
scenarioOps->playerInfos[elem.first].bonus = static_cast<PlayerStartingBonus>(gameRandomizer.getDefault().nextInt(2));
|
||||||
|
|
||||||
switch(scenarioOps->playerInfos[elem.first].bonus)
|
switch(scenarioOps->playerInfos[elem.first].bonus)
|
||||||
{
|
{
|
||||||
case PlayerStartingBonus::GOLD:
|
case PlayerStartingBonus::GOLD:
|
||||||
elem.second.resources[EGameResID::GOLD] += randomGenerator.nextInt(5, 10) * 100;
|
elem.second.resources[EGameResID::GOLD] += gameRandomizer.getDefault().nextInt(5, 10) * 100;
|
||||||
break;
|
break;
|
||||||
case PlayerStartingBonus::RESOURCE:
|
case PlayerStartingBonus::RESOURCE:
|
||||||
{
|
{
|
||||||
auto res = (*LIBRARY->townh)[scenarioOps->playerInfos[elem.first].castle]->town->primaryRes;
|
auto res = (*LIBRARY->townh)[scenarioOps->playerInfos[elem.first].castle]->town->primaryRes;
|
||||||
if(res == EGameResID::WOOD_AND_ORE)
|
if(res == EGameResID::WOOD_AND_ORE)
|
||||||
{
|
{
|
||||||
int amount = randomGenerator.nextInt(5, 10);
|
int amount = gameRandomizer.getDefault().nextInt(5, 10);
|
||||||
elem.second.resources[EGameResID::WOOD] += amount;
|
elem.second.resources[EGameResID::WOOD] += amount;
|
||||||
elem.second.resources[EGameResID::ORE] += amount;
|
elem.second.resources[EGameResID::ORE] += amount;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
elem.second.resources[res] += randomGenerator.nextInt(3, 6);
|
elem.second.resources[res] += gameRandomizer.getDefault().nextInt(3, 6);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -726,7 +728,7 @@ void CGameState::initStartingBonus(vstd::RNG & randomGenerator)
|
|||||||
logGlobal->error("Cannot give starting artifact - no heroes!");
|
logGlobal->error("Cannot give starting artifact - no heroes!");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
const Artifact * toGive = pickRandomArtifact(randomGenerator, EArtifactClass::ART_TREASURE).toEntity(LIBRARY);
|
const Artifact * toGive = gameRandomizer.rollArtifact(EArtifactClass::ART_TREASURE).toEntity(LIBRARY);
|
||||||
|
|
||||||
CGHeroInstance *hero = elem.second.getHeroes()[0];
|
CGHeroInstance *hero = elem.second.getHeroes()[0];
|
||||||
if(!giveHeroArtifact(hero, toGive->getId()))
|
if(!giveHeroArtifact(hero, toGive->getId()))
|
||||||
@ -923,12 +925,12 @@ void CGameState::initTowns(vstd::RNG & randomGenerator)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void CGameState::initMapObjects(vstd::RNG & randomGenerator)
|
void CGameState::initMapObjects(IGameRandomizer & gameRandomizer)
|
||||||
{
|
{
|
||||||
logGlobal->debug("\tObject initialization");
|
logGlobal->debug("\tObject initialization");
|
||||||
|
|
||||||
for(auto & obj : map->getObjects())
|
for(auto & obj : map->getObjects())
|
||||||
obj->initObj(randomGenerator);
|
obj->initObj(gameRandomizer);
|
||||||
|
|
||||||
logGlobal->debug("\tObject initialization done");
|
logGlobal->debug("\tObject initialization done");
|
||||||
for(auto & q : map->getObjects<CGSeerHut>())
|
for(auto & q : map->getObjects<CGSeerHut>())
|
||||||
@ -1611,71 +1613,6 @@ TeamState::TeamState()
|
|||||||
setNodeType(TEAM);
|
setNodeType(TEAM);
|
||||||
}
|
}
|
||||||
|
|
||||||
ArtifactID CGameState::pickRandomArtifact(vstd::RNG & randomGenerator, std::optional<EArtifactClass> type, std::function<bool(ArtifactID)> accepts)
|
|
||||||
{
|
|
||||||
std::set<ArtifactID> potentialPicks;
|
|
||||||
|
|
||||||
// Select artifacts that satisfy provided criteria
|
|
||||||
for (auto const & artifactID : map->allowedArtifact)
|
|
||||||
{
|
|
||||||
if (!LIBRARY->arth->legalArtifact(artifactID))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
auto const * artifact = artifactID.toArtifact();
|
|
||||||
|
|
||||||
assert(artifact->aClass != EArtifactClass::ART_SPECIAL); // should be filtered out when allowedArtifacts is initialized
|
|
||||||
|
|
||||||
if (type.has_value() && *type != artifact->aClass)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (!accepts(artifact->getId()))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
potentialPicks.insert(artifact->getId());
|
|
||||||
}
|
|
||||||
|
|
||||||
return pickRandomArtifact(randomGenerator, potentialPicks);
|
|
||||||
}
|
|
||||||
|
|
||||||
ArtifactID CGameState::pickRandomArtifact(vstd::RNG & randomGenerator, std::set<ArtifactID> potentialPicks)
|
|
||||||
{
|
|
||||||
// No allowed artifacts at all - give Grail - this can't be banned (hopefully)
|
|
||||||
// FIXME: investigate how such cases are handled by H3 - some heavily customized user-made maps likely rely on H3 behavior
|
|
||||||
if (potentialPicks.empty())
|
|
||||||
{
|
|
||||||
logGlobal->warn("Failed to find artifact that matches requested parameters!");
|
|
||||||
return ArtifactID::GRAIL;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Find how many times least used artifacts were picked by randomizer
|
|
||||||
int leastUsedTimes = std::numeric_limits<int>::max();
|
|
||||||
for (auto const & artifact : potentialPicks)
|
|
||||||
if (allocatedArtifacts[artifact] < leastUsedTimes)
|
|
||||||
leastUsedTimes = allocatedArtifacts[artifact];
|
|
||||||
|
|
||||||
// Pick all artifacts that were used least number of times
|
|
||||||
std::set<ArtifactID> preferredPicks;
|
|
||||||
for (auto const & artifact : potentialPicks)
|
|
||||||
if (allocatedArtifacts[artifact] == leastUsedTimes)
|
|
||||||
preferredPicks.insert(artifact);
|
|
||||||
|
|
||||||
assert(!preferredPicks.empty());
|
|
||||||
|
|
||||||
ArtifactID artID = *RandomGeneratorUtil::nextItem(preferredPicks, randomGenerator);
|
|
||||||
allocatedArtifacts[artID] += 1; // record +1 more usage
|
|
||||||
return artID;
|
|
||||||
}
|
|
||||||
|
|
||||||
ArtifactID CGameState::pickRandomArtifact(vstd::RNG & randomGenerator, std::function<bool(ArtifactID)> accepts)
|
|
||||||
{
|
|
||||||
return pickRandomArtifact(randomGenerator, std::nullopt, std::move(accepts));
|
|
||||||
}
|
|
||||||
|
|
||||||
ArtifactID CGameState::pickRandomArtifact(vstd::RNG & randomGenerator, std::optional<EArtifactClass> type)
|
|
||||||
{
|
|
||||||
return pickRandomArtifact(randomGenerator, type, [](const ArtifactID &) { return true; });
|
|
||||||
}
|
|
||||||
|
|
||||||
CArtifactInstance * CGameState::createScroll(const SpellID & spellId)
|
CArtifactInstance * CGameState::createScroll(const SpellID & spellId)
|
||||||
{
|
{
|
||||||
return map->createScroll(spellId);
|
return map->createScroll(spellId);
|
||||||
|
@ -22,6 +22,7 @@ VCMI_LIB_NAMESPACE_BEGIN
|
|||||||
|
|
||||||
class EVictoryLossCheckResult;
|
class EVictoryLossCheckResult;
|
||||||
class Services;
|
class Services;
|
||||||
|
class IGameRandomizer;
|
||||||
class IMapService;
|
class IMapService;
|
||||||
class CMap;
|
class CMap;
|
||||||
class CSaveFile;
|
class CSaveFile;
|
||||||
@ -54,9 +55,6 @@ public:
|
|||||||
ArtifactInstanceID saveCompatibilityLastAllocatedArtifactID;
|
ArtifactInstanceID saveCompatibilityLastAllocatedArtifactID;
|
||||||
std::vector<std::shared_ptr<CArtifactInstance>> saveCompatibilityUnregisteredArtifacts;
|
std::vector<std::shared_ptr<CArtifactInstance>> saveCompatibilityUnregisteredArtifacts;
|
||||||
|
|
||||||
/// Stores number of times each artifact was placed on map via randomization
|
|
||||||
std::map<ArtifactID, int> allocatedArtifacts;
|
|
||||||
|
|
||||||
/// List of currently ongoing battles
|
/// List of currently ongoing battles
|
||||||
std::vector<std::unique_ptr<BattleInfo>> currentBattles;
|
std::vector<std::unique_ptr<BattleInfo>> currentBattles;
|
||||||
/// ID that can be allocated to next battle
|
/// ID that can be allocated to next battle
|
||||||
@ -76,7 +74,7 @@ public:
|
|||||||
|
|
||||||
void preInit(Services * services);
|
void preInit(Services * services);
|
||||||
|
|
||||||
void init(const IMapService * mapService, StartInfo * si, vstd::RNG & randomGenerator, Load::ProgressAccumulator &, bool allowSavingRandomMap = true);
|
void init(const IMapService * mapService, StartInfo * si, IGameRandomizer & gameRandomizer, Load::ProgressAccumulator &, bool allowSavingRandomMap = true);
|
||||||
void updateOnLoad(StartInfo * si);
|
void updateOnLoad(StartInfo * si);
|
||||||
|
|
||||||
ui32 day; //total number of days in game
|
ui32 day; //total number of days in game
|
||||||
@ -103,12 +101,6 @@ public:
|
|||||||
void calculatePaths(const std::shared_ptr<PathfinderConfig> & config) const override;
|
void calculatePaths(const std::shared_ptr<PathfinderConfig> & config) const override;
|
||||||
std::vector<const CGObjectInstance*> guardingCreatures (int3 pos) const;
|
std::vector<const CGObjectInstance*> guardingCreatures (int3 pos) const;
|
||||||
|
|
||||||
/// Gets a artifact ID randomly and removes the selected artifact from this handler.
|
|
||||||
ArtifactID pickRandomArtifact(vstd::RNG & randomGenerator, std::optional<EArtifactClass> type);
|
|
||||||
ArtifactID pickRandomArtifact(vstd::RNG & randomGenerator, std::function<bool(ArtifactID)> accepts);
|
|
||||||
ArtifactID pickRandomArtifact(vstd::RNG & randomGenerator, std::optional<EArtifactClass> type, std::function<bool(ArtifactID)> accepts);
|
|
||||||
ArtifactID pickRandomArtifact(vstd::RNG & randomGenerator, std::set<ArtifactID> filtered);
|
|
||||||
|
|
||||||
/// Creates instance of spell scroll artifact with provided spell
|
/// Creates instance of spell scroll artifact with provided spell
|
||||||
CArtifactInstance * createScroll(const SpellID & spellId);
|
CArtifactInstance * createScroll(const SpellID & spellId);
|
||||||
|
|
||||||
@ -187,7 +179,11 @@ public:
|
|||||||
h & globalEffects;
|
h & globalEffects;
|
||||||
h & currentRumor;
|
h & currentRumor;
|
||||||
h & campaign;
|
h & campaign;
|
||||||
h & allocatedArtifacts;
|
if (!h.hasFeature(Handler::Version::RANDOMIZATION_REWORK))
|
||||||
|
{
|
||||||
|
std::map<ArtifactID, int> allocatedArtifactsUnused;
|
||||||
|
h & allocatedArtifactsUnused;
|
||||||
|
}
|
||||||
h & statistic;
|
h & statistic;
|
||||||
|
|
||||||
if(!h.saving && h.loadingGamestate)
|
if(!h.saving && h.loadingGamestate)
|
||||||
@ -201,19 +197,19 @@ private:
|
|||||||
void initGrailPosition(vstd::RNG & randomGenerator);
|
void initGrailPosition(vstd::RNG & randomGenerator);
|
||||||
void initRandomFactionsForPlayers(vstd::RNG & randomGenerator);
|
void initRandomFactionsForPlayers(vstd::RNG & randomGenerator);
|
||||||
void initOwnedObjects();
|
void initOwnedObjects();
|
||||||
void randomizeMapObjects(vstd::RNG & randomGenerator);
|
void randomizeMapObjects(IGameRandomizer & gameRandomizer);
|
||||||
void initPlayerStates();
|
void initPlayerStates();
|
||||||
void placeStartingHeroes(vstd::RNG & randomGenerator);
|
void placeStartingHeroes(vstd::RNG & randomGenerator);
|
||||||
void placeStartingHero(const PlayerColor & playerColor, const HeroTypeID & heroTypeId, int3 townPos);
|
void placeStartingHero(const PlayerColor & playerColor, const HeroTypeID & heroTypeId, int3 townPos);
|
||||||
void removeHeroPlaceholders();
|
void removeHeroPlaceholders();
|
||||||
void initDifficulty();
|
void initDifficulty();
|
||||||
void initHeroes(vstd::RNG & randomGenerator);
|
void initHeroes(IGameRandomizer & gameRandomizer);
|
||||||
void placeHeroesInTowns();
|
void placeHeroesInTowns();
|
||||||
void initFogOfWar();
|
void initFogOfWar();
|
||||||
void initStartingBonus(vstd::RNG & randomGenerator);
|
void initStartingBonus(IGameRandomizer & gameRandomizer);
|
||||||
void initTowns(vstd::RNG & randomGenerator);
|
void initTowns(vstd::RNG & randomGenerator);
|
||||||
void initTownNames(vstd::RNG & randomGenerator);
|
void initTownNames(vstd::RNG & randomGenerator);
|
||||||
void initMapObjects(vstd::RNG & randomGenerator);
|
void initMapObjects(IGameRandomizer & gameRandomizer);
|
||||||
void initVisitingAndGarrisonedHeroes();
|
void initVisitingAndGarrisonedHeroes();
|
||||||
void initCampaign();
|
void initCampaign();
|
||||||
|
|
||||||
|
@ -101,11 +101,6 @@ void GameStatePackVisitor::visitAddQuest(AddQuest & pack)
|
|||||||
logNetwork->warn("Warning! Attempt to add duplicated quest");
|
logNetwork->warn("Warning! Attempt to add duplicated quest");
|
||||||
}
|
}
|
||||||
|
|
||||||
void GameStatePackVisitor::visitUpdateArtHandlerLists(UpdateArtHandlerLists & pack)
|
|
||||||
{
|
|
||||||
gs.allocatedArtifacts = pack.allocatedArtifacts;
|
|
||||||
}
|
|
||||||
|
|
||||||
void GameStatePackVisitor::visitChangeFormation(ChangeFormation & pack)
|
void GameStatePackVisitor::visitChangeFormation(ChangeFormation & pack)
|
||||||
{
|
{
|
||||||
gs.getHero(pack.hid)->setFormation(pack.formation);
|
gs.getHero(pack.hid)->setFormation(pack.formation);
|
||||||
@ -1142,7 +1137,7 @@ void GameStatePackVisitor::visitHeroLevelUp(HeroLevelUp & pack)
|
|||||||
{
|
{
|
||||||
auto * hero = gs.getHero(pack.heroId);
|
auto * hero = gs.getHero(pack.heroId);
|
||||||
assert(hero);
|
assert(hero);
|
||||||
hero->levelUp(pack.skills);
|
hero->levelUp();
|
||||||
}
|
}
|
||||||
|
|
||||||
void GameStatePackVisitor::visitCommanderLevelUp(CommanderLevelUp & pack)
|
void GameStatePackVisitor::visitCommanderLevelUp(CommanderLevelUp & pack)
|
||||||
|
@ -84,7 +84,6 @@ public:
|
|||||||
void visitEntitiesChanged(EntitiesChanged & pack) override;
|
void visitEntitiesChanged(EntitiesChanged & pack) override;
|
||||||
void visitSetCommanderProperty(SetCommanderProperty & pack) override;
|
void visitSetCommanderProperty(SetCommanderProperty & pack) override;
|
||||||
void visitAddQuest(AddQuest & pack) override;
|
void visitAddQuest(AddQuest & pack) override;
|
||||||
void visitUpdateArtHandlerLists(UpdateArtHandlerLists & pack) override;
|
|
||||||
void visitChangeFormation(ChangeFormation & pack) override;
|
void visitChangeFormation(ChangeFormation & pack) override;
|
||||||
void visitChangeSpells(ChangeSpells & pack) override;
|
void visitChangeSpells(ChangeSpells & pack) override;
|
||||||
void visitSetAvailableHero(SetAvailableHero & pack) override;
|
void visitSetAvailableHero(SetAvailableHero & pack) override;
|
||||||
|
@ -19,6 +19,7 @@
|
|||||||
#include "JsonBonus.h"
|
#include "JsonBonus.h"
|
||||||
|
|
||||||
#include "../callback/IGameInfoCallback.h"
|
#include "../callback/IGameInfoCallback.h"
|
||||||
|
#include "../callback/IGameRandomizer.h"
|
||||||
#include "../constants/StringConstants.h"
|
#include "../constants/StringConstants.h"
|
||||||
#include "../GameLibrary.h"
|
#include "../GameLibrary.h"
|
||||||
#include "../CCreatureHandler.h"
|
#include "../CCreatureHandler.h"
|
||||||
@ -49,6 +50,12 @@ JsonRandomizationException::JsonRandomizationException(const std::string & messa
|
|||||||
: std::runtime_error(message + " Input was: " + cleanupJson(input))
|
: std::runtime_error(message + " Input was: " + cleanupJson(input))
|
||||||
{}
|
{}
|
||||||
|
|
||||||
|
JsonRandom::JsonRandom(IGameInfoCallback * cb, IGameRandomizer & gameRandomizer)
|
||||||
|
: GameCallbackHolder(cb)
|
||||||
|
, gameRandomizer(gameRandomizer)
|
||||||
|
, rng(gameRandomizer.getDefault())
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
si32 JsonRandom::loadVariable(const std::string & variableGroup, const std::string & value, const Variables & variables, si32 defaultValue)
|
si32 JsonRandom::loadVariable(const std::string & variableGroup, const std::string & value, const Variables & variables, si32 defaultValue)
|
||||||
{
|
{
|
||||||
@ -68,7 +75,7 @@ JsonRandomizationException::JsonRandomizationException(const std::string & messa
|
|||||||
return variables.at(variableID);
|
return variables.at(variableID);
|
||||||
}
|
}
|
||||||
|
|
||||||
si32 JsonRandom::loadValue(const JsonNode & value, vstd::RNG & rng, const Variables & variables, si32 defaultValue)
|
si32 JsonRandom::loadValue(const JsonNode & value, const Variables & variables, si32 defaultValue)
|
||||||
{
|
{
|
||||||
if(value.isNull())
|
if(value.isNull())
|
||||||
return defaultValue;
|
return defaultValue;
|
||||||
@ -82,14 +89,14 @@ JsonRandomizationException::JsonRandomizationException(const std::string & messa
|
|||||||
const auto & vector = value.Vector();
|
const auto & vector = value.Vector();
|
||||||
|
|
||||||
size_t index= rng.nextInt64(0, vector.size()-1);
|
size_t index= rng.nextInt64(0, vector.size()-1);
|
||||||
return loadValue(vector[index], rng, variables, 0);
|
return loadValue(vector[index], variables, 0);
|
||||||
}
|
}
|
||||||
if(value.isStruct())
|
if(value.isStruct())
|
||||||
{
|
{
|
||||||
if (!value["amount"].isNull())
|
if (!value["amount"].isNull())
|
||||||
return loadValue(value["amount"], rng, variables, defaultValue);
|
return loadValue(value["amount"], variables, defaultValue);
|
||||||
si32 min = loadValue(value["min"], rng, variables, 0);
|
si32 min = loadValue(value["min"], variables, 0);
|
||||||
si32 max = loadValue(value["max"], rng, variables, 0);
|
si32 max = loadValue(value["max"], variables, 0);
|
||||||
return rng.nextInt64(min, max);
|
return rng.nextInt64(min, max);
|
||||||
}
|
}
|
||||||
return defaultValue;
|
return defaultValue;
|
||||||
@ -280,25 +287,25 @@ JsonRandomizationException::JsonRandomizationException(const std::string & messa
|
|||||||
return valuesSet;
|
return valuesSet;
|
||||||
}
|
}
|
||||||
|
|
||||||
TResources JsonRandom::loadResources(const JsonNode & value, vstd::RNG & rng, const Variables & variables)
|
TResources JsonRandom::loadResources(const JsonNode & value, const Variables & variables)
|
||||||
{
|
{
|
||||||
TResources ret;
|
TResources ret;
|
||||||
|
|
||||||
if (value.isVector())
|
if (value.isVector())
|
||||||
{
|
{
|
||||||
for (const auto & entry : value.Vector())
|
for (const auto & entry : value.Vector())
|
||||||
ret += loadResource(entry, rng, variables);
|
ret += loadResource(entry, variables);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (size_t i=0; i<GameConstants::RESOURCE_QUANTITY; i++)
|
for (size_t i=0; i<GameConstants::RESOURCE_QUANTITY; i++)
|
||||||
{
|
{
|
||||||
ret[i] = loadValue(value[GameConstants::RESOURCE_NAMES[i]], rng, variables);
|
ret[i] = loadValue(value[GameConstants::RESOURCE_NAMES[i]], variables);
|
||||||
}
|
}
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
TResources JsonRandom::loadResource(const JsonNode & value, vstd::RNG & rng, const Variables & variables)
|
TResources JsonRandom::loadResource(const JsonNode & value, const Variables & variables)
|
||||||
{
|
{
|
||||||
std::set<GameResID> defaultResources{
|
std::set<GameResID> defaultResources{
|
||||||
GameResID::WOOD,
|
GameResID::WOOD,
|
||||||
@ -312,14 +319,14 @@ JsonRandomizationException::JsonRandomizationException(const std::string & messa
|
|||||||
|
|
||||||
std::set<GameResID> potentialPicks = filterKeys(value, defaultResources, variables);
|
std::set<GameResID> potentialPicks = filterKeys(value, defaultResources, variables);
|
||||||
GameResID resourceID = *RandomGeneratorUtil::nextItem(potentialPicks, rng);
|
GameResID resourceID = *RandomGeneratorUtil::nextItem(potentialPicks, rng);
|
||||||
si32 resourceAmount = loadValue(value, rng, variables, 0);
|
si32 resourceAmount = loadValue(value, variables, 0);
|
||||||
|
|
||||||
TResources ret;
|
TResources ret;
|
||||||
ret[resourceID] = resourceAmount;
|
ret[resourceID] = resourceAmount;
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
PrimarySkill JsonRandom::loadPrimary(const JsonNode & value, vstd::RNG & rng, const Variables & variables)
|
PrimarySkill JsonRandom::loadPrimary(const JsonNode & value, const Variables & variables)
|
||||||
{
|
{
|
||||||
std::set<PrimarySkill> defaultSkills{
|
std::set<PrimarySkill> defaultSkills{
|
||||||
PrimarySkill::ATTACK,
|
PrimarySkill::ATTACK,
|
||||||
@ -331,7 +338,7 @@ JsonRandomizationException::JsonRandomizationException(const std::string & messa
|
|||||||
return *RandomGeneratorUtil::nextItem(potentialPicks, rng);
|
return *RandomGeneratorUtil::nextItem(potentialPicks, rng);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<si32> JsonRandom::loadPrimaries(const JsonNode & value, vstd::RNG & rng, const Variables & variables)
|
std::vector<si32> JsonRandom::loadPrimaries(const JsonNode & value, const Variables & variables)
|
||||||
{
|
{
|
||||||
std::vector<si32> ret(GameConstants::PRIMARY_SKILLS, 0);
|
std::vector<si32> ret(GameConstants::PRIMARY_SKILLS, 0);
|
||||||
std::set<PrimarySkill> defaultSkills{
|
std::set<PrimarySkill> defaultSkills{
|
||||||
@ -346,7 +353,7 @@ JsonRandomizationException::JsonRandomizationException(const std::string & messa
|
|||||||
for(const auto & pair : value.Struct())
|
for(const auto & pair : value.Struct())
|
||||||
{
|
{
|
||||||
PrimarySkill id = decodeKey<PrimarySkill>(pair.second.getModScope(), pair.first, variables);
|
PrimarySkill id = decodeKey<PrimarySkill>(pair.second.getModScope(), pair.first, variables);
|
||||||
ret[id.getNum()] += loadValue(pair.second, rng, variables);
|
ret[id.getNum()] += loadValue(pair.second, variables);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if(value.isVector())
|
if(value.isVector())
|
||||||
@ -357,13 +364,13 @@ JsonRandomizationException::JsonRandomizationException(const std::string & messa
|
|||||||
PrimarySkill skillID = *RandomGeneratorUtil::nextItem(potentialPicks, rng);
|
PrimarySkill skillID = *RandomGeneratorUtil::nextItem(potentialPicks, rng);
|
||||||
|
|
||||||
defaultSkills.erase(skillID);
|
defaultSkills.erase(skillID);
|
||||||
ret[skillID.getNum()] += loadValue(element, rng, variables);
|
ret[skillID.getNum()] += loadValue(element, variables);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
SecondarySkill JsonRandom::loadSecondary(const JsonNode & value, vstd::RNG & rng, const Variables & variables)
|
SecondarySkill JsonRandom::loadSecondary(const JsonNode & value, const Variables & variables)
|
||||||
{
|
{
|
||||||
std::set<SecondarySkill> defaultSkills;
|
std::set<SecondarySkill> defaultSkills;
|
||||||
for(const auto & skill : LIBRARY->skillh->objects)
|
for(const auto & skill : LIBRARY->skillh->objects)
|
||||||
@ -374,7 +381,7 @@ JsonRandomizationException::JsonRandomizationException(const std::string & messa
|
|||||||
return *RandomGeneratorUtil::nextItem(potentialPicks, rng);
|
return *RandomGeneratorUtil::nextItem(potentialPicks, rng);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::map<SecondarySkill, si32> JsonRandom::loadSecondaries(const JsonNode & value, vstd::RNG & rng, const Variables & variables)
|
std::map<SecondarySkill, si32> JsonRandom::loadSecondaries(const JsonNode & value, const Variables & variables)
|
||||||
{
|
{
|
||||||
std::map<SecondarySkill, si32> ret;
|
std::map<SecondarySkill, si32> ret;
|
||||||
if(value.isStruct())
|
if(value.isStruct())
|
||||||
@ -382,7 +389,7 @@ JsonRandomizationException::JsonRandomizationException(const std::string & messa
|
|||||||
for(const auto & pair : value.Struct())
|
for(const auto & pair : value.Struct())
|
||||||
{
|
{
|
||||||
SecondarySkill id = decodeKey<SecondarySkill>(pair.second.getModScope(), pair.first, variables);
|
SecondarySkill id = decodeKey<SecondarySkill>(pair.second.getModScope(), pair.first, variables);
|
||||||
ret[id] = loadValue(pair.second, rng, variables);
|
ret[id] = loadValue(pair.second, variables);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if(value.isVector())
|
if(value.isVector())
|
||||||
@ -398,13 +405,13 @@ JsonRandomizationException::JsonRandomizationException(const std::string & messa
|
|||||||
SecondarySkill skillID = *RandomGeneratorUtil::nextItem(potentialPicks, rng);
|
SecondarySkill skillID = *RandomGeneratorUtil::nextItem(potentialPicks, rng);
|
||||||
|
|
||||||
defaultSkills.erase(skillID); //avoid dupicates
|
defaultSkills.erase(skillID); //avoid dupicates
|
||||||
ret[skillID] = loadValue(element, rng, variables);
|
ret[skillID] = loadValue(element, variables);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
ArtifactID JsonRandom::loadArtifact(const JsonNode & value, vstd::RNG & rng, const Variables & variables)
|
ArtifactID JsonRandom::loadArtifact(const JsonNode & value, const Variables & variables)
|
||||||
{
|
{
|
||||||
std::set<ArtifactID> allowedArts;
|
std::set<ArtifactID> allowedArts;
|
||||||
for(const auto & artifact : LIBRARY->arth->objects)
|
for(const auto & artifact : LIBRARY->arth->objects)
|
||||||
@ -413,20 +420,20 @@ JsonRandomizationException::JsonRandomizationException(const std::string & messa
|
|||||||
|
|
||||||
std::set<ArtifactID> potentialPicks = filterKeys(value, allowedArts, variables);
|
std::set<ArtifactID> potentialPicks = filterKeys(value, allowedArts, variables);
|
||||||
|
|
||||||
return cb->gameState().pickRandomArtifact(rng, potentialPicks);
|
return gameRandomizer.rollArtifact(potentialPicks);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<ArtifactID> JsonRandom::loadArtifacts(const JsonNode & value, vstd::RNG & rng, const Variables & variables)
|
std::vector<ArtifactID> JsonRandom::loadArtifacts(const JsonNode & value, const Variables & variables)
|
||||||
{
|
{
|
||||||
std::vector<ArtifactID> ret;
|
std::vector<ArtifactID> ret;
|
||||||
for (const JsonNode & entry : value.Vector())
|
for (const JsonNode & entry : value.Vector())
|
||||||
{
|
{
|
||||||
ret.push_back(loadArtifact(entry, rng, variables));
|
ret.push_back(loadArtifact(entry, variables));
|
||||||
}
|
}
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<ArtifactPosition> JsonRandom::loadArtifactSlots(const JsonNode & value, vstd::RNG & rng, const Variables & variables)
|
std::vector<ArtifactPosition> JsonRandom::loadArtifactSlots(const JsonNode & value, const Variables & variables)
|
||||||
{
|
{
|
||||||
std::set<ArtifactPosition> allowedSlots;
|
std::set<ArtifactPosition> allowedSlots;
|
||||||
for(ArtifactPosition pos(0); pos < ArtifactPosition::BACKPACK_START; ++pos)
|
for(ArtifactPosition pos(0); pos < ArtifactPosition::BACKPACK_START; ++pos)
|
||||||
@ -441,7 +448,7 @@ JsonRandomizationException::JsonRandomizationException(const std::string & messa
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
SpellID JsonRandom::loadSpell(const JsonNode & value, vstd::RNG & rng, const Variables & variables)
|
SpellID JsonRandom::loadSpell(const JsonNode & value, const Variables & variables)
|
||||||
{
|
{
|
||||||
std::set<SpellID> defaultSpells;
|
std::set<SpellID> defaultSpells;
|
||||||
for(const auto & spell : LIBRARY->spellh->objects)
|
for(const auto & spell : LIBRARY->spellh->objects)
|
||||||
@ -458,17 +465,17 @@ JsonRandomizationException::JsonRandomizationException(const std::string & messa
|
|||||||
return *RandomGeneratorUtil::nextItem(potentialPicks, rng);
|
return *RandomGeneratorUtil::nextItem(potentialPicks, rng);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<SpellID> JsonRandom::loadSpells(const JsonNode & value, vstd::RNG & rng, const Variables & variables)
|
std::vector<SpellID> JsonRandom::loadSpells(const JsonNode & value, const Variables & variables)
|
||||||
{
|
{
|
||||||
std::vector<SpellID> ret;
|
std::vector<SpellID> ret;
|
||||||
for (const JsonNode & entry : value.Vector())
|
for (const JsonNode & entry : value.Vector())
|
||||||
{
|
{
|
||||||
ret.push_back(loadSpell(entry, rng, variables));
|
ret.push_back(loadSpell(entry, variables));
|
||||||
}
|
}
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<PlayerColor> JsonRandom::loadColors(const JsonNode & value, vstd::RNG & rng, const Variables & variables)
|
std::vector<PlayerColor> JsonRandom::loadColors(const JsonNode & value, const Variables & variables)
|
||||||
{
|
{
|
||||||
std::vector<PlayerColor> ret;
|
std::vector<PlayerColor> ret;
|
||||||
std::set<PlayerColor> defaultPlayers;
|
std::set<PlayerColor> defaultPlayers;
|
||||||
@ -484,7 +491,7 @@ JsonRandomizationException::JsonRandomizationException(const std::string & messa
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<HeroTypeID> JsonRandom::loadHeroes(const JsonNode & value, vstd::RNG & rng)
|
std::vector<HeroTypeID> JsonRandom::loadHeroes(const JsonNode & value)
|
||||||
{
|
{
|
||||||
std::vector<HeroTypeID> ret;
|
std::vector<HeroTypeID> ret;
|
||||||
for(auto & entry : value.Vector())
|
for(auto & entry : value.Vector())
|
||||||
@ -494,7 +501,7 @@ JsonRandomizationException::JsonRandomizationException(const std::string & messa
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<HeroClassID> JsonRandom::loadHeroClasses(const JsonNode & value, vstd::RNG & rng)
|
std::vector<HeroClassID> JsonRandom::loadHeroClasses(const JsonNode & value)
|
||||||
{
|
{
|
||||||
std::vector<HeroClassID> ret;
|
std::vector<HeroClassID> ret;
|
||||||
for(auto & entry : value.Vector())
|
for(auto & entry : value.Vector())
|
||||||
@ -504,7 +511,7 @@ JsonRandomizationException::JsonRandomizationException(const std::string & messa
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
CStackBasicDescriptor JsonRandom::loadCreature(const JsonNode & value, vstd::RNG & rng, const Variables & variables)
|
CStackBasicDescriptor JsonRandom::loadCreature(const JsonNode & value, const Variables & variables)
|
||||||
{
|
{
|
||||||
CStackBasicDescriptor stack;
|
CStackBasicDescriptor stack;
|
||||||
|
|
||||||
@ -525,7 +532,7 @@ JsonRandomizationException::JsonRandomizationException(const std::string & messa
|
|||||||
throw JsonRandomizationException("Invalid creature picked!", value);
|
throw JsonRandomizationException("Invalid creature picked!", value);
|
||||||
|
|
||||||
stack.setType(pickedCreature.toCreature());
|
stack.setType(pickedCreature.toCreature());
|
||||||
stack.setCount(loadValue(value, rng, variables));
|
stack.setCount(loadValue(value, variables));
|
||||||
if (!value["upgradeChance"].isNull() && !stack.getCreature()->upgrades.empty())
|
if (!value["upgradeChance"].isNull() && !stack.getCreature()->upgrades.empty())
|
||||||
{
|
{
|
||||||
if (int(value["upgradeChance"].Float()) > rng.nextInt(99)) // select random upgrade
|
if (int(value["upgradeChance"].Float()) > rng.nextInt(99)) // select random upgrade
|
||||||
@ -536,12 +543,12 @@ JsonRandomizationException::JsonRandomizationException(const std::string & messa
|
|||||||
return stack;
|
return stack;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<CStackBasicDescriptor> JsonRandom::loadCreatures(const JsonNode & value, vstd::RNG & rng, const Variables & variables)
|
std::vector<CStackBasicDescriptor> JsonRandom::loadCreatures(const JsonNode & value, const Variables & variables)
|
||||||
{
|
{
|
||||||
std::vector<CStackBasicDescriptor> ret;
|
std::vector<CStackBasicDescriptor> ret;
|
||||||
for (const JsonNode & node : value.Vector())
|
for (const JsonNode & node : value.Vector())
|
||||||
{
|
{
|
||||||
ret.push_back(loadCreature(node, rng, variables));
|
ret.push_back(loadCreature(node, variables));
|
||||||
}
|
}
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
@ -26,6 +26,7 @@ using JsonVector = std::vector<JsonNode>;
|
|||||||
struct Bonus;
|
struct Bonus;
|
||||||
struct Component;
|
struct Component;
|
||||||
class CStackBasicDescriptor;
|
class CStackBasicDescriptor;
|
||||||
|
class IGameRandomizer;
|
||||||
|
|
||||||
class JsonRandomizationException : public std::runtime_error
|
class JsonRandomizationException : public std::runtime_error
|
||||||
{
|
{
|
||||||
@ -36,6 +37,8 @@ public:
|
|||||||
|
|
||||||
class JsonRandom : public GameCallbackHolder
|
class JsonRandom : public GameCallbackHolder
|
||||||
{
|
{
|
||||||
|
IGameRandomizer & gameRandomizer;
|
||||||
|
vstd::RNG & rng;
|
||||||
public:
|
public:
|
||||||
using Variables = std::map<std::string, int>;
|
using Variables = std::map<std::string, int>;
|
||||||
|
|
||||||
@ -55,7 +58,7 @@ private:
|
|||||||
si32 loadVariable(const std::string & variableGroup, const std::string & value, const Variables & variables, si32 defaultValue);
|
si32 loadVariable(const std::string & variableGroup, const std::string & value, const Variables & variables, si32 defaultValue);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
using GameCallbackHolder::GameCallbackHolder;
|
JsonRandom(IGameInfoCallback *cb, IGameRandomizer & gameRandomizer);
|
||||||
|
|
||||||
struct RandomStackInfo
|
struct RandomStackInfo
|
||||||
{
|
{
|
||||||
@ -64,29 +67,29 @@ public:
|
|||||||
si32 maxAmount;
|
si32 maxAmount;
|
||||||
};
|
};
|
||||||
|
|
||||||
si32 loadValue(const JsonNode & value, vstd::RNG & rng, const Variables & variables, si32 defaultValue = 0);
|
si32 loadValue(const JsonNode & value, const Variables & variables, si32 defaultValue = 0);
|
||||||
|
|
||||||
TResources loadResources(const JsonNode & value, vstd::RNG & rng, const Variables & variables);
|
TResources loadResources(const JsonNode & value, const Variables & variables);
|
||||||
TResources loadResource(const JsonNode & value, vstd::RNG & rng, const Variables & variables);
|
TResources loadResource(const JsonNode & value, const Variables & variables);
|
||||||
PrimarySkill loadPrimary(const JsonNode & value, vstd::RNG & rng, const Variables & variables);
|
PrimarySkill loadPrimary(const JsonNode & value, const Variables & variables);
|
||||||
std::vector<si32> loadPrimaries(const JsonNode & value, vstd::RNG & rng, const Variables & variables);
|
std::vector<si32> loadPrimaries(const JsonNode & value, const Variables & variables);
|
||||||
SecondarySkill loadSecondary(const JsonNode & value, vstd::RNG & rng, const Variables & variables);
|
SecondarySkill loadSecondary(const JsonNode & value, const Variables & variables);
|
||||||
std::map<SecondarySkill, si32> loadSecondaries(const JsonNode & value, vstd::RNG & rng, const Variables & variables);
|
std::map<SecondarySkill, si32> loadSecondaries(const JsonNode & value, const Variables & variables);
|
||||||
|
|
||||||
ArtifactID loadArtifact(const JsonNode & value, vstd::RNG & rng, const Variables & variables);
|
ArtifactID loadArtifact(const JsonNode & value, const Variables & variables);
|
||||||
std::vector<ArtifactID> loadArtifacts(const JsonNode & value, vstd::RNG & rng, const Variables & variables);
|
std::vector<ArtifactID> loadArtifacts(const JsonNode & value, const Variables & variables);
|
||||||
std::vector<ArtifactPosition> loadArtifactSlots(const JsonNode & value, vstd::RNG & rng, const Variables & variables);
|
std::vector<ArtifactPosition> loadArtifactSlots(const JsonNode & value, const Variables & variables);
|
||||||
|
|
||||||
SpellID loadSpell(const JsonNode & value, vstd::RNG & rng, const Variables & variables);
|
SpellID loadSpell(const JsonNode & value, const Variables & variables);
|
||||||
std::vector<SpellID> loadSpells(const JsonNode & value, vstd::RNG & rng, const Variables & variables);
|
std::vector<SpellID> loadSpells(const JsonNode & value, const Variables & variables);
|
||||||
|
|
||||||
CStackBasicDescriptor loadCreature(const JsonNode & value, vstd::RNG & rng, const Variables & variables);
|
CStackBasicDescriptor loadCreature(const JsonNode & value, const Variables & variables);
|
||||||
std::vector<CStackBasicDescriptor> loadCreatures(const JsonNode & value, vstd::RNG & rng, const Variables & variables);
|
std::vector<CStackBasicDescriptor> loadCreatures(const JsonNode & value, const Variables & variables);
|
||||||
std::vector<RandomStackInfo> evaluateCreatures(const JsonNode & value, const Variables & variables);
|
std::vector<RandomStackInfo> evaluateCreatures(const JsonNode & value, const Variables & variables);
|
||||||
|
|
||||||
std::vector<PlayerColor> loadColors(const JsonNode & value, vstd::RNG & rng, const Variables & variables);
|
std::vector<PlayerColor> loadColors(const JsonNode & value, const Variables & variables);
|
||||||
std::vector<HeroTypeID> loadHeroes(const JsonNode & value, vstd::RNG & rng);
|
std::vector<HeroTypeID> loadHeroes(const JsonNode & value);
|
||||||
std::vector<HeroClassID> loadHeroClasses(const JsonNode & value, vstd::RNG & rng);
|
std::vector<HeroClassID> loadHeroClasses(const JsonNode & value);
|
||||||
|
|
||||||
static std::vector<Bonus> loadBonuses(const JsonNode & value);
|
static std::vector<Bonus> loadBonuses(const JsonNode & value);
|
||||||
};
|
};
|
||||||
|
@ -24,6 +24,7 @@ class ObjectTemplate;
|
|||||||
class CGObjectInstance;
|
class CGObjectInstance;
|
||||||
class IObjectInfo;
|
class IObjectInfo;
|
||||||
class IGameInfoCallback;
|
class IGameInfoCallback;
|
||||||
|
class IGameRandomizer;
|
||||||
|
|
||||||
/// Class responsible for creation of objects of specific type & subtype
|
/// Class responsible for creation of objects of specific type & subtype
|
||||||
class DLL_LINKAGE AObjectTypeHandler : public boost::noncopyable
|
class DLL_LINKAGE AObjectTypeHandler : public boost::noncopyable
|
||||||
@ -122,7 +123,7 @@ public:
|
|||||||
|
|
||||||
/// Configures object properties. Should be re-entrable, resetting state of the object if necessarily
|
/// Configures object properties. Should be re-entrable, resetting state of the object if necessarily
|
||||||
/// This should set remaining properties, including randomized or depending on map
|
/// This should set remaining properties, including randomized or depending on map
|
||||||
virtual void configureObject(CGObjectInstance * object, vstd::RNG & rng) const = 0;
|
virtual void configureObject(CGObjectInstance * object, IGameRandomizer & gameRandomizer) const = 0;
|
||||||
|
|
||||||
/// Returns object configuration, if available. Otherwise returns NULL
|
/// Returns object configuration, if available. Otherwise returns NULL
|
||||||
virtual std::unique_ptr<IObjectInfo> getObjectInfo(std::shared_ptr<const ObjectTemplate> tmpl) const;
|
virtual std::unique_ptr<IObjectInfo> getObjectInfo(std::shared_ptr<const ObjectTemplate> tmpl) const;
|
||||||
|
@ -17,14 +17,14 @@ VCMI_LIB_NAMESPACE_BEGIN
|
|||||||
template<class ObjectType>
|
template<class ObjectType>
|
||||||
class CDefaultObjectTypeHandler : public AObjectTypeHandler
|
class CDefaultObjectTypeHandler : public AObjectTypeHandler
|
||||||
{
|
{
|
||||||
void configureObject(CGObjectInstance * object, vstd::RNG & rng) const final
|
void configureObject(CGObjectInstance * object, IGameRandomizer & gameRandomizer) const final
|
||||||
{
|
{
|
||||||
ObjectType * castedObject = dynamic_cast<ObjectType*>(object);
|
ObjectType * castedObject = dynamic_cast<ObjectType*>(object);
|
||||||
|
|
||||||
if(castedObject == nullptr)
|
if(castedObject == nullptr)
|
||||||
throw std::runtime_error("Unexpected object type!");
|
throw std::runtime_error("Unexpected object type!");
|
||||||
|
|
||||||
randomizeObject(castedObject, rng);
|
randomizeObject(castedObject, gameRandomizer);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::shared_ptr<CGObjectInstance> create(IGameInfoCallback * cb, std::shared_ptr<const ObjectTemplate> tmpl) const final
|
std::shared_ptr<CGObjectInstance> create(IGameInfoCallback * cb, std::shared_ptr<const ObjectTemplate> tmpl) const final
|
||||||
@ -43,7 +43,7 @@ class CDefaultObjectTypeHandler : public AObjectTypeHandler
|
|||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual void initializeObject(ObjectType * object) const {}
|
virtual void initializeObject(ObjectType * object) const {}
|
||||||
virtual void randomizeObject(ObjectType * object, vstd::RNG & rng) const {}
|
virtual void randomizeObject(ObjectType * object, IGameRandomizer & gameRandomizer) const {}
|
||||||
virtual std::shared_ptr<ObjectType> createObject(IGameInfoCallback * cb) const
|
virtual std::shared_ptr<ObjectType> createObject(IGameInfoCallback * cb) const
|
||||||
{
|
{
|
||||||
return std::make_shared<ObjectType>(cb);
|
return std::make_shared<ObjectType>(cb);
|
||||||
|
@ -55,13 +55,13 @@ void CRewardableConstructor::assignBonuses(std::vector<Bonus> & bonuses, MapObje
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Rewardable::Configuration CRewardableConstructor::generateConfiguration(IGameInfoCallback * cb, vstd::RNG & rand, MapObjectID objectID, const std::map<std::string, JsonNode> & presetVariables) const
|
Rewardable::Configuration CRewardableConstructor::generateConfiguration(IGameInfoCallback * cb, IGameRandomizer & gameRandomizer, MapObjectID objectID, const std::map<std::string, JsonNode> & presetVariables) const
|
||||||
{
|
{
|
||||||
Rewardable::Configuration result;
|
Rewardable::Configuration result;
|
||||||
result.variables.preset = presetVariables;
|
result.variables.preset = presetVariables;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
objectInfo.configureObject(result, rand, cb);
|
objectInfo.configureObject(result, gameRandomizer, cb);
|
||||||
}
|
}
|
||||||
catch (const JsonRandomizationException & e)
|
catch (const JsonRandomizationException & e)
|
||||||
{
|
{
|
||||||
@ -78,14 +78,14 @@ Rewardable::Configuration CRewardableConstructor::generateConfiguration(IGameInf
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CRewardableConstructor::configureObject(CGObjectInstance * object, vstd::RNG & rng) const
|
void CRewardableConstructor::configureObject(CGObjectInstance * object, IGameRandomizer & gameRandomizer) const
|
||||||
{
|
{
|
||||||
auto * rewardableObject = dynamic_cast<CRewardableObject*>(object);
|
auto * rewardableObject = dynamic_cast<CRewardableObject*>(object);
|
||||||
|
|
||||||
if (!rewardableObject)
|
if (!rewardableObject)
|
||||||
throw std::runtime_error("Object " + std::to_string(object->getObjGroupIndex()) + ", " + std::to_string(object->getObjTypeIndex()) + " is not a rewardable object!" );
|
throw std::runtime_error("Object " + std::to_string(object->getObjGroupIndex()) + ", " + std::to_string(object->getObjTypeIndex()) + " is not a rewardable object!" );
|
||||||
|
|
||||||
rewardableObject->configuration = generateConfiguration(object->cb, rng, object->ID, rewardableObject->configuration.variables.preset);
|
rewardableObject->configuration = generateConfiguration(object->cb, gameRandomizer, object->ID, rewardableObject->configuration.variables.preset);
|
||||||
rewardableObject->initializeGuards();
|
rewardableObject->initializeGuards();
|
||||||
|
|
||||||
if (rewardableObject->configuration.info.empty())
|
if (rewardableObject->configuration.info.empty())
|
||||||
|
@ -30,11 +30,11 @@ public:
|
|||||||
|
|
||||||
std::shared_ptr<CGObjectInstance> create(IGameInfoCallback * cb, std::shared_ptr<const ObjectTemplate> tmpl = nullptr) const override;
|
std::shared_ptr<CGObjectInstance> create(IGameInfoCallback * cb, std::shared_ptr<const ObjectTemplate> tmpl = nullptr) const override;
|
||||||
|
|
||||||
void configureObject(CGObjectInstance * object, vstd::RNG & rng) const override;
|
void configureObject(CGObjectInstance * object, IGameRandomizer & gameRandomizer) const override;
|
||||||
|
|
||||||
std::unique_ptr<IObjectInfo> getObjectInfo(std::shared_ptr<const ObjectTemplate> tmpl) const override;
|
std::unique_ptr<IObjectInfo> getObjectInfo(std::shared_ptr<const ObjectTemplate> tmpl) const override;
|
||||||
|
|
||||||
Rewardable::Configuration generateConfiguration(IGameInfoCallback * cb, vstd::RNG & rand, MapObjectID objectID, const std::map<std::string, JsonNode> & presetVariables) const;
|
Rewardable::Configuration generateConfiguration(IGameInfoCallback * cb, IGameRandomizer & gameRandomizer, MapObjectID objectID, const std::map<std::string, JsonNode> & presetVariables) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
VCMI_LIB_NAMESPACE_END
|
VCMI_LIB_NAMESPACE_END
|
||||||
|
@ -79,16 +79,16 @@ int ResourceInstanceConstructor::getAmountMultiplier() const
|
|||||||
return config["amountMultiplier"].Integer();
|
return config["amountMultiplier"].Integer();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ResourceInstanceConstructor::randomizeObject(CGResource * object, vstd::RNG & rng) const
|
void ResourceInstanceConstructor::randomizeObject(CGResource * object, IGameRandomizer & gameRandomizer) const
|
||||||
{
|
{
|
||||||
if (object->amount != CGResource::RANDOM_AMOUNT)
|
if (object->amount != CGResource::RANDOM_AMOUNT)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
JsonRandom randomizer(object->cb);
|
JsonRandom randomizer(object->cb, gameRandomizer);
|
||||||
JsonRandom::Variables dummy;
|
JsonRandom::Variables dummy;
|
||||||
|
|
||||||
if (!config["amounts"].isNull())
|
if (!config["amounts"].isNull())
|
||||||
object->amount = randomizer.loadValue(config["amounts"], rng, dummy, 0) * getAmountMultiplier();
|
object->amount = randomizer.loadValue(config["amounts"], dummy, 0) * getAmountMultiplier();
|
||||||
else
|
else
|
||||||
object->amount = 5 * getAmountMultiplier();
|
object->amount = 5 * getAmountMultiplier();
|
||||||
}
|
}
|
||||||
@ -136,7 +136,7 @@ void CTownInstanceConstructor::initializeObject(CGTownInstance * obj) const
|
|||||||
obj->tempOwner = PlayerColor::NEUTRAL;
|
obj->tempOwner = PlayerColor::NEUTRAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CTownInstanceConstructor::randomizeObject(CGTownInstance * object, vstd::RNG & rng) const
|
void CTownInstanceConstructor::randomizeObject(CGTownInstance * object, IGameRandomizer & gameRandomizer) const
|
||||||
{
|
{
|
||||||
auto templ = getOverride(object->cb->getTile(object->pos)->getTerrainID(), object);
|
auto templ = getOverride(object->cb->getTile(object->pos)->getTerrainID(), object);
|
||||||
if(templ)
|
if(templ)
|
||||||
@ -227,7 +227,7 @@ std::shared_ptr<const ObjectTemplate> CHeroInstanceConstructor::getOverride(Terr
|
|||||||
return candidateBase;
|
return candidateBase;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CHeroInstanceConstructor::randomizeObject(CGHeroInstance * object, vstd::RNG & rng) const
|
void CHeroInstanceConstructor::randomizeObject(CGHeroInstance * object, IGameRandomizer & gameRandomizer) const
|
||||||
{
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -340,14 +340,14 @@ const std::set<EMarketMode> & MarketInstanceConstructor::availableModes() const
|
|||||||
return marketModes;
|
return marketModes;
|
||||||
}
|
}
|
||||||
|
|
||||||
void MarketInstanceConstructor::randomizeObject(CGMarket * object, vstd::RNG & rng) const
|
void MarketInstanceConstructor::randomizeObject(CGMarket * object, IGameRandomizer & gameRandomizer) const
|
||||||
{
|
{
|
||||||
JsonRandom randomizer(object->cb);
|
JsonRandom randomizer(object->cb, gameRandomizer);
|
||||||
JsonRandom::Variables emptyVariables;
|
JsonRandom::Variables emptyVariables;
|
||||||
|
|
||||||
if(auto * university = dynamic_cast<CGUniversity *>(object))
|
if(auto * university = dynamic_cast<CGUniversity *>(object))
|
||||||
{
|
{
|
||||||
for(auto skill : randomizer.loadSecondaries(predefinedOffer, rng, emptyVariables))
|
for(auto skill : randomizer.loadSecondaries(predefinedOffer, emptyVariables))
|
||||||
university->skills.push_back(skill.first);
|
university->skills.push_back(skill.first);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -61,7 +61,7 @@ public:
|
|||||||
int getAmountMultiplier() const;
|
int getAmountMultiplier() const;
|
||||||
int getBaseAmount(vstd::RNG & rng) const;
|
int getBaseAmount(vstd::RNG & rng) const;
|
||||||
|
|
||||||
void randomizeObject(CGResource * object, vstd::RNG & rng) const override;
|
void randomizeObject(CGResource * object, IGameRandomizer & gameRandomizer) const override;
|
||||||
};
|
};
|
||||||
|
|
||||||
class CTownInstanceConstructor : public CDefaultObjectTypeHandler<CGTownInstance>
|
class CTownInstanceConstructor : public CDefaultObjectTypeHandler<CGTownInstance>
|
||||||
@ -76,7 +76,7 @@ public:
|
|||||||
std::map<std::string, LogicalExpression<BuildingID>> filters;
|
std::map<std::string, LogicalExpression<BuildingID>> filters;
|
||||||
|
|
||||||
void initializeObject(CGTownInstance * object) const override;
|
void initializeObject(CGTownInstance * object) const override;
|
||||||
void randomizeObject(CGTownInstance * object, vstd::RNG & rng) const override;
|
void randomizeObject(CGTownInstance * object, IGameRandomizer & gameRandomizer) const override;
|
||||||
void afterLoadFinalization() override;
|
void afterLoadFinalization() override;
|
||||||
|
|
||||||
bool hasNameTextID() const override;
|
bool hasNameTextID() const override;
|
||||||
@ -99,7 +99,7 @@ class CHeroInstanceConstructor : public CDefaultObjectTypeHandler<CGHeroInstance
|
|||||||
void initTypeData(const JsonNode & input) override;
|
void initTypeData(const JsonNode & input) override;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
void randomizeObject(CGHeroInstance * object, vstd::RNG & rng) const override;
|
void randomizeObject(CGHeroInstance * object, IGameRandomizer & gameRandomizer) const override;
|
||||||
|
|
||||||
bool hasNameTextID() const override;
|
bool hasNameTextID() const override;
|
||||||
std::string getNameTextID() const override;
|
std::string getNameTextID() const override;
|
||||||
@ -138,7 +138,7 @@ class MarketInstanceConstructor : public CDefaultObjectTypeHandler<CGMarket>
|
|||||||
void initTypeData(const JsonNode & config) override;
|
void initTypeData(const JsonNode & config) override;
|
||||||
public:
|
public:
|
||||||
std::shared_ptr<CGMarket> createObject(IGameInfoCallback * cb) const override;
|
std::shared_ptr<CGMarket> createObject(IGameInfoCallback * cb) const override;
|
||||||
void randomizeObject(CGMarket * object, vstd::RNG & rng) const override;
|
void randomizeObject(CGMarket * object, IGameRandomizer & gameRandomizer) const override;
|
||||||
|
|
||||||
const std::set<EMarketMode> & availableModes() const;
|
const std::set<EMarketMode> & availableModes() const;
|
||||||
bool hasDescription() const;
|
bool hasDescription() const;
|
||||||
|
@ -100,9 +100,9 @@ void DwellingInstanceConstructor::initializeObject(CGDwelling * obj) const
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void DwellingInstanceConstructor::randomizeObject(CGDwelling * dwelling, vstd::RNG &rng) const
|
void DwellingInstanceConstructor::randomizeObject(CGDwelling * dwelling, IGameRandomizer & gameRandomizer) const
|
||||||
{
|
{
|
||||||
JsonRandom randomizer(dwelling->cb);
|
JsonRandom randomizer(dwelling->cb, gameRandomizer);
|
||||||
|
|
||||||
dwelling->creatures.clear();
|
dwelling->creatures.clear();
|
||||||
dwelling->creatures.reserve(availableCreatures.size());
|
dwelling->creatures.reserve(availableCreatures.size());
|
||||||
@ -128,7 +128,7 @@ void DwellingInstanceConstructor::randomizeObject(CGDwelling * dwelling, vstd::R
|
|||||||
{
|
{
|
||||||
//custom guards (eg. Elemental Conflux)
|
//custom guards (eg. Elemental Conflux)
|
||||||
JsonRandom::Variables emptyVariables;
|
JsonRandom::Variables emptyVariables;
|
||||||
for(auto & stack : randomizer.loadCreatures(guards, rng, emptyVariables))
|
for(auto & stack : randomizer.loadCreatures(guards, emptyVariables))
|
||||||
{
|
{
|
||||||
dwelling->putStack(SlotID(dwelling->stacksCount()), std::make_unique<CStackInstance>(dwelling->cb, stack.getId(), stack.getCount()));
|
dwelling->putStack(SlotID(dwelling->stacksCount()), std::make_unique<CStackInstance>(dwelling->cb, stack.getId(), stack.getCount()));
|
||||||
}
|
}
|
||||||
|
@ -34,7 +34,7 @@ public:
|
|||||||
bool hasNameTextID() const override;
|
bool hasNameTextID() const override;
|
||||||
|
|
||||||
void initializeObject(CGDwelling * object) const override;
|
void initializeObject(CGDwelling * object) const override;
|
||||||
void randomizeObject(CGDwelling * object, vstd::RNG & rng) const override;
|
void randomizeObject(CGDwelling * object, IGameRandomizer & gameRandomizer) const override;
|
||||||
|
|
||||||
bool isBannedForRandomDwelling() const;
|
bool isBannedForRandomDwelling() const;
|
||||||
bool producesCreature(const CCreature * crea) const;
|
bool producesCreature(const CCreature * crea) const;
|
||||||
|
@ -17,6 +17,7 @@
|
|||||||
#include "../IGameSettings.h"
|
#include "../IGameSettings.h"
|
||||||
#include "../callback/IGameInfoCallback.h"
|
#include "../callback/IGameInfoCallback.h"
|
||||||
#include "../callback/IGameEventCallback.h"
|
#include "../callback/IGameEventCallback.h"
|
||||||
|
#include "../callback/IGameRandomizer.h"
|
||||||
#include "../gameState/CGameState.h"
|
#include "../gameState/CGameState.h"
|
||||||
#include "../mapObjectConstructors/CObjectClassesHandler.h"
|
#include "../mapObjectConstructors/CObjectClassesHandler.h"
|
||||||
#include "../networkPacks/PacksForClient.h"
|
#include "../networkPacks/PacksForClient.h"
|
||||||
@ -221,33 +222,33 @@ TQuantity CGCreature::getJoiningAmount() const
|
|||||||
return std::max(static_cast<int64_t>(1), getStackCount(SlotID(0)) * cb->getSettings().getInteger(EGameSettings::CREATURES_JOINING_PERCENTAGE) / 100);
|
return std::max(static_cast<int64_t>(1), getStackCount(SlotID(0)) * cb->getSettings().getInteger(EGameSettings::CREATURES_JOINING_PERCENTAGE) / 100);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CGCreature::pickRandomObject(vstd::RNG & rand)
|
void CGCreature::pickRandomObject(IGameRandomizer & gameRandomizer)
|
||||||
{
|
{
|
||||||
switch(ID.toEnum())
|
switch(ID.toEnum())
|
||||||
{
|
{
|
||||||
case MapObjectID::RANDOM_MONSTER:
|
case MapObjectID::RANDOM_MONSTER:
|
||||||
subID = LIBRARY->creh->pickRandomMonster(rand);
|
subID = gameRandomizer.rollCreature();
|
||||||
break;
|
break;
|
||||||
case MapObjectID::RANDOM_MONSTER_L1:
|
case MapObjectID::RANDOM_MONSTER_L1:
|
||||||
subID = LIBRARY->creh->pickRandomMonster(rand, 1);
|
subID = gameRandomizer.rollCreature(1);
|
||||||
break;
|
break;
|
||||||
case MapObjectID::RANDOM_MONSTER_L2:
|
case MapObjectID::RANDOM_MONSTER_L2:
|
||||||
subID = LIBRARY->creh->pickRandomMonster(rand, 2);
|
subID = gameRandomizer.rollCreature(2);
|
||||||
break;
|
break;
|
||||||
case MapObjectID::RANDOM_MONSTER_L3:
|
case MapObjectID::RANDOM_MONSTER_L3:
|
||||||
subID = LIBRARY->creh->pickRandomMonster(rand, 3);
|
subID = gameRandomizer.rollCreature(3);
|
||||||
break;
|
break;
|
||||||
case MapObjectID::RANDOM_MONSTER_L4:
|
case MapObjectID::RANDOM_MONSTER_L4:
|
||||||
subID = LIBRARY->creh->pickRandomMonster(rand, 4);
|
subID = gameRandomizer.rollCreature(4);
|
||||||
break;
|
break;
|
||||||
case MapObjectID::RANDOM_MONSTER_L5:
|
case MapObjectID::RANDOM_MONSTER_L5:
|
||||||
subID = LIBRARY->creh->pickRandomMonster(rand, 5);
|
subID = gameRandomizer.rollCreature(5);
|
||||||
break;
|
break;
|
||||||
case MapObjectID::RANDOM_MONSTER_L6:
|
case MapObjectID::RANDOM_MONSTER_L6:
|
||||||
subID = LIBRARY->creh->pickRandomMonster(rand, 6);
|
subID = gameRandomizer.rollCreature(6);
|
||||||
break;
|
break;
|
||||||
case MapObjectID::RANDOM_MONSTER_L7:
|
case MapObjectID::RANDOM_MONSTER_L7:
|
||||||
subID = LIBRARY->creh->pickRandomMonster(rand, 7);
|
subID = gameRandomizer.rollCreature(7);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -266,7 +267,7 @@ void CGCreature::pickRandomObject(vstd::RNG & rand)
|
|||||||
setType(ID, subID);
|
setType(ID, subID);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CGCreature::initObj(vstd::RNG & rand)
|
void CGCreature::initObj(IGameRandomizer & gameRandomizer)
|
||||||
{
|
{
|
||||||
blockVisit = true;
|
blockVisit = true;
|
||||||
switch(character)
|
switch(character)
|
||||||
@ -275,13 +276,13 @@ void CGCreature::initObj(vstd::RNG & rand)
|
|||||||
character = -4;
|
character = -4;
|
||||||
break;
|
break;
|
||||||
case 1:
|
case 1:
|
||||||
character = rand.nextInt(1, 7);
|
character = gameRandomizer.getDefault().nextInt(1, 7);
|
||||||
break;
|
break;
|
||||||
case 2:
|
case 2:
|
||||||
character = rand.nextInt(1, 10);
|
character = gameRandomizer.getDefault().nextInt(1, 10);
|
||||||
break;
|
break;
|
||||||
case 3:
|
case 3:
|
||||||
character = rand.nextInt(4, 10);
|
character = gameRandomizer.getDefault().nextInt(4, 10);
|
||||||
break;
|
break;
|
||||||
case 4:
|
case 4:
|
||||||
character = 10;
|
character = 10;
|
||||||
@ -292,7 +293,7 @@ void CGCreature::initObj(vstd::RNG & rand)
|
|||||||
const Creature * c = getCreature();
|
const Creature * c = getCreature();
|
||||||
if(stacks[SlotID(0)]->getCount() == 0)
|
if(stacks[SlotID(0)]->getCount() == 0)
|
||||||
{
|
{
|
||||||
stacks[SlotID(0)]->setCount(rand.nextInt(c->getAdvMapAmountMin(), c->getAdvMapAmountMax()));
|
stacks[SlotID(0)]->setCount(gameRandomizer.getDefault().nextInt(c->getAdvMapAmountMin(), c->getAdvMapAmountMax()));
|
||||||
|
|
||||||
if(stacks[SlotID(0)]->getCount() == 0) //armies with 0 creatures are illegal
|
if(stacks[SlotID(0)]->getCount() == 0) //armies with 0 creatures are illegal
|
||||||
{
|
{
|
||||||
@ -305,7 +306,7 @@ void CGCreature::initObj(vstd::RNG & rand)
|
|||||||
refusedJoining = false;
|
refusedJoining = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CGCreature::newTurn(IGameEventCallback & gameEvents) const
|
void CGCreature::newTurn(IGameEventCallback & gameEvents, IGameRandomizer & gameRandomizer) const
|
||||||
{//Works only for stacks of single type of size up to 2 millions
|
{//Works only for stacks of single type of size up to 2 millions
|
||||||
if (!notGrowingTeam)
|
if (!notGrowingTeam)
|
||||||
{
|
{
|
||||||
|
@ -44,9 +44,9 @@ public:
|
|||||||
std::string getPopupText(PlayerColor player) const override;
|
std::string getPopupText(PlayerColor player) const override;
|
||||||
std::string getPopupText(const CGHeroInstance * hero) const override;
|
std::string getPopupText(const CGHeroInstance * hero) const override;
|
||||||
std::vector<Component> getPopupComponents(PlayerColor player) const override;
|
std::vector<Component> getPopupComponents(PlayerColor player) const override;
|
||||||
void initObj(vstd::RNG & rand) override;
|
void initObj(IGameRandomizer & gameRandomizer) override;
|
||||||
void pickRandomObject(vstd::RNG & rand) override;
|
void pickRandomObject(IGameRandomizer & gameRandomizer) override;
|
||||||
void newTurn(IGameEventCallback & gameEvents) const override;
|
void newTurn(IGameEventCallback & gameEvents, IGameRandomizer & gameRandomizer) const override;
|
||||||
void battleFinished(IGameEventCallback & gameEvents, const CGHeroInstance *hero, const BattleResult &result) const override;
|
void battleFinished(IGameEventCallback & gameEvents, const CGHeroInstance *hero, const BattleResult &result) const override;
|
||||||
void blockingDialogAnswered(IGameEventCallback & gameEvents, const CGHeroInstance *hero, int32_t answer) const override;
|
void blockingDialogAnswered(IGameEventCallback & gameEvents, const CGHeroInstance *hero, int32_t answer) const override;
|
||||||
CreatureID getCreatureID() const;
|
CreatureID getCreatureID() const;
|
||||||
|
@ -12,6 +12,7 @@
|
|||||||
#include "CGDwelling.h"
|
#include "CGDwelling.h"
|
||||||
#include "../callback/IGameInfoCallback.h"
|
#include "../callback/IGameInfoCallback.h"
|
||||||
#include "../callback/IGameEventCallback.h"
|
#include "../callback/IGameEventCallback.h"
|
||||||
|
#include "../callback/IGameRandomizer.h"
|
||||||
#include "../serializer/JsonSerializeFormat.h"
|
#include "../serializer/JsonSerializeFormat.h"
|
||||||
#include "../entities/faction/CTownHandler.h"
|
#include "../entities/faction/CTownHandler.h"
|
||||||
#include "../mapping/CMap.h"
|
#include "../mapping/CMap.h"
|
||||||
@ -53,7 +54,7 @@ CGDwelling::CGDwelling(IGameInfoCallback *cb):
|
|||||||
|
|
||||||
CGDwelling::~CGDwelling() = default;
|
CGDwelling::~CGDwelling() = default;
|
||||||
|
|
||||||
FactionID CGDwelling::randomizeFaction(vstd::RNG & rand)
|
FactionID CGDwelling::randomizeFaction(IGameRandomizer & gameRandomizer)
|
||||||
{
|
{
|
||||||
if (ID == Obj::RANDOM_DWELLING_FACTION)
|
if (ID == Obj::RANDOM_DWELLING_FACTION)
|
||||||
return FactionID(subID.getNum());
|
return FactionID(subID.getNum());
|
||||||
@ -90,7 +91,7 @@ FactionID CGDwelling::randomizeFaction(vstd::RNG & rand)
|
|||||||
if (linkedTown)
|
if (linkedTown)
|
||||||
{
|
{
|
||||||
if(linkedTown->ID==Obj::RANDOM_TOWN)
|
if(linkedTown->ID==Obj::RANDOM_TOWN)
|
||||||
linkedTown->pickRandomObject(rand); //we have to randomize the castle first
|
linkedTown->pickRandomObject(gameRandomizer); //we have to randomize the castle first
|
||||||
|
|
||||||
assert(linkedTown->ID == Obj::TOWN);
|
assert(linkedTown->ID == Obj::TOWN);
|
||||||
if(linkedTown->ID==Obj::TOWN)
|
if(linkedTown->ID==Obj::TOWN)
|
||||||
@ -98,7 +99,7 @@ FactionID CGDwelling::randomizeFaction(vstd::RNG & rand)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if(!randomizationInfo->allowedFactions.empty())
|
if(!randomizationInfo->allowedFactions.empty())
|
||||||
return *RandomGeneratorUtil::nextItem(randomizationInfo->allowedFactions, rand);
|
return *RandomGeneratorUtil::nextItem(randomizationInfo->allowedFactions, gameRandomizer.getDefault());
|
||||||
|
|
||||||
|
|
||||||
std::vector<FactionID> potentialPicks;
|
std::vector<FactionID> potentialPicks;
|
||||||
@ -108,7 +109,7 @@ FactionID CGDwelling::randomizeFaction(vstd::RNG & rand)
|
|||||||
potentialPicks.push_back(faction);
|
potentialPicks.push_back(faction);
|
||||||
|
|
||||||
assert(!potentialPicks.empty());
|
assert(!potentialPicks.empty());
|
||||||
return *RandomGeneratorUtil::nextItem(potentialPicks, rand);
|
return *RandomGeneratorUtil::nextItem(potentialPicks, gameRandomizer.getDefault());
|
||||||
}
|
}
|
||||||
|
|
||||||
int CGDwelling::randomizeLevel(vstd::RNG & rand)
|
int CGDwelling::randomizeLevel(vstd::RNG & rand)
|
||||||
@ -128,12 +129,12 @@ int CGDwelling::randomizeLevel(vstd::RNG & rand)
|
|||||||
return rand.nextInt(randomizationInfo->minLevel, randomizationInfo->maxLevel) - 1;
|
return rand.nextInt(randomizationInfo->minLevel, randomizationInfo->maxLevel) - 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CGDwelling::pickRandomObject(vstd::RNG & rand)
|
void CGDwelling::pickRandomObject(IGameRandomizer & gameRandomizer)
|
||||||
{
|
{
|
||||||
if (ID == Obj::RANDOM_DWELLING || ID == Obj::RANDOM_DWELLING_LVL || ID == Obj::RANDOM_DWELLING_FACTION)
|
if (ID == Obj::RANDOM_DWELLING || ID == Obj::RANDOM_DWELLING_LVL || ID == Obj::RANDOM_DWELLING_FACTION)
|
||||||
{
|
{
|
||||||
FactionID faction = randomizeFaction(rand);
|
FactionID faction = randomizeFaction(gameRandomizer);
|
||||||
int level = randomizeLevel(rand);
|
int level = randomizeLevel(gameRandomizer.getDefault());
|
||||||
assert(faction != FactionID::NONE && faction != FactionID::NEUTRAL);
|
assert(faction != FactionID::NONE && faction != FactionID::NEUTRAL);
|
||||||
assert(level >= 0 && level <= 6);
|
assert(level >= 0 && level <= 6);
|
||||||
randomizationInfo.reset();
|
randomizationInfo.reset();
|
||||||
@ -168,14 +169,14 @@ void CGDwelling::pickRandomObject(vstd::RNG & rand)
|
|||||||
{
|
{
|
||||||
logGlobal->error("Error: failed to find dwelling for %s of level %d", (*LIBRARY->townh)[faction]->getNameTranslated(), int(level));
|
logGlobal->error("Error: failed to find dwelling for %s of level %d", (*LIBRARY->townh)[faction]->getNameTranslated(), int(level));
|
||||||
ID = Obj::CREATURE_GENERATOR1;
|
ID = Obj::CREATURE_GENERATOR1;
|
||||||
subID = *RandomGeneratorUtil::nextItem(LIBRARY->objtypeh->knownSubObjects(Obj::CREATURE_GENERATOR1), rand);
|
subID = *RandomGeneratorUtil::nextItem(LIBRARY->objtypeh->knownSubObjects(Obj::CREATURE_GENERATOR1), gameRandomizer.getDefault());
|
||||||
}
|
}
|
||||||
|
|
||||||
setType(ID, subID);
|
setType(ID, subID);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void CGDwelling::initObj(vstd::RNG & rand)
|
void CGDwelling::initObj(IGameRandomizer & gameRandomizer)
|
||||||
{
|
{
|
||||||
switch(ID.toEnum())
|
switch(ID.toEnum())
|
||||||
{
|
{
|
||||||
@ -183,7 +184,7 @@ void CGDwelling::initObj(vstd::RNG & rand)
|
|||||||
case Obj::CREATURE_GENERATOR4:
|
case Obj::CREATURE_GENERATOR4:
|
||||||
case Obj::WAR_MACHINE_FACTORY:
|
case Obj::WAR_MACHINE_FACTORY:
|
||||||
{
|
{
|
||||||
getObjectHandler()->configureObject(this, rand);
|
getObjectHandler()->configureObject(this, gameRandomizer);
|
||||||
assert(!creatures.empty());
|
assert(!creatures.empty());
|
||||||
assert(!creatures[0].second.empty());
|
assert(!creatures[0].second.empty());
|
||||||
break;
|
break;
|
||||||
@ -278,7 +279,7 @@ void CGDwelling::onHeroVisit(IGameEventCallback & gameEvents, const CGHeroInstan
|
|||||||
gameEvents.showBlockingDialog(this, &bd);
|
gameEvents.showBlockingDialog(this, &bd);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CGDwelling::newTurn(IGameEventCallback & gameEvents) const
|
void CGDwelling::newTurn(IGameEventCallback & gameEvents, IGameRandomizer & gameRandomizer) const
|
||||||
{
|
{
|
||||||
if(cb->getDate(Date::DAY_OF_WEEK) != 1) //not first day of week
|
if(cb->getDate(Date::DAY_OF_WEEK) != 1) //not first day of week
|
||||||
return;
|
return;
|
||||||
@ -289,7 +290,7 @@ void CGDwelling::newTurn(IGameEventCallback & gameEvents) const
|
|||||||
|
|
||||||
if(ID == Obj::REFUGEE_CAMP) //if it's a refugee camp, we need to pick an available creature
|
if(ID == Obj::REFUGEE_CAMP) //if it's a refugee camp, we need to pick an available creature
|
||||||
{
|
{
|
||||||
gameEvents.setObjPropertyID(id, ObjProperty::AVAILABLE_CREATURE, LIBRARY->creh->pickRandomMonster(gameEvents.getRandomGenerator()));
|
gameEvents.setObjPropertyID(id, ObjProperty::AVAILABLE_CREATURE, gameRandomizer.rollCreature());
|
||||||
}
|
}
|
||||||
|
|
||||||
bool change = false;
|
bool change = false;
|
||||||
|
@ -50,13 +50,13 @@ protected:
|
|||||||
void serializeJsonOptions(JsonSerializeFormat & handler) override;
|
void serializeJsonOptions(JsonSerializeFormat & handler) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
FactionID randomizeFaction(vstd::RNG & rand);
|
FactionID randomizeFaction(IGameRandomizer & gameRandomizer);
|
||||||
int randomizeLevel(vstd::RNG & rand);
|
int randomizeLevel(vstd::RNG & rand);
|
||||||
|
|
||||||
void pickRandomObject(vstd::RNG & rand) override;
|
void pickRandomObject(IGameRandomizer & gameRandomizer) override;
|
||||||
void initObj(vstd::RNG & rand) override;
|
void initObj(IGameRandomizer & gameRandomizer) override;
|
||||||
void onHeroVisit(IGameEventCallback & gameEvents, const CGHeroInstance * h) const override;
|
void onHeroVisit(IGameEventCallback & gameEvents, const CGHeroInstance * h) const override;
|
||||||
void newTurn(IGameEventCallback & gameEvents) const override;
|
void newTurn(IGameEventCallback & gameEvents, IGameRandomizer & gameRandomizer) const override;
|
||||||
void setPropertyDer(ObjProperty what, ObjPropertyID identifier) override;
|
void setPropertyDer(ObjProperty what, ObjPropertyID identifier) override;
|
||||||
void battleFinished(IGameEventCallback & gameEvents, const CGHeroInstance *hero, const BattleResult &result) const override;
|
void battleFinished(IGameEventCallback & gameEvents, const CGHeroInstance *hero, const BattleResult &result) const override;
|
||||||
void blockingDialogAnswered(IGameEventCallback & gameEvents, const CGHeroInstance *hero, int32_t answer) const override;
|
void blockingDialogAnswered(IGameEventCallback & gameEvents, const CGHeroInstance *hero, int32_t answer) const override;
|
||||||
|
@ -17,6 +17,7 @@
|
|||||||
|
|
||||||
#include "../callback/IGameInfoCallback.h"
|
#include "../callback/IGameInfoCallback.h"
|
||||||
#include "../callback/IGameEventCallback.h"
|
#include "../callback/IGameEventCallback.h"
|
||||||
|
#include "../callback/IGameRandomizer.h"
|
||||||
#include "../texts/CGeneralTextHandler.h"
|
#include "../texts/CGeneralTextHandler.h"
|
||||||
#include "../TerrainHandler.h"
|
#include "../TerrainHandler.h"
|
||||||
#include "../RoadHandler.h"
|
#include "../RoadHandler.h"
|
||||||
@ -347,16 +348,16 @@ CCommanderInstance * CGHeroInstance::getCommander()
|
|||||||
return commander.get();
|
return commander.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
void CGHeroInstance::initObj(vstd::RNG & rand)
|
void CGHeroInstance::initObj(IGameRandomizer & gameRandomizer)
|
||||||
{
|
{
|
||||||
if (ID == Obj::HERO)
|
if (ID == Obj::HERO)
|
||||||
updateAppearance();
|
updateAppearance();
|
||||||
}
|
}
|
||||||
|
|
||||||
void CGHeroInstance::initHero(vstd::RNG & rand, const HeroTypeID & SUBID)
|
void CGHeroInstance::initHero(IGameRandomizer & gameRandomizer, const HeroTypeID & SUBID)
|
||||||
{
|
{
|
||||||
subID = SUBID.getNum();
|
subID = SUBID.getNum();
|
||||||
initHero(rand);
|
initHero(gameRandomizer);
|
||||||
}
|
}
|
||||||
|
|
||||||
TObjectTypeHandler CGHeroInstance::getObjectHandler() const
|
TObjectTypeHandler CGHeroInstance::getObjectHandler() const
|
||||||
@ -376,7 +377,7 @@ void CGHeroInstance::updateAppearance()
|
|||||||
appearance = app;
|
appearance = app;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CGHeroInstance::initHero(vstd::RNG & rand)
|
void CGHeroInstance::initHero(IGameRandomizer & gameRandomizer)
|
||||||
{
|
{
|
||||||
assert(validTypes(true));
|
assert(validTypes(true));
|
||||||
|
|
||||||
@ -429,7 +430,7 @@ void CGHeroInstance::initHero(vstd::RNG & rand)
|
|||||||
setFormation(EArmyFormation::LOOSE);
|
setFormation(EArmyFormation::LOOSE);
|
||||||
if (!stacksCount()) //standard army//initial army
|
if (!stacksCount()) //standard army//initial army
|
||||||
{
|
{
|
||||||
initArmy(rand);
|
initArmy(gameRandomizer.getDefault());
|
||||||
}
|
}
|
||||||
assert(validTypes());
|
assert(validTypes());
|
||||||
|
|
||||||
@ -438,11 +439,11 @@ void CGHeroInstance::initHero(vstd::RNG & rand)
|
|||||||
|
|
||||||
if(exp == UNINITIALIZED_EXPERIENCE)
|
if(exp == UNINITIALIZED_EXPERIENCE)
|
||||||
{
|
{
|
||||||
initExp(rand);
|
initExp(gameRandomizer.getDefault());
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
levelUpAutomatically(rand);
|
levelUpAutomatically(gameRandomizer);
|
||||||
}
|
}
|
||||||
|
|
||||||
// load base hero bonuses, TODO: per-map loading of base hero bonuses
|
// load base hero bonuses, TODO: per-map loading of base hero bonuses
|
||||||
@ -467,8 +468,6 @@ void CGHeroInstance::initHero(vstd::RNG & rand)
|
|||||||
commander->giveTotalStackExperience(exp); //after our exp is set
|
commander->giveTotalStackExperience(exp); //after our exp is set
|
||||||
}
|
}
|
||||||
|
|
||||||
skillsInfo = SecondarySkillsInfo();
|
|
||||||
|
|
||||||
//copy active (probably growing) bonuses from hero prototype to hero object
|
//copy active (probably growing) bonuses from hero prototype to hero object
|
||||||
for(const std::shared_ptr<Bonus> & b : getHeroType()->specialty)
|
for(const std::shared_ptr<Bonus> & b : getHeroType()->specialty)
|
||||||
addNewBonus(b);
|
addNewBonus(b);
|
||||||
@ -649,27 +648,13 @@ ui8 CGHeroInstance::maxlevelsToWisdom() const
|
|||||||
return getHeroClass()->isMagicHero() ? 3 : 6;
|
return getHeroClass()->isMagicHero() ? 3 : 6;
|
||||||
}
|
}
|
||||||
|
|
||||||
CGHeroInstance::SecondarySkillsInfo::SecondarySkillsInfo():
|
void CGHeroInstance::pickRandomObject(IGameRandomizer & gameRandomizer)
|
||||||
magicSchoolCounter(1),
|
|
||||||
wisdomCounter(1)
|
|
||||||
{}
|
|
||||||
|
|
||||||
void CGHeroInstance::SecondarySkillsInfo::resetMagicSchoolCounter()
|
|
||||||
{
|
|
||||||
magicSchoolCounter = 0;
|
|
||||||
}
|
|
||||||
void CGHeroInstance::SecondarySkillsInfo::resetWisdomCounter()
|
|
||||||
{
|
|
||||||
wisdomCounter = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void CGHeroInstance::pickRandomObject(vstd::RNG & randomGenerator)
|
|
||||||
{
|
{
|
||||||
assert(ID == Obj::HERO || ID == Obj::PRISON || ID == Obj::RANDOM_HERO);
|
assert(ID == Obj::HERO || ID == Obj::PRISON || ID == Obj::RANDOM_HERO);
|
||||||
|
|
||||||
if (ID == Obj::RANDOM_HERO)
|
if (ID == Obj::RANDOM_HERO)
|
||||||
{
|
{
|
||||||
auto selectedHero = cb->gameState().pickNextHeroType(randomGenerator, getOwner());
|
auto selectedHero = cb->gameState().pickNextHeroType(gameRandomizer.getDefault(), getOwner());
|
||||||
|
|
||||||
ID = Obj::HERO;
|
ID = Obj::HERO;
|
||||||
subID = selectedHero;
|
subID = selectedHero;
|
||||||
@ -1425,34 +1410,18 @@ ArtBearer CGHeroInstance::bearerType() const
|
|||||||
return ArtBearer::HERO;
|
return ArtBearer::HERO;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<SecondarySkill> CGHeroInstance::getLevelUpProposedSecondarySkills(vstd::RNG & rand) const
|
std::vector<SecondarySkill> CGHeroInstance::getLevelupSkillCandidates(IGameRandomizer & gameRandomizer) const
|
||||||
{
|
{
|
||||||
auto getObligatorySkills = [](CSkill::Obligatory obl){
|
|
||||||
std::set<SecondarySkill> obligatory;
|
|
||||||
for(auto i = 0; i < LIBRARY->skillh->size(); i++)
|
|
||||||
if((*LIBRARY->skillh)[SecondarySkill(i)]->obligatory(obl))
|
|
||||||
obligatory.insert(i); //Always return all obligatory skills
|
|
||||||
|
|
||||||
return obligatory;
|
|
||||||
};
|
|
||||||
|
|
||||||
auto intersect = [](const std::set<SecondarySkill> & left, const std::set<SecondarySkill> & right)
|
|
||||||
{
|
|
||||||
std::set<SecondarySkill> intersect;
|
|
||||||
std::set_intersection(left.begin(), left.end(), right.begin(), right.end(),
|
|
||||||
std::inserter(intersect, intersect.begin()));
|
|
||||||
return intersect;
|
|
||||||
};
|
|
||||||
|
|
||||||
std::set<SecondarySkill> wisdomList = getObligatorySkills(CSkill::Obligatory::MAJOR);
|
|
||||||
std::set<SecondarySkill> schoolList = getObligatorySkills(CSkill::Obligatory::MINOR);
|
|
||||||
|
|
||||||
std::set<SecondarySkill> basicAndAdv;
|
std::set<SecondarySkill> basicAndAdv;
|
||||||
std::set<SecondarySkill> none;
|
std::set<SecondarySkill> none;
|
||||||
|
std::vector<SecondarySkill> skills;
|
||||||
|
|
||||||
|
if (canLearnSkill())
|
||||||
|
{
|
||||||
for(int i = 0; i < LIBRARY->skillh->size(); i++)
|
for(int i = 0; i < LIBRARY->skillh->size(); i++)
|
||||||
if (canLearnSkill(SecondarySkill(i)))
|
if (canLearnSkill(SecondarySkill(i)))
|
||||||
none.insert(SecondarySkill(i));
|
none.insert(SecondarySkill(i));
|
||||||
|
}
|
||||||
|
|
||||||
for(const auto & elem : secSkills)
|
for(const auto & elem : secSkills)
|
||||||
{
|
{
|
||||||
@ -1461,96 +1430,33 @@ std::vector<SecondarySkill> CGHeroInstance::getLevelUpProposedSecondarySkills(vs
|
|||||||
none.erase(elem.first);
|
none.erase(elem.first);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool wantsWisdom = skillsInfo.wisdomCounter + 1 >= maxlevelsToWisdom();
|
|
||||||
bool wantsSchool = skillsInfo.magicSchoolCounter + 1 >= maxlevelsToMagicSchool();
|
|
||||||
|
|
||||||
std::vector<SecondarySkill> skills;
|
|
||||||
|
|
||||||
auto chooseSkill = [&](std::set<SecondarySkill> & options)
|
|
||||||
{
|
|
||||||
bool selectWisdom = wantsWisdom && !intersect(options, wisdomList).empty();
|
|
||||||
bool selectSchool = wantsSchool && !intersect(options, schoolList).empty();
|
|
||||||
SecondarySkill selection;
|
|
||||||
|
|
||||||
if (selectWisdom)
|
|
||||||
selection = getHeroClass()->chooseSecSkill(intersect(options, wisdomList), rand);
|
|
||||||
else if (selectSchool)
|
|
||||||
selection = getHeroClass()->chooseSecSkill(intersect(options, schoolList), rand);
|
|
||||||
else
|
|
||||||
selection = getHeroClass()->chooseSecSkill(options, rand);
|
|
||||||
|
|
||||||
skills.push_back(selection);
|
|
||||||
options.erase(selection);
|
|
||||||
|
|
||||||
if (wisdomList.count(selection))
|
|
||||||
wisdomList.clear();
|
|
||||||
|
|
||||||
if (schoolList.count(selection))
|
|
||||||
schoolList.clear();
|
|
||||||
};
|
|
||||||
|
|
||||||
if (!basicAndAdv.empty())
|
if (!basicAndAdv.empty())
|
||||||
chooseSkill(basicAndAdv);
|
{
|
||||||
|
skills.push_back(gameRandomizer.rollSecondarySkillForLevelup(this, basicAndAdv));
|
||||||
|
basicAndAdv.erase(skills.back());
|
||||||
|
}
|
||||||
|
|
||||||
if (canLearnSkill() && !none.empty())
|
if (!none.empty())
|
||||||
chooseSkill(none);
|
{
|
||||||
|
skills.push_back(gameRandomizer.rollSecondarySkillForLevelup(this, none));
|
||||||
|
none.erase(skills.back());
|
||||||
|
}
|
||||||
|
|
||||||
if (!basicAndAdv.empty() && skills.size() < 2)
|
if (!basicAndAdv.empty() && skills.size() < 2)
|
||||||
chooseSkill(basicAndAdv);
|
{
|
||||||
|
skills.push_back(gameRandomizer.rollSecondarySkillForLevelup(this, basicAndAdv));
|
||||||
|
basicAndAdv.erase(skills.back());
|
||||||
|
}
|
||||||
|
|
||||||
if (canLearnSkill() && !none.empty() && skills.size() < 2)
|
if (!none.empty() && skills.size() < 2)
|
||||||
chooseSkill(none);
|
{
|
||||||
|
skills.push_back(gameRandomizer.rollSecondarySkillForLevelup(this, none));
|
||||||
|
none.erase(skills.back());
|
||||||
|
}
|
||||||
|
|
||||||
return skills;
|
return skills;
|
||||||
}
|
}
|
||||||
|
|
||||||
PrimarySkill CGHeroInstance::nextPrimarySkill(vstd::RNG & rand) const
|
|
||||||
{
|
|
||||||
assert(gainsLevel());
|
|
||||||
const auto isLowLevelHero = level < GameConstants::HERO_HIGH_LEVEL;
|
|
||||||
const auto & skillChances = isLowLevelHero ? getHeroClass()->primarySkillLowLevel : getHeroClass()->primarySkillHighLevel;
|
|
||||||
|
|
||||||
if (isCampaignYog())
|
|
||||||
{
|
|
||||||
// Yog can only receive Attack or Defence on level-up
|
|
||||||
std::vector<int> yogChances = { skillChances[0], skillChances[1]};
|
|
||||||
return static_cast<PrimarySkill>(RandomGeneratorUtil::nextItemWeighted(yogChances, rand));
|
|
||||||
}
|
|
||||||
|
|
||||||
return static_cast<PrimarySkill>(RandomGeneratorUtil::nextItemWeighted(skillChances, rand));
|
|
||||||
}
|
|
||||||
|
|
||||||
std::optional<SecondarySkill> CGHeroInstance::nextSecondarySkill(vstd::RNG & rand) const
|
|
||||||
{
|
|
||||||
assert(gainsLevel());
|
|
||||||
|
|
||||||
std::optional<SecondarySkill> chosenSecondarySkill;
|
|
||||||
const auto proposedSecondarySkills = getLevelUpProposedSecondarySkills(rand);
|
|
||||||
if(!proposedSecondarySkills.empty())
|
|
||||||
{
|
|
||||||
std::vector<SecondarySkill> learnedSecondarySkills;
|
|
||||||
for(const auto & secondarySkill : proposedSecondarySkills)
|
|
||||||
{
|
|
||||||
if(getSecSkillLevel(secondarySkill) > 0)
|
|
||||||
{
|
|
||||||
learnedSecondarySkills.push_back(secondarySkill);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(learnedSecondarySkills.empty())
|
|
||||||
{
|
|
||||||
// there are only new skills to learn, so choose anyone of them
|
|
||||||
chosenSecondarySkill = std::make_optional(*RandomGeneratorUtil::nextItem(proposedSecondarySkills, rand));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// preferably upgrade a already learned secondary skill
|
|
||||||
chosenSecondarySkill = std::make_optional(*RandomGeneratorUtil::nextItem(learnedSecondarySkills, rand));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return chosenSecondarySkill;
|
|
||||||
}
|
|
||||||
|
|
||||||
void CGHeroInstance::setPrimarySkill(PrimarySkill primarySkill, si64 value, ChangeValueMode mode)
|
void CGHeroInstance::setPrimarySkill(PrimarySkill primarySkill, si64 value, ChangeValueMode mode)
|
||||||
{
|
{
|
||||||
if(primarySkill < PrimarySkill::EXPERIENCE)
|
if(primarySkill < PrimarySkill::EXPERIENCE)
|
||||||
@ -1588,22 +1494,9 @@ bool CGHeroInstance::gainsLevel() const
|
|||||||
return level < LIBRARY->heroh->maxSupportedLevel() && exp >= static_cast<TExpType>(LIBRARY->heroh->reqExp(level+1));
|
return level < LIBRARY->heroh->maxSupportedLevel() && exp >= static_cast<TExpType>(LIBRARY->heroh->reqExp(level+1));
|
||||||
}
|
}
|
||||||
|
|
||||||
void CGHeroInstance::levelUp(const std::vector<SecondarySkill> & skills)
|
void CGHeroInstance::levelUp()
|
||||||
{
|
{
|
||||||
++level;
|
++level;
|
||||||
|
|
||||||
//deterministic secondary skills
|
|
||||||
++skillsInfo.magicSchoolCounter;
|
|
||||||
++skillsInfo.wisdomCounter;
|
|
||||||
|
|
||||||
for(const auto & skill : skills)
|
|
||||||
{
|
|
||||||
if((*LIBRARY->skillh)[skill]->obligatory(CSkill::Obligatory::MAJOR))
|
|
||||||
skillsInfo.resetWisdomCounter();
|
|
||||||
if((*LIBRARY->skillh)[skill]->obligatory(CSkill::Obligatory::MINOR))
|
|
||||||
skillsInfo.resetMagicSchoolCounter();
|
|
||||||
}
|
|
||||||
|
|
||||||
//update specialty and other bonuses that scale with level
|
//update specialty and other bonuses that scale with level
|
||||||
nodeHasChanged();
|
nodeHasChanged();
|
||||||
}
|
}
|
||||||
@ -1614,23 +1507,18 @@ void CGHeroInstance::attachCommanderToArmy()
|
|||||||
commander->setArmy(this);
|
commander->setArmy(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CGHeroInstance::levelUpAutomatically(vstd::RNG & rand)
|
void CGHeroInstance::levelUpAutomatically(IGameRandomizer & gameRandomizer)
|
||||||
{
|
{
|
||||||
while(gainsLevel())
|
while(gainsLevel())
|
||||||
{
|
{
|
||||||
const auto primarySkill = nextPrimarySkill(rand);
|
const auto primarySkill = gameRandomizer.rollPrimarySkillForLevelup(this);
|
||||||
|
const auto proposedSecondarySkills = getLevelupSkillCandidates(gameRandomizer);
|
||||||
|
|
||||||
setPrimarySkill(primarySkill, 1, ChangeValueMode::RELATIVE);
|
setPrimarySkill(primarySkill, 1, ChangeValueMode::RELATIVE);
|
||||||
|
if(!proposedSecondarySkills.empty())
|
||||||
|
setSecSkillLevel(proposedSecondarySkills.front(), 1, ChangeValueMode::RELATIVE);
|
||||||
|
|
||||||
auto proposedSecondarySkills = getLevelUpProposedSecondarySkills(rand);
|
levelUp();
|
||||||
|
|
||||||
const auto secondarySkill = nextSecondarySkill(rand);
|
|
||||||
if(secondarySkill)
|
|
||||||
{
|
|
||||||
setSecSkillLevel(*secondarySkill, 1, ChangeValueMode::RELATIVE);
|
|
||||||
}
|
|
||||||
|
|
||||||
//TODO why has the secondary skills to be passed to the method?
|
|
||||||
levelUp(proposedSecondarySkills);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -116,23 +116,6 @@ public:
|
|||||||
}
|
}
|
||||||
} patrol;
|
} patrol;
|
||||||
|
|
||||||
struct DLL_LINKAGE SecondarySkillsInfo
|
|
||||||
{
|
|
||||||
ui8 magicSchoolCounter;
|
|
||||||
ui8 wisdomCounter;
|
|
||||||
|
|
||||||
SecondarySkillsInfo();
|
|
||||||
|
|
||||||
void resetMagicSchoolCounter();
|
|
||||||
void resetWisdomCounter();
|
|
||||||
|
|
||||||
template <typename Handler> void serialize(Handler &h)
|
|
||||||
{
|
|
||||||
h & magicSchoolCounter;
|
|
||||||
h & wisdomCounter;
|
|
||||||
}
|
|
||||||
} skillsInfo;
|
|
||||||
|
|
||||||
inline bool isInitialized() const
|
inline bool isInitialized() const
|
||||||
{ // has this hero been on the map at least once?
|
{ // has this hero been on the map at least once?
|
||||||
return movement != UNINITIALIZED_MOVEMENT && mana != UNINITIALIZED_MANA;
|
return movement != UNINITIALIZED_MOVEMENT && mana != UNINITIALIZED_MANA;
|
||||||
@ -200,14 +183,8 @@ public:
|
|||||||
/// Returns true if hero has lower level than should upon his experience.
|
/// Returns true if hero has lower level than should upon his experience.
|
||||||
bool gainsLevel() const;
|
bool gainsLevel() const;
|
||||||
|
|
||||||
/// Returns the next primary skill on level up. Can only be called if hero can gain a level up.
|
/// Selects 0-2 skills for player to select on levelup
|
||||||
PrimarySkill nextPrimarySkill(vstd::RNG & rand) const;
|
std::vector<SecondarySkill> getLevelupSkillCandidates(IGameRandomizer & gameRandomizer) const;
|
||||||
|
|
||||||
/// Returns the next secondary skill randomly on level up. Can only be called if hero can gain a level up.
|
|
||||||
std::optional<SecondarySkill> nextSecondarySkill(vstd::RNG & rand) const;
|
|
||||||
|
|
||||||
/// Gets 0, 1 or 2 secondary skills which are proposed on hero level up.
|
|
||||||
std::vector<SecondarySkill> getLevelUpProposedSecondarySkills(vstd::RNG & rand) const;
|
|
||||||
|
|
||||||
ui8 getSecSkillLevel(const SecondarySkill & skill) const; //0 - no skill
|
ui8 getSecSkillLevel(const SecondarySkill & skill) const; //0 - no skill
|
||||||
int getPrimSkillLevel(PrimarySkill id) const;
|
int getPrimSkillLevel(PrimarySkill id) const;
|
||||||
@ -218,7 +195,7 @@ public:
|
|||||||
|
|
||||||
void setPrimarySkill(PrimarySkill primarySkill, si64 value, ChangeValueMode mode);
|
void setPrimarySkill(PrimarySkill primarySkill, si64 value, ChangeValueMode mode);
|
||||||
void setSecSkillLevel(const SecondarySkill & which, int val, ChangeValueMode mode); // abs == 0 - changes by value; 1 - sets to value
|
void setSecSkillLevel(const SecondarySkill & which, int val, ChangeValueMode mode); // abs == 0 - changes by value; 1 - sets to value
|
||||||
void levelUp(const std::vector<SecondarySkill> & skills);
|
void levelUp();
|
||||||
|
|
||||||
void setMovementPoints(int points);
|
void setMovementPoints(int points);
|
||||||
int movementPointsRemaining() const;
|
int movementPointsRemaining() const;
|
||||||
@ -261,9 +238,9 @@ public:
|
|||||||
const CCommanderInstance * getCommander() const;
|
const CCommanderInstance * getCommander() const;
|
||||||
CCommanderInstance * getCommander();
|
CCommanderInstance * getCommander();
|
||||||
|
|
||||||
void initObj(vstd::RNG & rand) override;
|
void initObj(IGameRandomizer & gameRandomizer) override;
|
||||||
void initHero(vstd::RNG & rand);
|
void initHero(IGameRandomizer & gameRandomizer);
|
||||||
void initHero(vstd::RNG & rand, const HeroTypeID & SUBID);
|
void initHero(IGameRandomizer & gameRandomizer, const HeroTypeID & SUBID);
|
||||||
|
|
||||||
ArtPlacementMap putArtifact(const ArtifactPosition & pos, const CArtifactInstance * art) override;
|
ArtPlacementMap putArtifact(const ArtifactPosition & pos, const CArtifactInstance * art) override;
|
||||||
void removeArtifact(const ArtifactPosition & pos) override;
|
void removeArtifact(const ArtifactPosition & pos) override;
|
||||||
@ -320,7 +297,7 @@ public:
|
|||||||
|
|
||||||
void updateAppearance();
|
void updateAppearance();
|
||||||
|
|
||||||
void pickRandomObject(vstd::RNG & rand) override;
|
void pickRandomObject(IGameRandomizer & gameRandomizer) override;
|
||||||
void onHeroVisit(IGameEventCallback & gameEvents, const CGHeroInstance * h) const override;
|
void onHeroVisit(IGameEventCallback & gameEvents, const CGHeroInstance * h) const override;
|
||||||
std::string getObjectName() const override;
|
std::string getObjectName() const override;
|
||||||
std::string getHoverText(PlayerColor player) const override;
|
std::string getHoverText(PlayerColor player) const override;
|
||||||
@ -351,7 +328,7 @@ protected:
|
|||||||
void serializeJsonOptions(JsonSerializeFormat & handler) override;
|
void serializeJsonOptions(JsonSerializeFormat & handler) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void levelUpAutomatically(vstd::RNG & rand);
|
void levelUpAutomatically(IGameRandomizer & gameRandomizer);
|
||||||
void attachCommanderToArmy();
|
void attachCommanderToArmy();
|
||||||
|
|
||||||
public:
|
public:
|
||||||
@ -377,7 +354,14 @@ public:
|
|||||||
h & spells;
|
h & spells;
|
||||||
h & patrol;
|
h & patrol;
|
||||||
h & moveDir;
|
h & moveDir;
|
||||||
h & skillsInfo;
|
if (!h.hasFeature(Handler::Version::RANDOMIZATION_REWORK))
|
||||||
|
{
|
||||||
|
ui8 magicSchoolCounter = 0;
|
||||||
|
ui8 wisdomCounter = 0;
|
||||||
|
|
||||||
|
h & magicSchoolCounter;
|
||||||
|
h & wisdomCounter;
|
||||||
|
}
|
||||||
|
|
||||||
if (h.hasFeature(Handler::Version::NO_RAW_POINTERS_IN_SERIALIZER))
|
if (h.hasFeature(Handler::Version::NO_RAW_POINTERS_IN_SERIALIZER))
|
||||||
{
|
{
|
||||||
|
@ -13,6 +13,7 @@
|
|||||||
|
|
||||||
#include "../callback/IGameInfoCallback.h"
|
#include "../callback/IGameInfoCallback.h"
|
||||||
#include "../callback/IGameEventCallback.h"
|
#include "../callback/IGameEventCallback.h"
|
||||||
|
#include "../callback/IGameRandomizer.h"
|
||||||
#include "../texts/CGeneralTextHandler.h"
|
#include "../texts/CGeneralTextHandler.h"
|
||||||
#include "../CCreatureHandler.h"
|
#include "../CCreatureHandler.h"
|
||||||
#include "CGTownInstance.h"
|
#include "CGTownInstance.h"
|
||||||
@ -30,9 +31,9 @@ ObjectInstanceID CGMarket::getObjInstanceID() const
|
|||||||
return id;
|
return id;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CGMarket::initObj(vstd::RNG & rand)
|
void CGMarket::initObj(IGameRandomizer & gameRandomizer)
|
||||||
{
|
{
|
||||||
getObjectHandler()->configureObject(this, rand);
|
getObjectHandler()->configureObject(this, gameRandomizer);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CGMarket::onHeroVisit(IGameEventCallback & gameEvents, const CGHeroInstance * h) const
|
void CGMarket::onHeroVisit(IGameEventCallback & gameEvents, const CGHeroInstance * h) const
|
||||||
@ -99,7 +100,7 @@ std::vector<TradeItemBuy> CGBlackMarket::availableItemsIds(EMarketMode mode) con
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void CGBlackMarket::newTurn(IGameEventCallback & gameEvents) const
|
void CGBlackMarket::newTurn(IGameEventCallback & gameEvents, IGameRandomizer & gameRandomizer) const
|
||||||
{
|
{
|
||||||
int resetPeriod = cb->getSettings().getInteger(EGameSettings::MARKETS_BLACK_MARKET_RESTOCK_PERIOD);
|
int resetPeriod = cb->getSettings().getInteger(EGameSettings::MARKETS_BLACK_MARKET_RESTOCK_PERIOD);
|
||||||
|
|
||||||
@ -111,7 +112,7 @@ void CGBlackMarket::newTurn(IGameEventCallback & gameEvents) const
|
|||||||
|
|
||||||
SetAvailableArtifacts saa;
|
SetAvailableArtifacts saa;
|
||||||
saa.id = id;
|
saa.id = id;
|
||||||
cb->pickAllowedArtsSet(saa.arts, gameEvents.getRandomGenerator());
|
saa.arts = gameRandomizer.rollMarketArtifactSet();
|
||||||
gameEvents.sendAndApply(saa);
|
gameEvents.sendAndApply(saa);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -25,7 +25,7 @@ public:
|
|||||||
CGMarket(IGameInfoCallback *cb);
|
CGMarket(IGameInfoCallback *cb);
|
||||||
///IObjectInterface
|
///IObjectInterface
|
||||||
void onHeroVisit(IGameEventCallback & gameEvents, const CGHeroInstance * h) const override; //open trading window
|
void onHeroVisit(IGameEventCallback & gameEvents, const CGHeroInstance * h) const override; //open trading window
|
||||||
void initObj(vstd::RNG & rand) override;//set skills for trade
|
void initObj(IGameRandomizer & gameRandomizer) override;//set skills for trade
|
||||||
|
|
||||||
std::string getPopupText(PlayerColor player) const override;
|
std::string getPopupText(PlayerColor player) const override;
|
||||||
std::string getPopupText(const CGHeroInstance * hero) const override;
|
std::string getPopupText(const CGHeroInstance * hero) const override;
|
||||||
@ -44,7 +44,7 @@ public:
|
|||||||
|
|
||||||
std::vector<ArtifactID> artifacts; //available artifacts
|
std::vector<ArtifactID> artifacts; //available artifacts
|
||||||
|
|
||||||
void newTurn(IGameEventCallback & gameEvents) const override; //reset artifacts for black market every month
|
void newTurn(IGameEventCallback & gameEvents, IGameRandomizer & gameRandomizer) const override; //reset artifacts for black market every month
|
||||||
std::vector<TradeItemBuy> availableItemsIds(EMarketMode mode) const override;
|
std::vector<TradeItemBuy> availableItemsIds(EMarketMode mode) const override;
|
||||||
|
|
||||||
template <typename Handler> void serialize(Handler &h)
|
template <typename Handler> void serialize(Handler &h)
|
||||||
|
@ -160,12 +160,12 @@ void CGObjectInstance::setType(MapObjectID newID, MapObjectSubID newSubID)
|
|||||||
cb->gameState().getMap().showObject(this);
|
cb->gameState().getMap().showObject(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CGObjectInstance::pickRandomObject(vstd::RNG & rand)
|
void CGObjectInstance::pickRandomObject(IGameRandomizer & gameRandomizer)
|
||||||
{
|
{
|
||||||
// no-op
|
// no-op
|
||||||
}
|
}
|
||||||
|
|
||||||
void CGObjectInstance::initObj(vstd::RNG & rand)
|
void CGObjectInstance::initObj(IGameRandomizer & gameRandomizer)
|
||||||
{
|
{
|
||||||
// no-op
|
// no-op
|
||||||
}
|
}
|
||||||
|
@ -131,8 +131,8 @@ public:
|
|||||||
|
|
||||||
/** OVERRIDES OF IObjectInterface **/
|
/** OVERRIDES OF IObjectInterface **/
|
||||||
|
|
||||||
void initObj(vstd::RNG & rand) override;
|
void initObj(IGameRandomizer & gameRandomizer) override;
|
||||||
void pickRandomObject(vstd::RNG & rand) override;
|
void pickRandomObject(IGameRandomizer & gameRandomizer) override;
|
||||||
void onHeroVisit(IGameEventCallback & gameEvents, const CGHeroInstance * h) const override;
|
void onHeroVisit(IGameEventCallback & gameEvents, const CGHeroInstance * h) const override;
|
||||||
/// method for synchronous update. Note: For new properties classes should override setPropertyDer instead
|
/// method for synchronous update. Note: For new properties classes should override setPropertyDer instead
|
||||||
void setProperty(ObjProperty what, ObjPropertyID identifier) final;
|
void setProperty(ObjProperty what, ObjPropertyID identifier) final;
|
||||||
|
@ -42,11 +42,11 @@ void CGPandoraBox::init()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void CGPandoraBox::initObj(vstd::RNG & rand)
|
void CGPandoraBox::initObj(IGameRandomizer & gameRandomizer)
|
||||||
{
|
{
|
||||||
init();
|
init();
|
||||||
|
|
||||||
CRewardableObject::initObj(rand);
|
CRewardableObject::initObj(gameRandomizer);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CGPandoraBox::grantRewardWithMessage(IGameEventCallback & gameEvents, const CGHeroInstance * h, int index, bool markAsVisit) const
|
void CGPandoraBox::grantRewardWithMessage(IGameEventCallback & gameEvents, const CGHeroInstance * h, int index, bool markAsVisit) const
|
||||||
|
@ -23,7 +23,7 @@ public:
|
|||||||
|
|
||||||
MetaString message;
|
MetaString message;
|
||||||
|
|
||||||
void initObj(vstd::RNG & rand) override;
|
void initObj(IGameRandomizer & gameRandomizer) override;
|
||||||
void onHeroVisit(IGameEventCallback & gameEvents, const CGHeroInstance * h) const override;
|
void onHeroVisit(IGameEventCallback & gameEvents, const CGHeroInstance * h) const override;
|
||||||
void battleFinished(IGameEventCallback & gameEvents, const CGHeroInstance *hero, const BattleResult &result) const override;
|
void battleFinished(IGameEventCallback & gameEvents, const CGHeroInstance *hero, const BattleResult &result) const override;
|
||||||
void blockingDialogAnswered(IGameEventCallback & gameEvents, const CGHeroInstance *hero, int32_t answer) const override;
|
void blockingDialogAnswered(IGameEventCallback & gameEvents, const CGHeroInstance *hero, int32_t answer) const override;
|
||||||
|
@ -13,6 +13,7 @@
|
|||||||
|
|
||||||
#include "../callback/IGameInfoCallback.h"
|
#include "../callback/IGameInfoCallback.h"
|
||||||
#include "../callback/IGameEventCallback.h"
|
#include "../callback/IGameEventCallback.h"
|
||||||
|
#include "../callback/IGameRandomizer.h"
|
||||||
#include "../mapObjectConstructors/CommonConstructors.h"
|
#include "../mapObjectConstructors/CommonConstructors.h"
|
||||||
#include "../texts/CGeneralTextHandler.h"
|
#include "../texts/CGeneralTextHandler.h"
|
||||||
#include "../networkPacks/PacksForClient.h"
|
#include "../networkPacks/PacksForClient.h"
|
||||||
@ -52,24 +53,24 @@ std::string CGResource::getHoverText(PlayerColor player) const
|
|||||||
return LIBRARY->generaltexth->restypes[resourceID().getNum()];
|
return LIBRARY->generaltexth->restypes[resourceID().getNum()];
|
||||||
}
|
}
|
||||||
|
|
||||||
void CGResource::pickRandomObject(vstd::RNG & rand)
|
void CGResource::pickRandomObject(IGameRandomizer & gameRandomizer)
|
||||||
{
|
{
|
||||||
assert(ID == Obj::RESOURCE || ID == Obj::RANDOM_RESOURCE);
|
assert(ID == Obj::RESOURCE || ID == Obj::RANDOM_RESOURCE);
|
||||||
|
|
||||||
if (ID == Obj::RANDOM_RESOURCE)
|
if (ID == Obj::RANDOM_RESOURCE)
|
||||||
{
|
{
|
||||||
ID = Obj::RESOURCE;
|
ID = Obj::RESOURCE;
|
||||||
subID = rand.nextInt(EGameResID::WOOD, EGameResID::GOLD);
|
subID = gameRandomizer.getDefault().nextInt(EGameResID::WOOD, EGameResID::GOLD);
|
||||||
setType(ID, subID);
|
setType(ID, subID);
|
||||||
|
|
||||||
amount *= getAmountMultiplier();
|
amount *= getAmountMultiplier();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void CGResource::initObj(vstd::RNG & rand)
|
void CGResource::initObj(IGameRandomizer & gameRandomizer)
|
||||||
{
|
{
|
||||||
blockVisit = true;
|
blockVisit = true;
|
||||||
getResourceHandler()->randomizeObject(this, rand);
|
getResourceHandler()->randomizeObject(this, gameRandomizer);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CGResource::onHeroVisit(IGameEventCallback & gameEvents, const CGHeroInstance * h) const
|
void CGResource::onHeroVisit(IGameEventCallback & gameEvents, const CGHeroInstance * h) const
|
||||||
|
@ -35,8 +35,8 @@ public:
|
|||||||
using CArmedInstance::CArmedInstance;
|
using CArmedInstance::CArmedInstance;
|
||||||
|
|
||||||
void onHeroVisit(IGameEventCallback & gameEvents, const CGHeroInstance * h) const override;
|
void onHeroVisit(IGameEventCallback & gameEvents, const CGHeroInstance * h) const override;
|
||||||
void initObj(vstd::RNG & rand) override;
|
void initObj(IGameRandomizer & gameRandomizer) override;
|
||||||
void pickRandomObject(vstd::RNG & rand) override;
|
void pickRandomObject(IGameRandomizer & gameRandomizer) override;
|
||||||
void battleFinished(IGameEventCallback & gameEvents, const CGHeroInstance *hero, const BattleResult &result) const override;
|
void battleFinished(IGameEventCallback & gameEvents, const CGHeroInstance *hero, const BattleResult &result) const override;
|
||||||
void blockingDialogAnswered(IGameEventCallback & gameEvents, const CGHeroInstance *hero, int32_t answer) const override;
|
void blockingDialogAnswered(IGameEventCallback & gameEvents, const CGHeroInstance *hero, int32_t answer) const override;
|
||||||
std::string getHoverText(PlayerColor player) const override;
|
std::string getHoverText(PlayerColor player) const override;
|
||||||
|
@ -26,6 +26,7 @@
|
|||||||
#include "../TerrainHandler.h"
|
#include "../TerrainHandler.h"
|
||||||
#include "../callback/IGameInfoCallback.h"
|
#include "../callback/IGameInfoCallback.h"
|
||||||
#include "../callback/IGameEventCallback.h"
|
#include "../callback/IGameEventCallback.h"
|
||||||
|
#include "../callback/IGameRandomizer.h"
|
||||||
#include "../entities/building/CBuilding.h"
|
#include "../entities/building/CBuilding.h"
|
||||||
#include "../entities/faction/CTownHandler.h"
|
#include "../entities/faction/CTownHandler.h"
|
||||||
#include "../mapObjectConstructors/AObjectTypeHandler.h"
|
#include "../mapObjectConstructors/AObjectTypeHandler.h"
|
||||||
@ -389,7 +390,7 @@ bool CGTownInstance::townEnvisagesBuilding(BuildingSubID::EBuildingSubID subId)
|
|||||||
return getTown()->getBuildingType(subId) != BuildingID::NONE;
|
return getTown()->getBuildingType(subId) != BuildingID::NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CGTownInstance::initializeConfigurableBuildings(vstd::RNG & rand)
|
void CGTownInstance::initializeConfigurableBuildings(IGameRandomizer & gameRandomizer)
|
||||||
{
|
{
|
||||||
for(const auto & kvp : getTown()->buildings)
|
for(const auto & kvp : getTown()->buildings)
|
||||||
{
|
{
|
||||||
@ -397,7 +398,7 @@ void CGTownInstance::initializeConfigurableBuildings(vstd::RNG & rand)
|
|||||||
continue;
|
continue;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
rewardableBuildings[kvp.first] = std::make_unique<TownRewardableBuildingInstance>(this, kvp.second->bid, rand);
|
rewardableBuildings[kvp.first] = std::make_unique<TownRewardableBuildingInstance>(this, kvp.second->bid, gameRandomizer);
|
||||||
}
|
}
|
||||||
catch (std::runtime_error & e)
|
catch (std::runtime_error & e)
|
||||||
{
|
{
|
||||||
@ -458,13 +459,13 @@ FactionID CGTownInstance::randomizeFaction(vstd::RNG & rand)
|
|||||||
return *RandomGeneratorUtil::nextItem(potentialPicks, rand);
|
return *RandomGeneratorUtil::nextItem(potentialPicks, rand);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CGTownInstance::pickRandomObject(vstd::RNG & rand)
|
void CGTownInstance::pickRandomObject(IGameRandomizer & gameRandomizer)
|
||||||
{
|
{
|
||||||
assert(ID == MapObjectID::TOWN || ID == MapObjectID::RANDOM_TOWN);
|
assert(ID == MapObjectID::TOWN || ID == MapObjectID::RANDOM_TOWN);
|
||||||
if (ID == MapObjectID::RANDOM_TOWN)
|
if (ID == MapObjectID::RANDOM_TOWN)
|
||||||
{
|
{
|
||||||
ID = MapObjectID::TOWN;
|
ID = MapObjectID::TOWN;
|
||||||
subID = randomizeFaction(rand);
|
subID = randomizeFaction(gameRandomizer.getDefault());
|
||||||
}
|
}
|
||||||
|
|
||||||
assert(ID == Obj::TOWN); // just in case
|
assert(ID == Obj::TOWN); // just in case
|
||||||
@ -473,7 +474,7 @@ void CGTownInstance::pickRandomObject(vstd::RNG & rand)
|
|||||||
updateAppearance();
|
updateAppearance();
|
||||||
}
|
}
|
||||||
|
|
||||||
void CGTownInstance::initObj(vstd::RNG & rand) ///initialize town structures
|
void CGTownInstance::initObj(IGameRandomizer & gameRandomizer) ///initialize town structures
|
||||||
{
|
{
|
||||||
blockVisit = true;
|
blockVisit = true;
|
||||||
|
|
||||||
@ -493,8 +494,8 @@ void CGTownInstance::initObj(vstd::RNG & rand) ///initialize town structures
|
|||||||
creatures[level].second.push_back(getTown()->creatures[level][upgradeNum]);
|
creatures[level].second.push_back(getTown()->creatures[level][upgradeNum]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
initializeConfigurableBuildings(rand);
|
initializeConfigurableBuildings(gameRandomizer);
|
||||||
initializeNeutralTownGarrison(rand);
|
initializeNeutralTownGarrison(gameRandomizer.getDefault());
|
||||||
recreateBuildingsBonuses();
|
recreateBuildingsBonuses();
|
||||||
updateAppearance();
|
updateAppearance();
|
||||||
}
|
}
|
||||||
@ -536,10 +537,10 @@ void CGTownInstance::initializeNeutralTownGarrison(vstd::RNG & rand)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void CGTownInstance::newTurn(IGameEventCallback & gameEvents) const
|
void CGTownInstance::newTurn(IGameEventCallback & gameEvents, IGameRandomizer & gameRandomizer) const
|
||||||
{
|
{
|
||||||
for(const auto & building : rewardableBuildings)
|
for(const auto & building : rewardableBuildings)
|
||||||
building.second->newTurn(gameEvents);
|
building.second->newTurn(gameEvents, gameRandomizer);
|
||||||
|
|
||||||
if(hasBuilt(BuildingSubID::BANK) && bonusValue.second > 0)
|
if(hasBuilt(BuildingSubID::BANK) && bonusValue.second > 0)
|
||||||
{
|
{
|
||||||
|
@ -211,11 +211,11 @@ public:
|
|||||||
virtual ~CGTownInstance();
|
virtual ~CGTownInstance();
|
||||||
|
|
||||||
///IObjectInterface overrides
|
///IObjectInterface overrides
|
||||||
void newTurn(IGameEventCallback & gameEvents) const override;
|
void newTurn(IGameEventCallback & gameEvents, IGameRandomizer & gameRandomizer) const override;
|
||||||
void onHeroVisit(IGameEventCallback & gameEvents, const CGHeroInstance * h) const override;
|
void onHeroVisit(IGameEventCallback & gameEvents, const CGHeroInstance * h) const override;
|
||||||
void onHeroLeave(IGameEventCallback & gameEvents, const CGHeroInstance * h) const override;
|
void onHeroLeave(IGameEventCallback & gameEvents, const CGHeroInstance * h) const override;
|
||||||
void initObj(vstd::RNG & rand) override;
|
void initObj(IGameRandomizer & gameRandomizer) override;
|
||||||
void pickRandomObject(vstd::RNG & rand) override;
|
void pickRandomObject(IGameRandomizer & gameRandomizer) override;
|
||||||
void battleFinished(IGameEventCallback & gameEvents, const CGHeroInstance * hero, const BattleResult & result) const override;
|
void battleFinished(IGameEventCallback & gameEvents, const CGHeroInstance * hero, const BattleResult & result) const override;
|
||||||
std::string getObjectName() const override;
|
std::string getObjectName() const override;
|
||||||
|
|
||||||
@ -238,7 +238,7 @@ private:
|
|||||||
void onTownCaptured(IGameEventCallback & gameEvents, const PlayerColor & winner) const;
|
void onTownCaptured(IGameEventCallback & gameEvents, const PlayerColor & winner) const;
|
||||||
int getDwellingBonus(const std::vector<CreatureID>& creatureIds, const std::vector<const CGObjectInstance* >& dwellings) const;
|
int getDwellingBonus(const std::vector<CreatureID>& creatureIds, const std::vector<const CGObjectInstance* >& dwellings) const;
|
||||||
bool townEnvisagesBuilding(BuildingSubID::EBuildingSubID bid) const;
|
bool townEnvisagesBuilding(BuildingSubID::EBuildingSubID bid) const;
|
||||||
void initializeConfigurableBuildings(vstd::RNG & rand);
|
void initializeConfigurableBuildings(IGameRandomizer & gameRandomizer);
|
||||||
void initializeNeutralTownGarrison(vstd::RNG & rand);
|
void initializeNeutralTownGarrison(vstd::RNG & rand);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -19,6 +19,7 @@
|
|||||||
#include "../IGameSettings.h"
|
#include "../IGameSettings.h"
|
||||||
#include "../callback/IGameInfoCallback.h"
|
#include "../callback/IGameInfoCallback.h"
|
||||||
#include "../callback/IGameEventCallback.h"
|
#include "../callback/IGameEventCallback.h"
|
||||||
|
#include "../callback/IGameRandomizer.h"
|
||||||
#include "../entities/artifact/CArtifact.h"
|
#include "../entities/artifact/CArtifact.h"
|
||||||
#include "../entities/hero/CHeroHandler.h"
|
#include "../entities/hero/CHeroHandler.h"
|
||||||
#include "../mapObjectConstructors/CObjectClassesHandler.h"
|
#include "../mapObjectConstructors/CObjectClassesHandler.h"
|
||||||
@ -444,11 +445,11 @@ void CGSeerHut::init(vstd::RNG & rand)
|
|||||||
configuration.selectMode = Rewardable::ESelectMode::SELECT_PLAYER;
|
configuration.selectMode = Rewardable::ESelectMode::SELECT_PLAYER;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CGSeerHut::initObj(vstd::RNG & rand)
|
void CGSeerHut::initObj(IGameRandomizer & gameRandomizer)
|
||||||
{
|
{
|
||||||
init(rand);
|
init(gameRandomizer.getDefault());
|
||||||
|
|
||||||
CRewardableObject::initObj(rand);
|
CRewardableObject::initObj(gameRandomizer);
|
||||||
|
|
||||||
setObjToKill();
|
setObjToKill();
|
||||||
getQuest().defineQuestName();
|
getQuest().defineQuestName();
|
||||||
@ -551,9 +552,9 @@ void CGSeerHut::setPropertyDer(ObjProperty what, ObjPropertyID identifier)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void CGSeerHut::newTurn(IGameEventCallback & gameEvents) const
|
void CGSeerHut::newTurn(IGameEventCallback & gameEvents, IGameRandomizer & gameRandomizer) const
|
||||||
{
|
{
|
||||||
CRewardableObject::newTurn(gameEvents);
|
CRewardableObject::newTurn(gameEvents, gameRandomizer);
|
||||||
if(getQuest().lastDay >= 0 && getQuest().lastDay <= cb->getDate() - 1) //time is up
|
if(getQuest().lastDay >= 0 && getQuest().lastDay <= cb->getDate() - 1) //time is up
|
||||||
{
|
{
|
||||||
gameEvents.setObjPropertyValue(id, ObjProperty::SEERHUT_COMPLETE, true);
|
gameEvents.setObjPropertyValue(id, ObjProperty::SEERHUT_COMPLETE, true);
|
||||||
@ -815,7 +816,7 @@ void CGKeymasterTent::onHeroVisit(IGameEventCallback & gameEvents, const CGHeroI
|
|||||||
h->showInfoDialog(gameEvents, txt_id);
|
h->showInfoDialog(gameEvents, txt_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CGBorderGuard::initObj(vstd::RNG & rand)
|
void CGBorderGuard::initObj(IGameRandomizer & gameRandomizer)
|
||||||
{
|
{
|
||||||
blockVisit = true;
|
blockVisit = true;
|
||||||
}
|
}
|
||||||
|
@ -140,14 +140,14 @@ public:
|
|||||||
|
|
||||||
std::string seerName;
|
std::string seerName;
|
||||||
|
|
||||||
void initObj(vstd::RNG & rand) override;
|
void initObj(IGameRandomizer & gameRandomizer) override;
|
||||||
std::string getHoverText(PlayerColor player) const override;
|
std::string getHoverText(PlayerColor player) const override;
|
||||||
std::string getHoverText(const CGHeroInstance * hero) const override;
|
std::string getHoverText(const CGHeroInstance * hero) const override;
|
||||||
std::string getPopupText(PlayerColor player) const override;
|
std::string getPopupText(PlayerColor player) const override;
|
||||||
std::string getPopupText(const CGHeroInstance * hero) const override;
|
std::string getPopupText(const CGHeroInstance * hero) const override;
|
||||||
std::vector<Component> getPopupComponents(PlayerColor player) const override;
|
std::vector<Component> getPopupComponents(PlayerColor player) const override;
|
||||||
std::vector<Component> getPopupComponents(const CGHeroInstance * hero) const override;
|
std::vector<Component> getPopupComponents(const CGHeroInstance * hero) const override;
|
||||||
void newTurn(IGameEventCallback & gameEvents) const override;
|
void newTurn(IGameEventCallback & gameEvents, IGameRandomizer & gameRandomizer) const override;
|
||||||
void onHeroVisit(IGameEventCallback & gameEvents, const CGHeroInstance * h) const override;
|
void onHeroVisit(IGameEventCallback & gameEvents, const CGHeroInstance * h) const override;
|
||||||
void blockingDialogAnswered(IGameEventCallback & gameEvents, const CGHeroInstance *hero, int32_t answer) const override;
|
void blockingDialogAnswered(IGameEventCallback & gameEvents, const CGHeroInstance *hero, int32_t answer) const override;
|
||||||
void getVisitText (MetaString &text, std::vector<Component> &components, bool FirstVisit, const CGHeroInstance * h = nullptr) const override;
|
void getVisitText (MetaString &text, std::vector<Component> &components, bool FirstVisit, const CGHeroInstance * h = nullptr) const override;
|
||||||
@ -227,7 +227,7 @@ class DLL_LINKAGE CGBorderGuard : public CGKeys, public IQuestObject
|
|||||||
public:
|
public:
|
||||||
using CGKeys::CGKeys;
|
using CGKeys::CGKeys;
|
||||||
|
|
||||||
void initObj(vstd::RNG & rand) override;
|
void initObj(IGameRandomizer & gameRandomizer) override;
|
||||||
void onHeroVisit(IGameEventCallback & gameEvents, const CGHeroInstance * h) const override;
|
void onHeroVisit(IGameEventCallback & gameEvents, const CGHeroInstance * h) const override;
|
||||||
void blockingDialogAnswered(IGameEventCallback & gameEvents, const CGHeroInstance *hero, int32_t answer) const override;
|
void blockingDialogAnswered(IGameEventCallback & gameEvents, const CGHeroInstance *hero, int32_t answer) const override;
|
||||||
|
|
||||||
|
@ -331,14 +331,14 @@ void CRewardableObject::setPropertyDer(ObjProperty what, ObjPropertyID identifie
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void CRewardableObject::newTurn(IGameEventCallback & gameEvents) const
|
void CRewardableObject::newTurn(IGameEventCallback & gameEvents, IGameRandomizer & gameRandomizer) const
|
||||||
{
|
{
|
||||||
if (configuration.resetParameters.period != 0 && cb->getDate(Date::DAY) > 1 && ((cb->getDate(Date::DAY)-1) % configuration.resetParameters.period) == 0)
|
if (configuration.resetParameters.period != 0 && cb->getDate(Date::DAY) > 1 && ((cb->getDate(Date::DAY)-1) % configuration.resetParameters.period) == 0)
|
||||||
{
|
{
|
||||||
if (configuration.resetParameters.rewards)
|
if (configuration.resetParameters.rewards)
|
||||||
{
|
{
|
||||||
auto handler = std::dynamic_pointer_cast<const CRewardableConstructor>(getObjectHandler());
|
auto handler = std::dynamic_pointer_cast<const CRewardableConstructor>(getObjectHandler());
|
||||||
auto newConfiguration = handler->generateConfiguration(cb, gameEvents.getRandomGenerator(), ID, configuration.variables.preset);
|
auto newConfiguration = handler->generateConfiguration(cb, gameRandomizer, ID, configuration.variables.preset);
|
||||||
gameEvents.setRewardableObjectConfiguration(id, newConfiguration);
|
gameEvents.setRewardableObjectConfiguration(id, newConfiguration);
|
||||||
}
|
}
|
||||||
if (configuration.resetParameters.visitors)
|
if (configuration.resetParameters.visitors)
|
||||||
@ -350,9 +350,9 @@ void CRewardableObject::newTurn(IGameEventCallback & gameEvents) const
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void CRewardableObject::initObj(vstd::RNG & rand)
|
void CRewardableObject::initObj(IGameRandomizer & gameRandomizer)
|
||||||
{
|
{
|
||||||
getObjectHandler()->configureObject(this, rand);
|
getObjectHandler()->configureObject(this, gameRandomizer);
|
||||||
}
|
}
|
||||||
|
|
||||||
CRewardableObject::CRewardableObject(IGameInfoCallback *cb)
|
CRewardableObject::CRewardableObject(IGameInfoCallback *cb)
|
||||||
|
@ -61,7 +61,7 @@ public:
|
|||||||
void garrisonDialogClosed(IGameEventCallback & gameEvents, const CGHeroInstance *hero) const override;
|
void garrisonDialogClosed(IGameEventCallback & gameEvents, const CGHeroInstance *hero) const override;
|
||||||
|
|
||||||
///possibly resets object state
|
///possibly resets object state
|
||||||
void newTurn(IGameEventCallback & gameEvents) const override;
|
void newTurn(IGameEventCallback & gameEvents, IGameRandomizer & gameRandomizer) const override;
|
||||||
|
|
||||||
/// gives second part of reward after hero level-ups for proper granting of spells/mana
|
/// gives second part of reward after hero level-ups for proper granting of spells/mana
|
||||||
void heroLevelUpDone(IGameEventCallback & gameEvents, const CGHeroInstance *hero) const override;
|
void heroLevelUpDone(IGameEventCallback & gameEvents, const CGHeroInstance *hero) const override;
|
||||||
@ -69,7 +69,7 @@ public:
|
|||||||
/// applies player selection of reward
|
/// applies player selection of reward
|
||||||
void blockingDialogAnswered(IGameEventCallback & gameEvents, const CGHeroInstance *hero, int32_t answer) const override;
|
void blockingDialogAnswered(IGameEventCallback & gameEvents, const CGHeroInstance *hero, int32_t answer) const override;
|
||||||
|
|
||||||
void initObj(vstd::RNG & rand) override;
|
void initObj(IGameRandomizer & gameRandomizer) override;
|
||||||
|
|
||||||
bool isCoastVisitable() const override;
|
bool isCoastVisitable() const override;
|
||||||
|
|
||||||
|
@ -50,7 +50,7 @@ void FlaggableMapObject::onHeroVisit(IGameEventCallback & gameEvents, const CGHe
|
|||||||
gameEvents.showInfoDialog(&iw);
|
gameEvents.showInfoDialog(&iw);
|
||||||
}
|
}
|
||||||
|
|
||||||
void FlaggableMapObject::initObj(vstd::RNG & rand)
|
void FlaggableMapObject::initObj(IGameRandomizer & gameRandomizer)
|
||||||
{
|
{
|
||||||
initBonuses();
|
initBonuses();
|
||||||
}
|
}
|
||||||
|
@ -28,7 +28,7 @@ public:
|
|||||||
using CGObjectInstance::CGObjectInstance;
|
using CGObjectInstance::CGObjectInstance;
|
||||||
|
|
||||||
void onHeroVisit(IGameEventCallback & gameEvents, const CGHeroInstance * h) const override;
|
void onHeroVisit(IGameEventCallback & gameEvents, const CGHeroInstance * h) const override;
|
||||||
void initObj(vstd::RNG & rand) override;
|
void initObj(IGameRandomizer & gameRandomizer) override;
|
||||||
|
|
||||||
const IOwnableObject * asOwnable() const final;
|
const IOwnableObject * asOwnable() const final;
|
||||||
ResourceSet dailyIncome() const override;
|
ResourceSet dailyIncome() const override;
|
||||||
|
@ -39,13 +39,13 @@ void IObjectInterface::onHeroVisit(IGameEventCallback & gameEvents, const CGHero
|
|||||||
void IObjectInterface::onHeroLeave(IGameEventCallback & gameEvents, const CGHeroInstance * h) const
|
void IObjectInterface::onHeroLeave(IGameEventCallback & gameEvents, const CGHeroInstance * h) const
|
||||||
{}
|
{}
|
||||||
|
|
||||||
void IObjectInterface::newTurn(IGameEventCallback & gameEvents) const
|
void IObjectInterface::newTurn(IGameEventCallback & gameEvents, IGameRandomizer & gameRandomizer) const
|
||||||
{}
|
{}
|
||||||
|
|
||||||
void IObjectInterface::initObj(vstd::RNG & rand)
|
void IObjectInterface::initObj(IGameRandomizer & gameRandomizer)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
void IObjectInterface::pickRandomObject(vstd::RNG & rand)
|
void IObjectInterface::pickRandomObject(IGameRandomizer & gameRandomizer)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
void IObjectInterface::setProperty(ObjProperty what, ObjPropertyID identifier)
|
void IObjectInterface::setProperty(ObjProperty what, ObjPropertyID identifier)
|
||||||
|
@ -30,6 +30,7 @@ class CStackInstance;
|
|||||||
class CGHeroInstance;
|
class CGHeroInstance;
|
||||||
class IGameInfoCallback;
|
class IGameInfoCallback;
|
||||||
class IGameEventCallback;
|
class IGameEventCallback;
|
||||||
|
class IGameRandomizer;
|
||||||
class ResourceSet;
|
class ResourceSet;
|
||||||
class int3;
|
class int3;
|
||||||
class MetaString;
|
class MetaString;
|
||||||
@ -55,9 +56,9 @@ public:
|
|||||||
|
|
||||||
/// Called on new turn by server. This method can not modify object state on its own
|
/// Called on new turn by server. This method can not modify object state on its own
|
||||||
/// Instead all changes must be propagated via netpacks
|
/// Instead all changes must be propagated via netpacks
|
||||||
virtual void newTurn(IGameEventCallback & gameEvents) const;
|
virtual void newTurn(IGameEventCallback & gameEvents, IGameRandomizer & gameRandomizer) const;
|
||||||
virtual void initObj(vstd::RNG & rand); //synchr
|
virtual void initObj(IGameRandomizer & gameRandomizer); //synchr
|
||||||
virtual void pickRandomObject(vstd::RNG & rand);
|
virtual void pickRandomObject(IGameRandomizer & gameRandomizer);
|
||||||
virtual void setProperty(ObjProperty what, ObjPropertyID identifier);//synchr
|
virtual void setProperty(ObjProperty what, ObjPropertyID identifier);//synchr
|
||||||
|
|
||||||
//Called when queries created DURING HERO VISIT are resolved
|
//Called when queries created DURING HERO VISIT are resolved
|
||||||
|
@ -14,6 +14,7 @@
|
|||||||
#include "../bonuses/Propagators.h"
|
#include "../bonuses/Propagators.h"
|
||||||
#include "../callback/IGameInfoCallback.h"
|
#include "../callback/IGameInfoCallback.h"
|
||||||
#include "../callback/IGameEventCallback.h"
|
#include "../callback/IGameEventCallback.h"
|
||||||
|
#include "../callback/IGameRandomizer.h"
|
||||||
#include "../constants/StringConstants.h"
|
#include "../constants/StringConstants.h"
|
||||||
#include "../entities/artifact/ArtifactUtils.h"
|
#include "../entities/artifact/ArtifactUtils.h"
|
||||||
#include "../entities/artifact/CArtifact.h"
|
#include "../entities/artifact/CArtifact.h"
|
||||||
@ -96,19 +97,19 @@ void CGMine::onHeroVisit(IGameEventCallback & gameEvents, const CGHeroInstance *
|
|||||||
flagMine(gameEvents, h->tempOwner);
|
flagMine(gameEvents, h->tempOwner);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CGMine::initObj(vstd::RNG & rand)
|
void CGMine::initObj(IGameRandomizer & gameRandomizer)
|
||||||
{
|
{
|
||||||
if(isAbandoned())
|
if(isAbandoned())
|
||||||
{
|
{
|
||||||
//set guardians
|
//set guardians
|
||||||
int howManyTroglodytes = rand.nextInt(100, 199);
|
int howManyTroglodytes = gameRandomizer.getDefault().nextInt(100, 199);
|
||||||
auto troglodytes = std::make_unique<CStackInstance>(cb, CreatureID::TROGLODYTES, howManyTroglodytes);
|
auto troglodytes = std::make_unique<CStackInstance>(cb, CreatureID::TROGLODYTES, howManyTroglodytes);
|
||||||
putStack(SlotID(0), std::move(troglodytes));
|
putStack(SlotID(0), std::move(troglodytes));
|
||||||
|
|
||||||
assert(!abandonedMineResources.empty());
|
assert(!abandonedMineResources.empty());
|
||||||
if (!abandonedMineResources.empty())
|
if (!abandonedMineResources.empty())
|
||||||
{
|
{
|
||||||
producedResource = *RandomGeneratorUtil::nextItem(abandonedMineResources, rand);
|
producedResource = *RandomGeneratorUtil::nextItem(abandonedMineResources, gameRandomizer.getDefault());
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -431,7 +432,7 @@ void CGMonolith::teleportDialogAnswered(IGameEventCallback & gameEvents, const C
|
|||||||
gameEvents.moveHero(hero->id, hero->convertFromVisitablePos(dPos), EMovementMode::MONOLITH);
|
gameEvents.moveHero(hero->id, hero->convertFromVisitablePos(dPos), EMovementMode::MONOLITH);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CGMonolith::initObj(vstd::RNG & rand)
|
void CGMonolith::initObj(IGameRandomizer & gameRandomizer)
|
||||||
{
|
{
|
||||||
std::vector<Obj> IDs;
|
std::vector<Obj> IDs;
|
||||||
IDs.push_back(ID);
|
IDs.push_back(ID);
|
||||||
@ -476,7 +477,7 @@ void CGSubterraneanGate::onHeroVisit(IGameEventCallback & gameEvents, const CGHe
|
|||||||
gameEvents.showTeleportDialog(&td);
|
gameEvents.showTeleportDialog(&td);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CGSubterraneanGate::initObj(vstd::RNG & rand)
|
void CGSubterraneanGate::initObj(IGameRandomizer & gameRandomizer)
|
||||||
{
|
{
|
||||||
type = BOTH;
|
type = BOTH;
|
||||||
}
|
}
|
||||||
@ -625,24 +626,24 @@ ArtifactID CGArtifact::getArtifactType() const
|
|||||||
return getObjTypeIndex().getNum();
|
return getObjTypeIndex().getNum();
|
||||||
}
|
}
|
||||||
|
|
||||||
void CGArtifact::pickRandomObject(vstd::RNG & rand)
|
void CGArtifact::pickRandomObject(IGameRandomizer & gameRandomizer)
|
||||||
{
|
{
|
||||||
switch(ID.toEnum())
|
switch(ID.toEnum())
|
||||||
{
|
{
|
||||||
case MapObjectID::RANDOM_ART:
|
case MapObjectID::RANDOM_ART:
|
||||||
subID = cb->gameState().pickRandomArtifact(rand, std::nullopt);
|
subID = gameRandomizer.rollArtifact();
|
||||||
break;
|
break;
|
||||||
case MapObjectID::RANDOM_TREASURE_ART:
|
case MapObjectID::RANDOM_TREASURE_ART:
|
||||||
subID = cb->gameState().pickRandomArtifact(rand, EArtifactClass::ART_TREASURE);
|
subID = gameRandomizer.rollArtifact(EArtifactClass::ART_TREASURE);
|
||||||
break;
|
break;
|
||||||
case MapObjectID::RANDOM_MINOR_ART:
|
case MapObjectID::RANDOM_MINOR_ART:
|
||||||
subID = cb->gameState().pickRandomArtifact(rand, EArtifactClass::ART_MINOR);
|
subID = gameRandomizer.rollArtifact(EArtifactClass::ART_MINOR);
|
||||||
break;
|
break;
|
||||||
case MapObjectID::RANDOM_MAJOR_ART:
|
case MapObjectID::RANDOM_MAJOR_ART:
|
||||||
subID = cb->gameState().pickRandomArtifact(rand, EArtifactClass::ART_MAJOR);
|
subID = gameRandomizer.rollArtifact(EArtifactClass::ART_MAJOR);
|
||||||
break;
|
break;
|
||||||
case MapObjectID::RANDOM_RELIC_ART:
|
case MapObjectID::RANDOM_RELIC_ART:
|
||||||
subID = cb->gameState().pickRandomArtifact(rand, EArtifactClass::ART_RELIC);
|
subID = gameRandomizer.rollArtifact(EArtifactClass::ART_RELIC);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -660,7 +661,7 @@ void CGArtifact::setArtifactInstance(const CArtifactInstance * instance)
|
|||||||
storedArtifact = instance->getId();
|
storedArtifact = instance->getId();
|
||||||
}
|
}
|
||||||
|
|
||||||
void CGArtifact::initObj(vstd::RNG & rand)
|
void CGArtifact::initObj(IGameRandomizer & gameRandomizer)
|
||||||
{
|
{
|
||||||
blockVisit = true;
|
blockVisit = true;
|
||||||
if(ID == Obj::ARTIFACT)
|
if(ID == Obj::ARTIFACT)
|
||||||
@ -827,13 +828,13 @@ void CGArtifact::serializeJsonOptions(JsonSerializeFormat& handler)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void CGSignBottle::initObj(vstd::RNG & rand)
|
void CGSignBottle::initObj(IGameRandomizer & gameRandomizer)
|
||||||
{
|
{
|
||||||
//if no text is set than we pick random from the predefined ones
|
//if no text is set than we pick random from the predefined ones
|
||||||
if(message.empty())
|
if(message.empty())
|
||||||
{
|
{
|
||||||
auto vector = LIBRARY->generaltexth->findStringsWithPrefix("core.randsign");
|
auto vector = LIBRARY->generaltexth->findStringsWithPrefix("core.randsign");
|
||||||
std::string messageIdentifier = *RandomGeneratorUtil::nextItem(vector, rand);
|
std::string messageIdentifier = *RandomGeneratorUtil::nextItem(vector, gameRandomizer.getDefault());
|
||||||
message.appendTextID(messageIdentifier);
|
message.appendTextID(messageIdentifier);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -917,7 +918,7 @@ void CGGarrison::serializeJsonOptions(JsonSerializeFormat& handler)
|
|||||||
CArmedInstance::serializeJsonOptions(handler);
|
CArmedInstance::serializeJsonOptions(handler);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CGGarrison::initObj(vstd::RNG &rand)
|
void CGGarrison::initObj(IGameRandomizer & gameRandomizer)
|
||||||
{
|
{
|
||||||
if(this->subID == MapObjectSubID::decode(this->ID, "antiMagic"))
|
if(this->subID == MapObjectSubID::decode(this->ID, "antiMagic"))
|
||||||
addAntimagicGarrisonBonus();
|
addAntimagicGarrisonBonus();
|
||||||
@ -934,7 +935,7 @@ void CGGarrison::addAntimagicGarrisonBonus()
|
|||||||
this->addNewBonus(bonus);
|
this->addNewBonus(bonus);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CGMagi::initObj(vstd::RNG & rand)
|
void CGMagi::initObj(IGameRandomizer & gameRandomizer)
|
||||||
{
|
{
|
||||||
if (ID == Obj::EYE_OF_MAGI)
|
if (ID == Obj::EYE_OF_MAGI)
|
||||||
blockVisit = true;
|
blockVisit = true;
|
||||||
@ -1012,7 +1013,7 @@ const CGHeroInstance * CGBoat::getBoardedHero() const
|
|||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CGSirens::initObj(vstd::RNG & rand)
|
void CGSirens::initObj(IGameRandomizer & gameRandomizer)
|
||||||
{
|
{
|
||||||
blockVisit = true;
|
blockVisit = true;
|
||||||
}
|
}
|
||||||
@ -1174,7 +1175,7 @@ void CGObelisk::onHeroVisit(IGameEventCallback & gameEvents, const CGHeroInstanc
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void CGObelisk::initObj(vstd::RNG & rand)
|
void CGObelisk::initObj(IGameRandomizer & gameRandomizer)
|
||||||
{
|
{
|
||||||
cb->gameState().getMap().obeliskCount++;
|
cb->gameState().getMap().obeliskCount++;
|
||||||
}
|
}
|
||||||
|
@ -50,7 +50,7 @@ public:
|
|||||||
MetaString message;
|
MetaString message;
|
||||||
|
|
||||||
void onHeroVisit(IGameEventCallback & gameEvents, const CGHeroInstance * h) const override;
|
void onHeroVisit(IGameEventCallback & gameEvents, const CGHeroInstance * h) const override;
|
||||||
void initObj(vstd::RNG & rand) override;
|
void initObj(IGameRandomizer & gameRandomizer) override;
|
||||||
|
|
||||||
template <typename Handler> void serialize(Handler &h)
|
template <typename Handler> void serialize(Handler &h)
|
||||||
{
|
{
|
||||||
@ -68,7 +68,7 @@ public:
|
|||||||
|
|
||||||
bool removableUnits;
|
bool removableUnits;
|
||||||
|
|
||||||
void initObj(vstd::RNG &rand) override;
|
void initObj(IGameRandomizer & gameRandomizer) override;
|
||||||
bool passableFor(PlayerColor color) const override;
|
bool passableFor(PlayerColor color) const override;
|
||||||
void onHeroVisit(IGameEventCallback & gameEvents, const CGHeroInstance * h) const override;
|
void onHeroVisit(IGameEventCallback & gameEvents, const CGHeroInstance * h) const override;
|
||||||
void battleFinished(IGameEventCallback & gameEvents, const CGHeroInstance *hero, const BattleResult &result) const override;
|
void battleFinished(IGameEventCallback & gameEvents, const CGHeroInstance *hero, const BattleResult &result) const override;
|
||||||
@ -106,8 +106,8 @@ public:
|
|||||||
std::vector<Component> getPopupComponents(PlayerColor player) const override;
|
std::vector<Component> getPopupComponents(PlayerColor player) const override;
|
||||||
|
|
||||||
void pick(IGameEventCallback & gameEvents, const CGHeroInstance * h) const;
|
void pick(IGameEventCallback & gameEvents, const CGHeroInstance * h) const;
|
||||||
void initObj(vstd::RNG & rand) override;
|
void initObj(IGameRandomizer & gameRandomizer) override;
|
||||||
void pickRandomObject(vstd::RNG & rand) override;
|
void pickRandomObject(IGameRandomizer & gameRandomizer) override;
|
||||||
|
|
||||||
BattleField getBattlefield() const override;
|
BattleField getBattlefield() const override;
|
||||||
|
|
||||||
@ -151,7 +151,7 @@ private:
|
|||||||
void blockingDialogAnswered(IGameEventCallback & gameEvents, const CGHeroInstance *hero, int32_t answer) const override;
|
void blockingDialogAnswered(IGameEventCallback & gameEvents, const CGHeroInstance *hero, int32_t answer) const override;
|
||||||
|
|
||||||
void flagMine(IGameEventCallback & gameEvents, const PlayerColor & player) const;
|
void flagMine(IGameEventCallback & gameEvents, const PlayerColor & player) const;
|
||||||
void initObj(vstd::RNG & rand) override;
|
void initObj(IGameRandomizer & gameRandomizer) override;
|
||||||
|
|
||||||
std::string getObjectName() const override;
|
std::string getObjectName() const override;
|
||||||
std::string getHoverText(PlayerColor player) const override;
|
std::string getHoverText(PlayerColor player) const override;
|
||||||
@ -238,7 +238,7 @@ class DLL_LINKAGE CGMonolith : public CGTeleport
|
|||||||
protected:
|
protected:
|
||||||
void onHeroVisit(IGameEventCallback & gameEvents, const CGHeroInstance * h) const override;
|
void onHeroVisit(IGameEventCallback & gameEvents, const CGHeroInstance * h) const override;
|
||||||
void teleportDialogAnswered(IGameEventCallback & gameEvents, const CGHeroInstance *hero, ui32 answer, TTeleportExitsList exits) const override;
|
void teleportDialogAnswered(IGameEventCallback & gameEvents, const CGHeroInstance *hero, ui32 answer, TTeleportExitsList exits) const override;
|
||||||
void initObj(vstd::RNG & rand) override;
|
void initObj(IGameRandomizer & gameRandomizer) override;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
using CGTeleport::CGTeleport;
|
using CGTeleport::CGTeleport;
|
||||||
@ -252,7 +252,7 @@ public:
|
|||||||
class DLL_LINKAGE CGSubterraneanGate : public CGMonolith
|
class DLL_LINKAGE CGSubterraneanGate : public CGMonolith
|
||||||
{
|
{
|
||||||
void onHeroVisit(IGameEventCallback & gameEvents, const CGHeroInstance * h) const override;
|
void onHeroVisit(IGameEventCallback & gameEvents, const CGHeroInstance * h) const override;
|
||||||
void initObj(vstd::RNG & rand) override;
|
void initObj(IGameRandomizer & gameRandomizer) override;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
using CGMonolith::CGMonolith;
|
using CGMonolith::CGMonolith;
|
||||||
@ -287,7 +287,7 @@ public:
|
|||||||
|
|
||||||
void onHeroVisit(IGameEventCallback & gameEvents, const CGHeroInstance * h) const override;
|
void onHeroVisit(IGameEventCallback & gameEvents, const CGHeroInstance * h) const override;
|
||||||
std::string getHoverText(const CGHeroInstance * hero) const override;
|
std::string getHoverText(const CGHeroInstance * hero) const override;
|
||||||
void initObj(vstd::RNG & rand) override;
|
void initObj(IGameRandomizer & gameRandomizer) override;
|
||||||
|
|
||||||
template <typename Handler> void serialize(Handler &h)
|
template <typename Handler> void serialize(Handler &h)
|
||||||
{
|
{
|
||||||
@ -377,7 +377,7 @@ class DLL_LINKAGE CGMagi : public CGObjectInstance
|
|||||||
public:
|
public:
|
||||||
using CGObjectInstance::CGObjectInstance;
|
using CGObjectInstance::CGObjectInstance;
|
||||||
|
|
||||||
void initObj(vstd::RNG & rand) override;
|
void initObj(IGameRandomizer & gameRandomizer) override;
|
||||||
void onHeroVisit(IGameEventCallback & gameEvents, const CGHeroInstance * h) const override;
|
void onHeroVisit(IGameEventCallback & gameEvents, const CGHeroInstance * h) const override;
|
||||||
|
|
||||||
template <typename Handler> void serialize(Handler &h)
|
template <typename Handler> void serialize(Handler &h)
|
||||||
@ -399,7 +399,7 @@ public:
|
|||||||
using CTeamVisited::CTeamVisited;
|
using CTeamVisited::CTeamVisited;
|
||||||
|
|
||||||
void onHeroVisit(IGameEventCallback & gameEvents, const CGHeroInstance * h) const override;
|
void onHeroVisit(IGameEventCallback & gameEvents, const CGHeroInstance * h) const override;
|
||||||
void initObj(vstd::RNG & rand) override;
|
void initObj(IGameRandomizer & gameRandomizer) override;
|
||||||
std::string getHoverText(PlayerColor player) const override;
|
std::string getHoverText(PlayerColor player) const override;
|
||||||
std::string getObjectDescription(PlayerColor player) const;
|
std::string getObjectDescription(PlayerColor player) const;
|
||||||
|
|
||||||
|
@ -66,11 +66,11 @@ TownRewardableBuildingInstance::TownRewardableBuildingInstance(IGameInfoCallback
|
|||||||
: TownBuildingInstance(cb)
|
: TownBuildingInstance(cb)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
TownRewardableBuildingInstance::TownRewardableBuildingInstance(CGTownInstance * town, const BuildingID & index, vstd::RNG & rand)
|
TownRewardableBuildingInstance::TownRewardableBuildingInstance(CGTownInstance * town, const BuildingID & index, IGameRandomizer & gameRandomizer)
|
||||||
: TownBuildingInstance(town, index)
|
: TownBuildingInstance(town, index)
|
||||||
{
|
{
|
||||||
assert(town && town->getTown());
|
assert(town && town->getTown());
|
||||||
configuration = generateConfiguration(rand);
|
configuration = generateConfiguration(gameRandomizer);
|
||||||
}
|
}
|
||||||
|
|
||||||
void TownRewardableBuildingInstance::assignBonuses(std::vector<Bonus> & bonuses) const
|
void TownRewardableBuildingInstance::assignBonuses(std::vector<Bonus> & bonuses) const
|
||||||
@ -92,14 +92,14 @@ void TownRewardableBuildingInstance::assignBonuses(std::vector<Bonus> & bonuses)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Rewardable::Configuration TownRewardableBuildingInstance::generateConfiguration(vstd::RNG & rand) const
|
Rewardable::Configuration TownRewardableBuildingInstance::generateConfiguration(IGameRandomizer & gameRandomizer) const
|
||||||
{
|
{
|
||||||
Rewardable::Configuration result;
|
Rewardable::Configuration result;
|
||||||
const auto & building = town->getTown()->buildings.at(getBuildingType());
|
const auto & building = town->getTown()->buildings.at(getBuildingType());
|
||||||
|
|
||||||
// force modal info window instead of displaying in inactive info box on adventure map
|
// force modal info window instead of displaying in inactive info box on adventure map
|
||||||
result.infoWindowType = EInfoWindowMode::MODAL;
|
result.infoWindowType = EInfoWindowMode::MODAL;
|
||||||
building->rewardableObjectInfo.configureObject(result, rand, cb);
|
building->rewardableObjectInfo.configureObject(result, gameRandomizer, cb);
|
||||||
for(auto & rewardInfo : result.info)
|
for(auto & rewardInfo : result.info)
|
||||||
{
|
{
|
||||||
assignBonuses(rewardInfo.reward.heroBonuses);
|
assignBonuses(rewardInfo.reward.heroBonuses);
|
||||||
@ -109,11 +109,11 @@ Rewardable::Configuration TownRewardableBuildingInstance::generateConfiguration(
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
void TownRewardableBuildingInstance::newTurn(IGameEventCallback & gameEvents) const
|
void TownRewardableBuildingInstance::newTurn(IGameEventCallback & gameEvents, IGameRandomizer & gameRandomizer) const
|
||||||
{
|
{
|
||||||
if (configuration.resetParameters.period != 0 && cb->getDate(Date::DAY) > 1 && ((cb->getDate(Date::DAY)-1) % configuration.resetParameters.period) == 0)
|
if (configuration.resetParameters.period != 0 && cb->getDate(Date::DAY) > 1 && ((cb->getDate(Date::DAY)-1) % configuration.resetParameters.period) == 0)
|
||||||
{
|
{
|
||||||
auto newConfiguration = generateConfiguration(gameEvents.getRandomGenerator());
|
auto newConfiguration = generateConfiguration(gameRandomizer);
|
||||||
gameEvents.setRewardableObjectConfiguration(town->id, getBuildingType(), newConfiguration);
|
gameEvents.setRewardableObjectConfiguration(town->id, getBuildingType(), newConfiguration);
|
||||||
|
|
||||||
if(configuration.resetParameters.visitors)
|
if(configuration.resetParameters.visitors)
|
||||||
|
@ -57,7 +57,7 @@ class DLL_LINKAGE TownRewardableBuildingInstance : public TownBuildingInstance,
|
|||||||
|
|
||||||
bool wasVisitedBefore(const CGHeroInstance * contextHero) const override;
|
bool wasVisitedBefore(const CGHeroInstance * contextHero) const override;
|
||||||
void grantReward(IGameEventCallback & gameEvents, ui32 rewardID, const CGHeroInstance * hero) const override;
|
void grantReward(IGameEventCallback & gameEvents, ui32 rewardID, const CGHeroInstance * hero) const override;
|
||||||
Rewardable::Configuration generateConfiguration(vstd::RNG & rand) const;
|
Rewardable::Configuration generateConfiguration(IGameRandomizer & gameRandomizer) const;
|
||||||
void assignBonuses(std::vector<Bonus> & bonuses) const;
|
void assignBonuses(std::vector<Bonus> & bonuses) const;
|
||||||
|
|
||||||
const IObjectInterface * getObject() const override;
|
const IObjectInterface * getObject() const override;
|
||||||
@ -69,7 +69,7 @@ public:
|
|||||||
void onHeroVisit(IGameEventCallback & gameEvents, const CGHeroInstance * h) const override;
|
void onHeroVisit(IGameEventCallback & gameEvents, const CGHeroInstance * h) const override;
|
||||||
bool wasVisited(const CGHeroInstance * contextHero) const override;
|
bool wasVisited(const CGHeroInstance * contextHero) const override;
|
||||||
|
|
||||||
void newTurn(IGameEventCallback & gameEvents) const override;
|
void newTurn(IGameEventCallback & gameEvents, IGameRandomizer & gameRandomizer) const override;
|
||||||
|
|
||||||
/// gives second part of reward after hero level-ups for proper granting of spells/mana
|
/// gives second part of reward after hero level-ups for proper granting of spells/mana
|
||||||
void heroLevelUpDone(IGameEventCallback & gameEvents, const CGHeroInstance *hero) const override;
|
void heroLevelUpDone(IGameEventCallback & gameEvents, const CGHeroInstance *hero) const override;
|
||||||
@ -77,7 +77,7 @@ public:
|
|||||||
/// applies player selection of reward
|
/// applies player selection of reward
|
||||||
void blockingDialogAnswered(IGameEventCallback & gameEvents, const CGHeroInstance *hero, int32_t answer) const override;
|
void blockingDialogAnswered(IGameEventCallback & gameEvents, const CGHeroInstance *hero, int32_t answer) const override;
|
||||||
|
|
||||||
TownRewardableBuildingInstance(CGTownInstance * town, const BuildingID & index, vstd::RNG & rand);
|
TownRewardableBuildingInstance(CGTownInstance * town, const BuildingID & index, IGameRandomizer & gameRandomizer);
|
||||||
TownRewardableBuildingInstance(IGameInfoCallback *cb);
|
TownRewardableBuildingInstance(IGameInfoCallback *cb);
|
||||||
|
|
||||||
template <typename Handler> void serialize(Handler &h)
|
template <typename Handler> void serialize(Handler &h)
|
||||||
|
@ -55,7 +55,6 @@ public:
|
|||||||
virtual void visitRemoveBonus(RemoveBonus & pack) {}
|
virtual void visitRemoveBonus(RemoveBonus & pack) {}
|
||||||
virtual void visitSetCommanderProperty(SetCommanderProperty & pack) {}
|
virtual void visitSetCommanderProperty(SetCommanderProperty & pack) {}
|
||||||
virtual void visitAddQuest(AddQuest & pack) {}
|
virtual void visitAddQuest(AddQuest & pack) {}
|
||||||
virtual void visitUpdateArtHandlerLists(UpdateArtHandlerLists & pack) {}
|
|
||||||
virtual void visitChangeFormation(ChangeFormation & pack) {}
|
virtual void visitChangeFormation(ChangeFormation & pack) {}
|
||||||
virtual void visitRemoveObject(RemoveObject & pack) {}
|
virtual void visitRemoveObject(RemoveObject & pack) {}
|
||||||
virtual void visitTryMoveHero(TryMoveHero & pack) {}
|
virtual void visitTryMoveHero(TryMoveHero & pack) {}
|
||||||
|
@ -198,11 +198,6 @@ void AddQuest::visitTyped(ICPackVisitor & visitor)
|
|||||||
visitor.visitAddQuest(*this);
|
visitor.visitAddQuest(*this);
|
||||||
}
|
}
|
||||||
|
|
||||||
void UpdateArtHandlerLists::visitTyped(ICPackVisitor & visitor)
|
|
||||||
{
|
|
||||||
visitor.visitUpdateArtHandlerLists(*this);
|
|
||||||
}
|
|
||||||
|
|
||||||
void ChangeFormation::visitTyped(ICPackVisitor & visitor)
|
void ChangeFormation::visitTyped(ICPackVisitor & visitor)
|
||||||
{
|
{
|
||||||
visitor.visitChangeFormation(*this);
|
visitor.visitChangeFormation(*this);
|
||||||
|
@ -540,18 +540,6 @@ struct DLL_LINKAGE AddQuest : public CPackForClient
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
struct DLL_LINKAGE UpdateArtHandlerLists : public CPackForClient
|
|
||||||
{
|
|
||||||
std::map<ArtifactID, int> allocatedArtifacts;
|
|
||||||
|
|
||||||
void visitTyped(ICPackVisitor & visitor) override;
|
|
||||||
|
|
||||||
template <typename Handler> void serialize(Handler & h)
|
|
||||||
{
|
|
||||||
h & allocatedArtifacts;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
struct DLL_LINKAGE ChangeFormation : public CPackForClient
|
struct DLL_LINKAGE ChangeFormation : public CPackForClient
|
||||||
{
|
{
|
||||||
ObjectInstanceID hid;
|
ObjectInstanceID hid;
|
||||||
|
@ -15,6 +15,7 @@
|
|||||||
#include "Limiter.h"
|
#include "Limiter.h"
|
||||||
#include "Reward.h"
|
#include "Reward.h"
|
||||||
|
|
||||||
|
#include "../callback/IGameRandomizer.h"
|
||||||
#include "../texts/CGeneralTextHandler.h"
|
#include "../texts/CGeneralTextHandler.h"
|
||||||
#include "../json/JsonRandom.h"
|
#include "../json/JsonRandom.h"
|
||||||
#include "../GameLibrary.h"
|
#include "../GameLibrary.h"
|
||||||
@ -108,14 +109,14 @@ void Rewardable::Info::init(const JsonNode & objectConfig, const std::string & o
|
|||||||
loadString(parameters["onGuardedMessage"], TextIdentifier(objectName, "onGuarded"));
|
loadString(parameters["onGuardedMessage"], TextIdentifier(objectName, "onGuarded"));
|
||||||
}
|
}
|
||||||
|
|
||||||
Rewardable::LimitersList Rewardable::Info::configureSublimiters(Rewardable::Configuration & object, vstd::RNG & rng, IGameInfoCallback * cb, const JsonNode & source) const
|
Rewardable::LimitersList Rewardable::Info::configureSublimiters(Rewardable::Configuration & object, IGameRandomizer & gameRandomizer, IGameInfoCallback * cb, const JsonNode & source) const
|
||||||
{
|
{
|
||||||
Rewardable::LimitersList result;
|
Rewardable::LimitersList result;
|
||||||
for (const auto & input : source.Vector())
|
for (const auto & input : source.Vector())
|
||||||
{
|
{
|
||||||
auto newLimiter = std::make_shared<Rewardable::Limiter>();
|
auto newLimiter = std::make_shared<Rewardable::Limiter>();
|
||||||
|
|
||||||
configureLimiter(object, rng, cb, *newLimiter, input);
|
configureLimiter(object, gameRandomizer, cb, *newLimiter, input);
|
||||||
|
|
||||||
result.push_back(newLimiter);
|
result.push_back(newLimiter);
|
||||||
}
|
}
|
||||||
@ -123,81 +124,81 @@ Rewardable::LimitersList Rewardable::Info::configureSublimiters(Rewardable::Conf
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Rewardable::Info::configureLimiter(Rewardable::Configuration & object, vstd::RNG & rng, IGameInfoCallback * cb, Rewardable::Limiter & limiter, const JsonNode & source) const
|
void Rewardable::Info::configureLimiter(Rewardable::Configuration & object, IGameRandomizer & gameRandomizer, IGameInfoCallback * cb, Rewardable::Limiter & limiter, const JsonNode & source) const
|
||||||
{
|
{
|
||||||
auto const & variables = object.variables.values;
|
auto const & variables = object.variables.values;
|
||||||
JsonRandom randomizer(cb);
|
JsonRandom randomizer(cb, gameRandomizer);
|
||||||
|
|
||||||
limiter.dayOfWeek = randomizer.loadValue(source["dayOfWeek"], rng, variables);
|
limiter.dayOfWeek = randomizer.loadValue(source["dayOfWeek"], variables);
|
||||||
limiter.daysPassed = randomizer.loadValue(source["daysPassed"], rng, variables);
|
limiter.daysPassed = randomizer.loadValue(source["daysPassed"], variables);
|
||||||
limiter.heroExperience = randomizer.loadValue(source["heroExperience"], rng, variables);
|
limiter.heroExperience = randomizer.loadValue(source["heroExperience"], variables);
|
||||||
limiter.heroLevel = randomizer.loadValue(source["heroLevel"], rng, variables);
|
limiter.heroLevel = randomizer.loadValue(source["heroLevel"], variables);
|
||||||
limiter.canLearnSkills = source["canLearnSkills"].Bool();
|
limiter.canLearnSkills = source["canLearnSkills"].Bool();
|
||||||
limiter.commanderAlive = source["commanderAlive"].Bool();
|
limiter.commanderAlive = source["commanderAlive"].Bool();
|
||||||
limiter.hasExtraCreatures = source["hasExtraCreatures"].Bool();
|
limiter.hasExtraCreatures = source["hasExtraCreatures"].Bool();
|
||||||
|
|
||||||
limiter.manaPercentage = randomizer.loadValue(source["manaPercentage"], rng, variables);
|
limiter.manaPercentage = randomizer.loadValue(source["manaPercentage"], variables);
|
||||||
limiter.manaPoints = randomizer.loadValue(source["manaPoints"], rng, variables);
|
limiter.manaPoints = randomizer.loadValue(source["manaPoints"], variables);
|
||||||
|
|
||||||
limiter.resources = randomizer.loadResources(source["resources"], rng, variables);
|
limiter.resources = randomizer.loadResources(source["resources"], variables);
|
||||||
|
|
||||||
limiter.primary = randomizer.loadPrimaries(source["primary"], rng, variables);
|
limiter.primary = randomizer.loadPrimaries(source["primary"], variables);
|
||||||
limiter.secondary = randomizer.loadSecondaries(source["secondary"], rng, variables);
|
limiter.secondary = randomizer.loadSecondaries(source["secondary"], variables);
|
||||||
limiter.artifacts = randomizer.loadArtifacts(source["artifacts"], rng, variables);
|
limiter.artifacts = randomizer.loadArtifacts(source["artifacts"], variables);
|
||||||
limiter.availableSlots = randomizer.loadArtifactSlots(source["availableSlots"], rng, variables);
|
limiter.availableSlots = randomizer.loadArtifactSlots(source["availableSlots"], variables);
|
||||||
limiter.spells = randomizer.loadSpells(source["spells"], rng, variables);
|
limiter.spells = randomizer.loadSpells(source["spells"], variables);
|
||||||
limiter.scrolls = randomizer.loadSpells(source["scrolls"], rng, variables);
|
limiter.scrolls = randomizer.loadSpells(source["scrolls"], variables);
|
||||||
limiter.canLearnSpells = randomizer.loadSpells(source["canLearnSpells"], rng, variables);
|
limiter.canLearnSpells = randomizer.loadSpells(source["canLearnSpells"], variables);
|
||||||
limiter.creatures = randomizer.loadCreatures(source["creatures"], rng, variables);
|
limiter.creatures = randomizer.loadCreatures(source["creatures"], variables);
|
||||||
limiter.canReceiveCreatures = randomizer.loadCreatures(source["canReceiveCreatures"], rng, variables);
|
limiter.canReceiveCreatures = randomizer.loadCreatures(source["canReceiveCreatures"], variables);
|
||||||
|
|
||||||
limiter.players = randomizer.loadColors(source["colors"], rng, variables);
|
limiter.players = randomizer.loadColors(source["colors"], variables);
|
||||||
limiter.heroes = randomizer.loadHeroes(source["heroes"], rng);
|
limiter.heroes = randomizer.loadHeroes(source["heroes"]);
|
||||||
limiter.heroClasses = randomizer.loadHeroClasses(source["heroClasses"], rng);
|
limiter.heroClasses = randomizer.loadHeroClasses(source["heroClasses"]);
|
||||||
|
|
||||||
limiter.allOf = configureSublimiters(object, rng, cb, source["allOf"] );
|
limiter.allOf = configureSublimiters(object, gameRandomizer, cb, source["allOf"]);
|
||||||
limiter.anyOf = configureSublimiters(object, rng, cb, source["anyOf"] );
|
limiter.anyOf = configureSublimiters(object, gameRandomizer, cb, source["anyOf"]);
|
||||||
limiter.noneOf = configureSublimiters(object, rng, cb, source["noneOf"] );
|
limiter.noneOf = configureSublimiters(object, gameRandomizer, cb, source["noneOf"]);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Rewardable::Info::configureReward(Rewardable::Configuration & object, vstd::RNG & rng, IGameInfoCallback * cb, Rewardable::Reward & reward, const JsonNode & source) const
|
void Rewardable::Info::configureReward(Rewardable::Configuration & object, IGameRandomizer & gameRandomizer, IGameInfoCallback * cb, Rewardable::Reward & reward, const JsonNode & source) const
|
||||||
{
|
{
|
||||||
auto const & variables = object.variables.values;
|
auto const & variables = object.variables.values;
|
||||||
JsonRandom randomizer(cb);
|
JsonRandom randomizer(cb, gameRandomizer);
|
||||||
|
|
||||||
reward.resources = randomizer.loadResources(source["resources"], rng, variables);
|
reward.resources = randomizer.loadResources(source["resources"], variables);
|
||||||
|
|
||||||
reward.heroExperience = randomizer.loadValue(source["heroExperience"], rng, variables);
|
reward.heroExperience = randomizer.loadValue(source["heroExperience"], variables);
|
||||||
reward.heroLevel = randomizer.loadValue(source["heroLevel"], rng, variables);
|
reward.heroLevel = randomizer.loadValue(source["heroLevel"], variables);
|
||||||
|
|
||||||
reward.manaDiff = randomizer.loadValue(source["manaPoints"], rng, variables);
|
reward.manaDiff = randomizer.loadValue(source["manaPoints"], variables);
|
||||||
reward.manaOverflowFactor = randomizer.loadValue(source["manaOverflowFactor"], rng, variables);
|
reward.manaOverflowFactor = randomizer.loadValue(source["manaOverflowFactor"], variables);
|
||||||
reward.manaPercentage = randomizer.loadValue(source["manaPercentage"], rng, variables, -1);
|
reward.manaPercentage = randomizer.loadValue(source["manaPercentage"], variables, -1);
|
||||||
|
|
||||||
reward.movePoints = randomizer.loadValue(source["movePoints"], rng, variables);
|
reward.movePoints = randomizer.loadValue(source["movePoints"], variables);
|
||||||
reward.movePercentage = randomizer.loadValue(source["movePercentage"], rng, variables, -1);
|
reward.movePercentage = randomizer.loadValue(source["movePercentage"], variables, -1);
|
||||||
|
|
||||||
reward.removeObject = source["removeObject"].Bool();
|
reward.removeObject = source["removeObject"].Bool();
|
||||||
reward.heroBonuses = randomizer.loadBonuses(source["bonuses"]);
|
reward.heroBonuses = randomizer.loadBonuses(source["bonuses"]);
|
||||||
reward.commanderBonuses = randomizer.loadBonuses(source["commanderBonuses"]);
|
reward.commanderBonuses = randomizer.loadBonuses(source["commanderBonuses"]);
|
||||||
reward.playerBonuses = randomizer.loadBonuses(source["playerBonuses"]);
|
reward.playerBonuses = randomizer.loadBonuses(source["playerBonuses"]);
|
||||||
|
|
||||||
reward.guards = randomizer.loadCreatures(source["guards"], rng, variables);
|
reward.guards = randomizer.loadCreatures(source["guards"], variables);
|
||||||
|
|
||||||
reward.primary = randomizer.loadPrimaries(source["primary"], rng, variables);
|
reward.primary = randomizer.loadPrimaries(source["primary"], variables);
|
||||||
reward.secondary = randomizer.loadSecondaries(source["secondary"], rng, variables);
|
reward.secondary = randomizer.loadSecondaries(source["secondary"], variables);
|
||||||
|
|
||||||
reward.grantedArtifacts = randomizer.loadArtifacts(source["artifacts"], rng, variables);
|
reward.grantedArtifacts = randomizer.loadArtifacts(source["artifacts"], variables);
|
||||||
reward.takenArtifacts = randomizer.loadArtifacts(source["takenArtifacts"], rng, variables);
|
reward.takenArtifacts = randomizer.loadArtifacts(source["takenArtifacts"], variables);
|
||||||
reward.takenArtifactSlots = randomizer.loadArtifactSlots(source["takenArtifactSlots"], rng, variables);
|
reward.takenArtifactSlots = randomizer.loadArtifactSlots(source["takenArtifactSlots"], variables);
|
||||||
reward.grantedScrolls = randomizer.loadSpells(source["scrolls"], rng, variables);
|
reward.grantedScrolls = randomizer.loadSpells(source["scrolls"], variables);
|
||||||
reward.takenScrolls = randomizer.loadSpells(source["takenScrolls"], rng, variables);
|
reward.takenScrolls = randomizer.loadSpells(source["takenScrolls"], variables);
|
||||||
reward.spells = randomizer.loadSpells(source["spells"], rng, variables);
|
reward.spells = randomizer.loadSpells(source["spells"], variables);
|
||||||
reward.creatures = randomizer.loadCreatures(source["creatures"], rng, variables);
|
reward.creatures = randomizer.loadCreatures(source["creatures"], variables);
|
||||||
reward.takenCreatures = randomizer.loadCreatures(source["takenCreatures"], rng, variables);
|
reward.takenCreatures = randomizer.loadCreatures(source["takenCreatures"], variables);
|
||||||
if(!source["spellCast"].isNull() && source["spellCast"].isStruct())
|
if(!source["spellCast"].isNull() && source["spellCast"].isStruct())
|
||||||
{
|
{
|
||||||
reward.spellCast.first = randomizer.loadSpell(source["spellCast"]["spell"], rng, variables);
|
reward.spellCast.first = randomizer.loadSpell(source["spellCast"]["spell"], variables);
|
||||||
reward.spellCast.second = source["spellCast"]["schoolLevel"].Integer();
|
reward.spellCast.second = source["spellCast"]["schoolLevel"].Integer();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -206,13 +207,13 @@ void Rewardable::Info::configureReward(Rewardable::Configuration & object, vstd:
|
|||||||
auto const & entry = source["revealTiles"];
|
auto const & entry = source["revealTiles"];
|
||||||
|
|
||||||
reward.revealTiles = RewardRevealTiles();
|
reward.revealTiles = RewardRevealTiles();
|
||||||
reward.revealTiles->radius = randomizer.loadValue(entry["radius"], rng, variables);
|
reward.revealTiles->radius = randomizer.loadValue(entry["radius"], variables);
|
||||||
reward.revealTiles->hide = entry["hide"].Bool();
|
reward.revealTiles->hide = entry["hide"].Bool();
|
||||||
|
|
||||||
reward.revealTiles->scoreSurface = randomizer.loadValue(entry["surface"], rng, variables);
|
reward.revealTiles->scoreSurface = randomizer.loadValue(entry["surface"], variables);
|
||||||
reward.revealTiles->scoreSubterra = randomizer.loadValue(entry["subterra"], rng, variables);
|
reward.revealTiles->scoreSubterra = randomizer.loadValue(entry["subterra"], variables);
|
||||||
reward.revealTiles->scoreWater = randomizer.loadValue(entry["water"], rng, variables);
|
reward.revealTiles->scoreWater = randomizer.loadValue(entry["water"], variables);
|
||||||
reward.revealTiles->scoreRock = randomizer.loadValue(entry["rock"], rng, variables);
|
reward.revealTiles->scoreRock = randomizer.loadValue(entry["rock"], variables);
|
||||||
}
|
}
|
||||||
|
|
||||||
for ( auto node : source["changeCreatures"].Struct() )
|
for ( auto node : source["changeCreatures"].Struct() )
|
||||||
@ -226,16 +227,16 @@ void Rewardable::Info::configureReward(Rewardable::Configuration & object, vstd:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Rewardable::Info::configureResetInfo(Rewardable::Configuration & object, vstd::RNG & rng, Rewardable::ResetInfo & resetParameters, const JsonNode & source) const
|
void Rewardable::Info::configureResetInfo(Rewardable::Configuration & object, IGameRandomizer & gameRandomizer, Rewardable::ResetInfo & resetParameters, const JsonNode & source) const
|
||||||
{
|
{
|
||||||
resetParameters.period = static_cast<ui32>(source["period"].Float());
|
resetParameters.period = static_cast<ui32>(source["period"].Float());
|
||||||
resetParameters.visitors = source["visitors"].Bool();
|
resetParameters.visitors = source["visitors"].Bool();
|
||||||
resetParameters.rewards = source["rewards"].Bool();
|
resetParameters.rewards = source["rewards"].Bool();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Rewardable::Info::configureVariables(Rewardable::Configuration & object, vstd::RNG & rng, IGameInfoCallback * cb, const JsonNode & source) const
|
void Rewardable::Info::configureVariables(Rewardable::Configuration & object, IGameRandomizer & gameRandomizer, IGameInfoCallback * cb, const JsonNode & source) const
|
||||||
{
|
{
|
||||||
JsonRandom randomizer(cb);
|
JsonRandom randomizer(cb, gameRandomizer);
|
||||||
|
|
||||||
for(const auto & category : source.Struct())
|
for(const auto & category : source.Struct())
|
||||||
{
|
{
|
||||||
@ -246,19 +247,19 @@ void Rewardable::Info::configureVariables(Rewardable::Configuration & object, vs
|
|||||||
int32_t value = -1;
|
int32_t value = -1;
|
||||||
|
|
||||||
if (category.first == "number")
|
if (category.first == "number")
|
||||||
value = randomizer.loadValue(input, rng, object.variables.values);
|
value = randomizer.loadValue(input, object.variables.values);
|
||||||
|
|
||||||
if (category.first == "artifact")
|
if (category.first == "artifact")
|
||||||
value = randomizer.loadArtifact(input, rng, object.variables.values).getNum();
|
value = randomizer.loadArtifact(input, object.variables.values).getNum();
|
||||||
|
|
||||||
if (category.first == "spell")
|
if (category.first == "spell")
|
||||||
value = randomizer.loadSpell(input, rng, object.variables.values).getNum();
|
value = randomizer.loadSpell(input, object.variables.values).getNum();
|
||||||
|
|
||||||
if (category.first == "primarySkill")
|
if (category.first == "primarySkill")
|
||||||
value = randomizer.loadPrimary(input, rng, object.variables.values).getNum();
|
value = randomizer.loadPrimary(input, object.variables.values).getNum();
|
||||||
|
|
||||||
if (category.first == "secondarySkill")
|
if (category.first == "secondarySkill")
|
||||||
value = randomizer.loadSecondary(input, rng, object.variables.values).getNum();
|
value = randomizer.loadSecondary(input, object.variables.values).getNum();
|
||||||
|
|
||||||
object.initVariable(category.first, entry.first, value);
|
object.initVariable(category.first, entry.first, value);
|
||||||
}
|
}
|
||||||
@ -350,7 +351,7 @@ void Rewardable::Info::replaceTextPlaceholders(MetaString & target, const Variab
|
|||||||
|
|
||||||
void Rewardable::Info::configureRewards(
|
void Rewardable::Info::configureRewards(
|
||||||
Rewardable::Configuration & object,
|
Rewardable::Configuration & object,
|
||||||
vstd::RNG & rng,
|
IGameRandomizer & gameRandomizer,
|
||||||
IGameInfoCallback * cb,
|
IGameInfoCallback * cb,
|
||||||
const JsonNode & source,
|
const JsonNode & source,
|
||||||
Rewardable::EEventType event,
|
Rewardable::EEventType event,
|
||||||
@ -371,7 +372,7 @@ void Rewardable::Info::configureRewards(
|
|||||||
{
|
{
|
||||||
const JsonNode & preset = object.getPresetVariable("dice", diceID);
|
const JsonNode & preset = object.getPresetVariable("dice", diceID);
|
||||||
if (preset.isNull())
|
if (preset.isNull())
|
||||||
object.initVariable("dice", diceID, rng.nextInt(0, 99));
|
object.initVariable("dice", diceID, gameRandomizer.getDefault().nextInt(0, 99));
|
||||||
else
|
else
|
||||||
object.initVariable("dice", diceID, preset.Integer());
|
object.initVariable("dice", diceID, preset.Integer());
|
||||||
|
|
||||||
@ -394,8 +395,8 @@ void Rewardable::Info::configureRewards(
|
|||||||
}
|
}
|
||||||
|
|
||||||
Rewardable::VisitInfo info;
|
Rewardable::VisitInfo info;
|
||||||
configureLimiter(object, rng, cb, info.limiter, reward["limiter"]);
|
configureLimiter(object, gameRandomizer, cb, info.limiter, reward["limiter"]);
|
||||||
configureReward(object, rng, cb, info.reward, reward);
|
configureReward(object, gameRandomizer, cb, info.reward, reward);
|
||||||
|
|
||||||
info.visitType = event;
|
info.visitType = event;
|
||||||
info.message = loadMessage(reward["message"], TextIdentifier(objectTextID, modeName, i));
|
info.message = loadMessage(reward["message"], TextIdentifier(objectTextID, modeName, i));
|
||||||
@ -408,16 +409,16 @@ void Rewardable::Info::configureRewards(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Rewardable::Info::configureObject(Rewardable::Configuration & object, vstd::RNG & rng, IGameInfoCallback * cb) const
|
void Rewardable::Info::configureObject(Rewardable::Configuration & object, IGameRandomizer & gameRandomizer, IGameInfoCallback * cb) const
|
||||||
{
|
{
|
||||||
object.info.clear();
|
object.info.clear();
|
||||||
object.variables.values.clear();
|
object.variables.values.clear();
|
||||||
|
|
||||||
configureVariables(object, rng, cb, parameters["variables"]);
|
configureVariables(object, gameRandomizer, cb, parameters["variables"]);
|
||||||
|
|
||||||
configureRewards(object, rng, cb, parameters["rewards"], Rewardable::EEventType::EVENT_FIRST_VISIT, "rewards");
|
configureRewards(object, gameRandomizer, cb, parameters["rewards"], Rewardable::EEventType::EVENT_FIRST_VISIT, "rewards");
|
||||||
configureRewards(object, rng, cb, parameters["onVisited"], Rewardable::EEventType::EVENT_ALREADY_VISITED, "onVisited");
|
configureRewards(object, gameRandomizer, cb, parameters["onVisited"], Rewardable::EEventType::EVENT_ALREADY_VISITED, "onVisited");
|
||||||
configureRewards(object, rng, cb, parameters["onEmpty"], Rewardable::EEventType::EVENT_NOT_AVAILABLE, "onEmpty");
|
configureRewards(object, gameRandomizer, cb, parameters["onEmpty"], Rewardable::EEventType::EVENT_NOT_AVAILABLE, "onEmpty");
|
||||||
|
|
||||||
object.onSelect = loadMessage(parameters["onSelectMessage"], TextIdentifier(objectTextID, "onSelect"));
|
object.onSelect = loadMessage(parameters["onSelectMessage"], TextIdentifier(objectTextID, "onSelect"));
|
||||||
object.description = loadMessage(parameters["description"], TextIdentifier(objectTextID, "description"));
|
object.description = loadMessage(parameters["description"], TextIdentifier(objectTextID, "description"));
|
||||||
@ -460,7 +461,7 @@ void Rewardable::Info::configureObject(Rewardable::Configuration & object, vstd:
|
|||||||
object.info.push_back(onGuarded);
|
object.info.push_back(onGuarded);
|
||||||
}
|
}
|
||||||
|
|
||||||
configureResetInfo(object, rng, object.resetParameters, parameters["resetParameters"]);
|
configureResetInfo(object, gameRandomizer, object.resetParameters, parameters["resetParameters"]);
|
||||||
|
|
||||||
object.canRefuse = parameters["canRefuse"].Bool();
|
object.canRefuse = parameters["canRefuse"].Bool();
|
||||||
object.showScoutedPreview = parameters["showScoutedPreview"].Bool();
|
object.showScoutedPreview = parameters["showScoutedPreview"].Bool();
|
||||||
@ -494,7 +495,7 @@ void Rewardable::Info::configureObject(Rewardable::Configuration & object, vstd:
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (object.visitMode == Rewardable::VISIT_LIMITER)
|
if (object.visitMode == Rewardable::VISIT_LIMITER)
|
||||||
configureLimiter(object, rng, cb, object.visitLimiter, parameters["visitLimiter"]);
|
configureLimiter(object, gameRandomizer, cb, object.visitLimiter, parameters["visitLimiter"]);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -15,13 +15,9 @@
|
|||||||
|
|
||||||
VCMI_LIB_NAMESPACE_BEGIN
|
VCMI_LIB_NAMESPACE_BEGIN
|
||||||
|
|
||||||
namespace vstd
|
|
||||||
{
|
|
||||||
class RNG;
|
|
||||||
}
|
|
||||||
|
|
||||||
class MetaString;
|
class MetaString;
|
||||||
class IGameInfoCallback;
|
class IGameInfoCallback;
|
||||||
|
class IGameRandomizer;
|
||||||
|
|
||||||
namespace Rewardable
|
namespace Rewardable
|
||||||
{
|
{
|
||||||
@ -42,14 +38,14 @@ class DLL_LINKAGE Info : public IObjectInfo
|
|||||||
void replaceTextPlaceholders(MetaString & target, const Variables & variables) const;
|
void replaceTextPlaceholders(MetaString & target, const Variables & variables) const;
|
||||||
void replaceTextPlaceholders(MetaString & target, const Variables & variables, const VisitInfo & info) const;
|
void replaceTextPlaceholders(MetaString & target, const Variables & variables, const VisitInfo & info) const;
|
||||||
|
|
||||||
void configureVariables(Rewardable::Configuration & object, vstd::RNG & rng, IGameInfoCallback * cb, const JsonNode & source) const;
|
void configureVariables(Rewardable::Configuration & object, IGameRandomizer & gameRandomizer, IGameInfoCallback * cb, const JsonNode & source) const;
|
||||||
void configureRewards(Rewardable::Configuration & object, vstd::RNG & rng, IGameInfoCallback * cb, const JsonNode & source, Rewardable::EEventType mode, const std::string & textPrefix) const;
|
void configureRewards(Rewardable::Configuration & object, IGameRandomizer & gameRandomizer, IGameInfoCallback * cb, const JsonNode & source, Rewardable::EEventType mode, const std::string & textPrefix) const;
|
||||||
|
|
||||||
void configureLimiter(Rewardable::Configuration & object, vstd::RNG & rng, IGameInfoCallback * cb, Rewardable::Limiter & limiter, const JsonNode & source) const;
|
void configureLimiter(Rewardable::Configuration & object, IGameRandomizer & gameRandomizer, IGameInfoCallback * cb, Rewardable::Limiter & limiter, const JsonNode & source) const;
|
||||||
Rewardable::LimitersList configureSublimiters(Rewardable::Configuration & object, vstd::RNG & rng, IGameInfoCallback * cb, const JsonNode & source) const;
|
Rewardable::LimitersList configureSublimiters(Rewardable::Configuration & object, IGameRandomizer & gameRandomizer, IGameInfoCallback * cb, const JsonNode & source) const;
|
||||||
|
|
||||||
void configureReward(Rewardable::Configuration & object, vstd::RNG & rng, IGameInfoCallback * cb, Rewardable::Reward & info, const JsonNode & source) const;
|
void configureReward(Rewardable::Configuration & object, IGameRandomizer & gameRandomizer, IGameInfoCallback * cb, Rewardable::Reward & info, const JsonNode & source) const;
|
||||||
void configureResetInfo(Rewardable::Configuration & object, vstd::RNG & rng, Rewardable::ResetInfo & info, const JsonNode & source) const;
|
void configureResetInfo(Rewardable::Configuration & object, IGameRandomizer & gameRandomizer, Rewardable::ResetInfo & info, const JsonNode & source) const;
|
||||||
public:
|
public:
|
||||||
const JsonNode & getParameters() const;
|
const JsonNode & getParameters() const;
|
||||||
|
|
||||||
@ -70,7 +66,7 @@ public:
|
|||||||
|
|
||||||
bool hasGuards() const override;
|
bool hasGuards() const override;
|
||||||
|
|
||||||
void configureObject(Rewardable::Configuration & object, vstd::RNG & rng, IGameInfoCallback * cb) const;
|
void configureObject(Rewardable::Configuration & object, IGameRandomizer & gameRandomizer, IGameInfoCallback * cb) const;
|
||||||
|
|
||||||
void init(const JsonNode & objectConfig, const std::string & objectTextID);
|
void init(const JsonNode & objectConfig, const std::string & objectTextID);
|
||||||
|
|
||||||
|
@ -41,8 +41,9 @@ enum class ESerializationVersion : int32_t
|
|||||||
STORE_UID_COUNTER_IN_CMAP, // fix crash caused by conflicting instanceName after loading game
|
STORE_UID_COUNTER_IN_CMAP, // fix crash caused by conflicting instanceName after loading game
|
||||||
REWARDABLE_EXTENSIONS, // new functionality for rewardable objects
|
REWARDABLE_EXTENSIONS, // new functionality for rewardable objects
|
||||||
FLAGGABLE_BONUS_SYSTEM_NODE, // flaggable objects now contain bonus system node
|
FLAGGABLE_BONUS_SYSTEM_NODE, // flaggable objects now contain bonus system node
|
||||||
|
RANDOMIZATION_REWORK, // random rolls logic has been moved to server
|
||||||
|
|
||||||
CURRENT = FLAGGABLE_BONUS_SYSTEM_NODE,
|
CURRENT = RANDOMIZATION_REWORK,
|
||||||
};
|
};
|
||||||
|
|
||||||
static_assert(ESerializationVersion::MINIMAL <= ESerializationVersion::CURRENT, "Invalid serialization version definition!");
|
static_assert(ESerializationVersion::MINIMAL <= ESerializationVersion::CURRENT, "Invalid serialization version definition!");
|
||||||
|
@ -159,7 +159,6 @@ void registerTypes(Serializer &s)
|
|||||||
s.template registerType<PlayerEndsGame>(103);
|
s.template registerType<PlayerEndsGame>(103);
|
||||||
s.template registerType<PlayerReinitInterface>(104);
|
s.template registerType<PlayerReinitInterface>(104);
|
||||||
s.template registerType<RemoveBonus>(105);
|
s.template registerType<RemoveBonus>(105);
|
||||||
s.template registerType<UpdateArtHandlerLists>(106);
|
|
||||||
s.template registerType<ChangeFormation>(107);
|
s.template registerType<ChangeFormation>(107);
|
||||||
s.template registerType<RemoveObject>(108);
|
s.template registerType<RemoveObject>(108);
|
||||||
s.template registerType<TryMoveHero>(109);
|
s.template registerType<TryMoveHero>(109);
|
||||||
|
@ -30,6 +30,7 @@
|
|||||||
#include "../lib/CPlayerState.h"
|
#include "../lib/CPlayerState.h"
|
||||||
#include "../lib/CRandomGenerator.h"
|
#include "../lib/CRandomGenerator.h"
|
||||||
#include "../lib/CSoundBase.h"
|
#include "../lib/CSoundBase.h"
|
||||||
|
#include "../lib/CSkillHandler.h"
|
||||||
#include "../lib/CThreadHelper.h"
|
#include "../lib/CThreadHelper.h"
|
||||||
#include "../lib/GameConstants.h"
|
#include "../lib/GameConstants.h"
|
||||||
#include "../lib/UnlockGuard.h"
|
#include "../lib/UnlockGuard.h"
|
||||||
@ -42,6 +43,7 @@
|
|||||||
#include "../lib/int3.h"
|
#include "../lib/int3.h"
|
||||||
|
|
||||||
#include "../lib/battle/BattleInfo.h"
|
#include "../lib/battle/BattleInfo.h"
|
||||||
|
#include "../lib/callback/GameRandomizer.h"
|
||||||
|
|
||||||
#include "../lib/entities/artifact/ArtifactUtils.h"
|
#include "../lib/entities/artifact/ArtifactUtils.h"
|
||||||
#include "../lib/entities/artifact/CArtifact.h"
|
#include "../lib/entities/artifact/CArtifact.h"
|
||||||
@ -156,7 +158,7 @@ void CGameHandler::levelUpHero(const CGHeroInstance * hero)
|
|||||||
|
|
||||||
// give primary skill
|
// give primary skill
|
||||||
logGlobal->trace("%s got level %d", hero->getNameTranslated(), hero->level);
|
logGlobal->trace("%s got level %d", hero->getNameTranslated(), hero->level);
|
||||||
auto primarySkill = hero->nextPrimarySkill(getRandomGenerator());
|
auto primarySkill = randomizer->rollPrimarySkillForLevelup(hero);
|
||||||
|
|
||||||
SetPrimSkill sps;
|
SetPrimSkill sps;
|
||||||
sps.id = hero->id;
|
sps.id = hero->id;
|
||||||
@ -169,7 +171,7 @@ void CGameHandler::levelUpHero(const CGHeroInstance * hero)
|
|||||||
hlu.player = hero->tempOwner;
|
hlu.player = hero->tempOwner;
|
||||||
hlu.heroId = hero->id;
|
hlu.heroId = hero->id;
|
||||||
hlu.primskill = primarySkill;
|
hlu.primskill = primarySkill;
|
||||||
hlu.skills = hero->getLevelUpProposedSecondarySkills(heroPool->getHeroSkillsRandomGenerator(hero->getHeroTypeID()));
|
hlu.skills = hero->getLevelupSkillCandidates(*randomizer);
|
||||||
|
|
||||||
if (hlu.skills.size() == 0)
|
if (hlu.skills.size() == 0)
|
||||||
{
|
{
|
||||||
@ -510,7 +512,7 @@ CGameHandler::CGameHandler(CVCMIServer * lobby)
|
|||||||
, turnOrder(std::make_unique<TurnOrderProcessor>(this))
|
, turnOrder(std::make_unique<TurnOrderProcessor>(this))
|
||||||
, queries(std::make_unique<QueriesProcessor>())
|
, queries(std::make_unique<QueriesProcessor>())
|
||||||
, playerMessages(std::make_unique<PlayerMessageProcessor>(this))
|
, playerMessages(std::make_unique<PlayerMessageProcessor>(this))
|
||||||
, randomNumberGenerator(std::make_unique<CRandomGenerator>())
|
, randomizer(std::make_unique<GameRandomizer>(*this))
|
||||||
, complainNoCreatures("No creatures to split")
|
, complainNoCreatures("No creatures to split")
|
||||||
, complainNotEnoughCreatures("Cannot split that stack, not enough creatures!")
|
, complainNotEnoughCreatures("Cannot split that stack, not enough creatures!")
|
||||||
, complainInvalidSlot("Invalid slot accessed!")
|
, complainInvalidSlot("Invalid slot accessed!")
|
||||||
@ -539,25 +541,19 @@ void CGameHandler::init(StartInfo *si, Load::ProgressAccumulator & progressTrack
|
|||||||
{
|
{
|
||||||
int requestedSeed = settings["server"]["seed"].Integer();
|
int requestedSeed = settings["server"]["seed"].Integer();
|
||||||
if (requestedSeed != 0)
|
if (requestedSeed != 0)
|
||||||
randomNumberGenerator->setSeed(requestedSeed);
|
randomizer->setSeed(requestedSeed);
|
||||||
logGlobal->info("Using random seed: %d", randomNumberGenerator->nextInt());
|
logGlobal->info("Using random seed: %d", randomizer->getDefault().nextInt());
|
||||||
|
|
||||||
CMapService mapService;
|
CMapService mapService;
|
||||||
gs = std::make_shared<CGameState>(this);
|
gs = std::make_shared<CGameState>(this);
|
||||||
gs->preInit(LIBRARY);
|
gs->preInit(LIBRARY);
|
||||||
logGlobal->info("Gamestate created!");
|
logGlobal->info("Gamestate created!");
|
||||||
gs->init(&mapService, si, getRandomGenerator(), progressTracking);
|
gs->init(&mapService, si, *randomizer, progressTracking);
|
||||||
logGlobal->info("Gamestate initialized!");
|
logGlobal->info("Gamestate initialized!");
|
||||||
|
|
||||||
for (const auto & elem : gameState().players)
|
for (const auto & elem : gameState().players)
|
||||||
turnOrder->addPlayer(elem.first);
|
turnOrder->addPlayer(elem.first);
|
||||||
|
|
||||||
for (const auto & elem : gameState().getMap().getObjects<CGHeroInstance>())
|
|
||||||
heroPool->getHeroSkillsRandomGenerator(elem->getHeroTypeID()); // init RMG seed
|
|
||||||
|
|
||||||
for (const auto & elem : gameState().getMap().getHeroesInPool())
|
|
||||||
heroPool->getHeroSkillsRandomGenerator(elem); // init RMG seed
|
|
||||||
|
|
||||||
reinitScripting();
|
reinitScripting();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -687,7 +683,7 @@ void CGameHandler::onNewTurn()
|
|||||||
{
|
{
|
||||||
SetAvailableArtifacts saa;
|
SetAvailableArtifacts saa;
|
||||||
saa.id = ObjectInstanceID::NONE;
|
saa.id = ObjectInstanceID::NONE;
|
||||||
pickAllowedArtsSet(saa.arts, getRandomGenerator());
|
saa.arts = randomizer->rollMarketArtifactSet();
|
||||||
sendAndApply(saa);
|
sendAndApply(saa);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -700,10 +696,8 @@ void CGameHandler::onNewTurn()
|
|||||||
for (auto & elem : gameState().getMap().getObjects())
|
for (auto & elem : gameState().getMap().getObjects())
|
||||||
{
|
{
|
||||||
if (elem)
|
if (elem)
|
||||||
elem->newTurn(*this);
|
elem->newTurn(*this, *randomizer);
|
||||||
}
|
}
|
||||||
|
|
||||||
synchronizeArtifactHandlerLists(); //new day events may have changed them. TODO better of managing that
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void CGameHandler::start(bool resume)
|
void CGameHandler::start(bool resume)
|
||||||
@ -4063,13 +4057,6 @@ void CGameHandler::spawnWanderingMonsters(CreatureID creatureID)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void CGameHandler::synchronizeArtifactHandlerLists()
|
|
||||||
{
|
|
||||||
UpdateArtHandlerLists uahl;
|
|
||||||
uahl.allocatedArtifacts = gameState().allocatedArtifacts;
|
|
||||||
sendAndApply(uahl);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool CGameHandler::isBlockedByQueries(const CPackForServer *pack, PlayerColor player)
|
bool CGameHandler::isBlockedByQueries(const CPackForServer *pack, PlayerColor player)
|
||||||
{
|
{
|
||||||
if (dynamic_cast<const PlayerMessage *>(pack) != nullptr)
|
if (dynamic_cast<const PlayerMessage *>(pack) != nullptr)
|
||||||
@ -4237,7 +4224,7 @@ void CGameHandler::showInfoDialog(InfoWindow * iw)
|
|||||||
|
|
||||||
vstd::RNG & CGameHandler::getRandomGenerator()
|
vstd::RNG & CGameHandler::getRandomGenerator()
|
||||||
{
|
{
|
||||||
return *randomNumberGenerator;
|
return randomizer->getDefault();
|
||||||
}
|
}
|
||||||
|
|
||||||
#if SCRIPTING_ENABLED
|
#if SCRIPTING_ENABLED
|
||||||
@ -4266,7 +4253,7 @@ std::shared_ptr<CGObjectInstance> CGameHandler::createNewObject(const int3 & vis
|
|||||||
auto handler = LIBRARY->objtypeh->getHandlerFor(objectID, subID);
|
auto handler = LIBRARY->objtypeh->getHandlerFor(objectID, subID);
|
||||||
|
|
||||||
auto o = handler->create(gameState().cb, nullptr);
|
auto o = handler->create(gameState().cb, nullptr);
|
||||||
handler->configureObject(o.get(), getRandomGenerator());
|
handler->configureObject(o.get(), *randomizer);
|
||||||
assert(o->ID == objectID);
|
assert(o->ID == objectID);
|
||||||
gameState().getMap().generateUniqueInstanceName(o.get());
|
gameState().getMap().generateUniqueInstanceName(o.get());
|
||||||
|
|
||||||
@ -4317,7 +4304,7 @@ void CGameHandler::createHole(const int3 & visitablePosition, PlayerColor initia
|
|||||||
|
|
||||||
void CGameHandler::newObject(std::shared_ptr<CGObjectInstance> object, PlayerColor initiator)
|
void CGameHandler::newObject(std::shared_ptr<CGObjectInstance> object, PlayerColor initiator)
|
||||||
{
|
{
|
||||||
object->initObj(getRandomGenerator());
|
object->initObj(*randomizer);
|
||||||
|
|
||||||
NewObject no;
|
NewObject no;
|
||||||
no.newObject = object;
|
no.newObject = object;
|
||||||
|
@ -27,6 +27,7 @@ class CConnection;
|
|||||||
class CCommanderInstance;
|
class CCommanderInstance;
|
||||||
class EVictoryLossCheckResult;
|
class EVictoryLossCheckResult;
|
||||||
class CRandomGenerator;
|
class CRandomGenerator;
|
||||||
|
class GameRandomizer;
|
||||||
|
|
||||||
struct CPackForServer;
|
struct CPackForServer;
|
||||||
struct NewTurn;
|
struct NewTurn;
|
||||||
@ -65,7 +66,7 @@ public:
|
|||||||
std::unique_ptr<TurnOrderProcessor> turnOrder;
|
std::unique_ptr<TurnOrderProcessor> turnOrder;
|
||||||
std::unique_ptr<TurnTimerHandler> turnTimerHandler;
|
std::unique_ptr<TurnTimerHandler> turnTimerHandler;
|
||||||
std::unique_ptr<NewTurnProcessor> newTurnProcessor;
|
std::unique_ptr<NewTurnProcessor> newTurnProcessor;
|
||||||
std::unique_ptr<CRandomGenerator> randomNumberGenerator;
|
std::unique_ptr<GameRandomizer> randomizer;
|
||||||
std::shared_ptr<CGameState> gs;
|
std::shared_ptr<CGameState> gs;
|
||||||
|
|
||||||
//use enums as parameters, because doMove(sth, true, false, true) is not readable
|
//use enums as parameters, because doMove(sth, true, false, true) is not readable
|
||||||
@ -150,7 +151,6 @@ public:
|
|||||||
bool saveArtifactsCostume(const PlayerColor & player, const ObjectInstanceID heroID, uint32_t costumeIdx);
|
bool saveArtifactsCostume(const PlayerColor & player, const ObjectInstanceID heroID, uint32_t costumeIdx);
|
||||||
bool switchArtifactsCostume(const PlayerColor & player, const ObjectInstanceID heroID, uint32_t costumeIdx);
|
bool switchArtifactsCostume(const PlayerColor & player, const ObjectInstanceID heroID, uint32_t costumeIdx);
|
||||||
bool eraseArtifactByClient(const ArtifactLocation & al);
|
bool eraseArtifactByClient(const ArtifactLocation & al);
|
||||||
void synchronizeArtifactHandlerLists();
|
|
||||||
|
|
||||||
void heroVisitCastle(const CGTownInstance * obj, const CGHeroInstance * hero) override;
|
void heroVisitCastle(const CGTownInstance * obj, const CGHeroInstance * hero) override;
|
||||||
void stopHeroVisitCastle(const CGTownInstance * obj, const CGHeroInstance * hero) override;
|
void stopHeroVisitCastle(const CGTownInstance * obj, const CGHeroInstance * hero) override;
|
||||||
@ -247,7 +247,7 @@ public:
|
|||||||
template <typename Handler> void serialize(Handler &h)
|
template <typename Handler> void serialize(Handler &h)
|
||||||
{
|
{
|
||||||
h & QID;
|
h & QID;
|
||||||
h & *randomNumberGenerator;
|
h & *randomizer;
|
||||||
h & *battles;
|
h & *battles;
|
||||||
h & *heroPool;
|
h & *heroPool;
|
||||||
h & *playerMessages;
|
h & *playerMessages;
|
||||||
|
@ -21,6 +21,7 @@
|
|||||||
#include "../../lib/battle/CObstacleInstance.h"
|
#include "../../lib/battle/CObstacleInstance.h"
|
||||||
#include "../../lib/battle/IBattleState.h"
|
#include "../../lib/battle/IBattleState.h"
|
||||||
#include "../../lib/battle/BattleAction.h"
|
#include "../../lib/battle/BattleAction.h"
|
||||||
|
#include "../../lib/callback/GameRandomizer.h"
|
||||||
#include "../../lib/entities/building/TownFortifications.h"
|
#include "../../lib/entities/building/TownFortifications.h"
|
||||||
#include "../../lib/gameState/CGameState.h"
|
#include "../../lib/gameState/CGameState.h"
|
||||||
#include "../../lib/networkPacks/PacksForClientBattle.h"
|
#include "../../lib/networkPacks/PacksForClientBattle.h"
|
||||||
@ -938,37 +939,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))
|
||||||
{
|
|
||||||
auto goodLuckChanceVector = gameHandler->getSettings().getVector(EGameSettings::COMBAT_GOOD_LUCK_CHANCE);
|
|
||||||
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(goodLuckChanceVector.size() > 0 && gameHandler->getRandomGenerator().nextInt(1, luckDiceSize) <= goodLuckChanceVector[chanceIndex])
|
|
||||||
bat.flags |= BattleAttack::LUCKY;
|
bat.flags |= BattleAttack::LUCKY;
|
||||||
}
|
|
||||||
|
|
||||||
if(attackerLuck < 0)
|
if(attackerLuck < 0 && gameHandler->randomizer->rollBadLuck(ownerArmy, -attackerLuck))
|
||||||
{
|
|
||||||
auto badLuckChanceVector = gameHandler->getSettings().getVector(EGameSettings::COMBAT_BAD_LUCK_CHANCE);
|
|
||||||
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(badLuckChanceVector.size() > 0 && gameHandler->getRandomGenerator().nextInt(1, luckDiceSize) <= badLuckChanceVector[chanceIndex])
|
|
||||||
bat.flags |= BattleAttack::UNLUCKY;
|
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1126,6 +1112,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));
|
||||||
@ -1169,7 +1157,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
|
||||||
@ -1325,9 +1313,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;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1351,10 +1341,8 @@ 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;
|
int chanceToTrigger = attacker->valOfBonuses(BonusType::TRANSMUTATION);
|
||||||
vstd::amin(chanceToTrigger, 1); //cap at 100%
|
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];
|
||||||
@ -1401,24 +1389,22 @@ 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%
|
if (!gameHandler->randomizer->rollCombatAbility(ownerArmy, chanceToTrigger))
|
||||||
|
|
||||||
if(gameHandler->getRandomGenerator().nextDouble(0, 1) > chanceToTrigger)
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
BattleStackAttacked bsa;
|
BattleStackAttacked bsa;
|
||||||
|
@ -19,6 +19,7 @@
|
|||||||
#include "../../lib/IGameSettings.h"
|
#include "../../lib/IGameSettings.h"
|
||||||
#include "../../lib/battle/CBattleInfoCallback.h"
|
#include "../../lib/battle/CBattleInfoCallback.h"
|
||||||
#include "../../lib/battle/IBattleState.h"
|
#include "../../lib/battle/IBattleState.h"
|
||||||
|
#include "../../lib/callback/GameRandomizer.h"
|
||||||
#include "../../lib/entities/building/TownFortifications.h"
|
#include "../../lib/entities/building/TownFortifications.h"
|
||||||
#include "../../lib/gameState/CGameState.h"
|
#include "../../lib/gameState/CGameState.h"
|
||||||
#include "../../lib/mapObjects/CGTownInstance.h"
|
#include "../../lib/mapObjects/CGTownInstance.h"
|
||||||
@ -347,11 +348,8 @@ 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 badMoraleChanceVector = gameHandler->getSettings().getVector(EGameSettings::COMBAT_BAD_MORALE_CHANCE);
|
ObjectInstanceID ownerArmy = battle.getBattle()->getSideArmy(next->unitSide())->id;
|
||||||
int moraleDiceSize = gameHandler->getSettings().getInteger(EGameSettings::COMBAT_MORALE_DICE_SIZE);
|
if (gameHandler->randomizer->rollBadMorale(ownerArmy, -nextStackMorale))
|
||||||
size_t chanceIndex = std::min<size_t>(badMoraleChanceVector.size(), -nextStackMorale) - 1; // array index, so 0-indexed
|
|
||||||
|
|
||||||
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;
|
||||||
@ -392,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;
|
||||||
@ -464,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;
|
||||||
@ -489,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();
|
||||||
@ -527,11 +525,8 @@ bool BattleFlowProcessor::rollGoodMorale(const CBattleInfoCallback & battle, con
|
|||||||
&& next->canMove()
|
&& next->canMove()
|
||||||
&& nextStackMorale > 0)
|
&& nextStackMorale > 0)
|
||||||
{
|
{
|
||||||
auto goodMoraleChanceVector = gameHandler->getSettings().getVector(EGameSettings::COMBAT_GOOD_MORALE_CHANCE);
|
ObjectInstanceID ownerArmy = battle.getBattle()->getSideArmy(next->unitSide())->id;
|
||||||
int moraleDiceSize = gameHandler->getSettings().getInteger(EGameSettings::COMBAT_MORALE_DICE_SIZE);
|
if (gameHandler->randomizer->rollGoodMorale(ownerArmy, nextStackMorale))
|
||||||
size_t chanceIndex = std::min<size_t>(goodMoraleChanceVector.size(), nextStackMorale) - 1; // array index, so 0-indexed
|
|
||||||
|
|
||||||
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();
|
||||||
@ -743,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))
|
||||||
{
|
{
|
||||||
@ -752,15 +748,12 @@ void BattleFlowProcessor::stackTurnTrigger(const CBattleInfoCallback & battle, c
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (fearsomeCreature)
|
if (fearsomeCreature && gameHandler->randomizer->rollCombatAbility(opponentArmyID, 10)) //fixed 10%
|
||||||
{
|
|
||||||
if (gameHandler->getRandomGenerator().nextInt(99) < 10) //fixed 10%
|
|
||||||
{
|
{
|
||||||
bte.effect = vstd::to_underlying(BonusType::FEAR);
|
bte.effect = vstd::to_underlying(BonusType::FEAR);
|
||||||
gameHandler->sendAndApply(bte);
|
gameHandler->sendAndApply(bte);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
BonusList bl = *(st->getBonuses(Selector::type()(BonusType::ENCHANTER)));
|
BonusList bl = *(st->getBonuses(Selector::type()(BonusType::ENCHANTER)));
|
||||||
bl.remove_if([](const Bonus * b)
|
bl.remove_if([](const Bonus * b)
|
||||||
{
|
{
|
||||||
|
@ -368,17 +368,6 @@ CGHeroInstance * HeroPoolProcessor::pickHeroFor(bool isNative, const PlayerColor
|
|||||||
return *RandomGeneratorUtil::nextItem(possibleHeroes, getRandomGenerator(player));
|
return *RandomGeneratorUtil::nextItem(possibleHeroes, getRandomGenerator(player));
|
||||||
}
|
}
|
||||||
|
|
||||||
vstd::RNG & HeroPoolProcessor::getHeroSkillsRandomGenerator(const HeroTypeID & hero)
|
|
||||||
{
|
|
||||||
if (heroSeed.count(hero) == 0)
|
|
||||||
{
|
|
||||||
int seed = gameHandler->getRandomGenerator().nextInt();
|
|
||||||
heroSeed.emplace(hero, std::make_unique<CRandomGenerator>(seed));
|
|
||||||
}
|
|
||||||
|
|
||||||
return *heroSeed.at(hero);
|
|
||||||
}
|
|
||||||
|
|
||||||
vstd::RNG & HeroPoolProcessor::getRandomGenerator(const PlayerColor & player)
|
vstd::RNG & HeroPoolProcessor::getRandomGenerator(const PlayerColor & player)
|
||||||
{
|
{
|
||||||
if (playerSeed.count(player) == 0)
|
if (playerSeed.count(player) == 0)
|
||||||
|
@ -38,9 +38,6 @@ class HeroPoolProcessor : boost::noncopyable
|
|||||||
/// per-player random generators
|
/// per-player random generators
|
||||||
std::map<PlayerColor, std::unique_ptr<CRandomGenerator>> playerSeed;
|
std::map<PlayerColor, std::unique_ptr<CRandomGenerator>> playerSeed;
|
||||||
|
|
||||||
/// per-hero random generators used to randomize skills
|
|
||||||
std::map<HeroTypeID, std::unique_ptr<CRandomGenerator>> heroSeed;
|
|
||||||
|
|
||||||
void clearHeroFromSlot(const PlayerColor & color, TavernHeroSlot slot);
|
void clearHeroFromSlot(const PlayerColor & color, TavernHeroSlot slot);
|
||||||
void selectNewHeroForSlot(const PlayerColor & color, TavernHeroSlot slot, bool needNativeHero, bool giveStartingArmy, const HeroTypeID & nextHero = HeroTypeID::NONE);
|
void selectNewHeroForSlot(const PlayerColor & color, TavernHeroSlot slot, bool needNativeHero, bool giveStartingArmy, const HeroTypeID & nextHero = HeroTypeID::NONE);
|
||||||
|
|
||||||
@ -63,14 +60,17 @@ public:
|
|||||||
|
|
||||||
void onNewWeek(const PlayerColor & color);
|
void onNewWeek(const PlayerColor & color);
|
||||||
|
|
||||||
vstd::RNG & getHeroSkillsRandomGenerator(const HeroTypeID & hero);
|
|
||||||
|
|
||||||
/// Incoming net pack handling
|
/// Incoming net pack handling
|
||||||
bool hireHero(const ObjectInstanceID & objectID, const HeroTypeID & hid, const PlayerColor & player, const HeroTypeID & nextHero);
|
bool hireHero(const ObjectInstanceID & objectID, const HeroTypeID & hid, const PlayerColor & player, const HeroTypeID & nextHero);
|
||||||
|
|
||||||
template <typename Handler> void serialize(Handler &h)
|
template <typename Handler> void serialize(Handler &h)
|
||||||
{
|
{
|
||||||
h & playerSeed;
|
h & playerSeed;
|
||||||
h & heroSeed;
|
if (!h.hasFeature(Handler::Version::RANDOMIZATION_REWORK))
|
||||||
|
{
|
||||||
|
std::map<HeroTypeID, std::unique_ptr<CRandomGenerator>> heroSeedUnused;
|
||||||
|
h & heroSeedUnused;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -18,6 +18,7 @@
|
|||||||
#include "../../lib/IGameSettings.h"
|
#include "../../lib/IGameSettings.h"
|
||||||
#include "../../lib/StartInfo.h"
|
#include "../../lib/StartInfo.h"
|
||||||
#include "../../lib/TerrainHandler.h"
|
#include "../../lib/TerrainHandler.h"
|
||||||
|
#include "../../lib/callback/GameRandomizer.h"
|
||||||
#include "../../lib/constants/StringConstants.h"
|
#include "../../lib/constants/StringConstants.h"
|
||||||
#include "../../lib/entities/building/CBuilding.h"
|
#include "../../lib/entities/building/CBuilding.h"
|
||||||
#include "../../lib/entities/faction/CTownHandler.h"
|
#include "../../lib/entities/faction/CTownHandler.h"
|
||||||
@ -524,7 +525,7 @@ std::tuple<EWeekType, CreatureID> NewTurnProcessor::pickWeekType(bool newMonth)
|
|||||||
{
|
{
|
||||||
if (gameHandler->getSettings().getBoolean(EGameSettings::CREATURES_ALLOW_ALL_FOR_DOUBLE_MONTH))
|
if (gameHandler->getSettings().getBoolean(EGameSettings::CREATURES_ALLOW_ALL_FOR_DOUBLE_MONTH))
|
||||||
{
|
{
|
||||||
CreatureID creatureID = LIBRARY->creh->pickRandomMonster(gameHandler->getRandomGenerator());
|
CreatureID creatureID = gameHandler->randomizer->rollCreature();
|
||||||
return { EWeekType::DOUBLE_GROWTH, creatureID};
|
return { EWeekType::DOUBLE_GROWTH, creatureID};
|
||||||
}
|
}
|
||||||
else if (LIBRARY->creh->doubledCreatures.size())
|
else if (LIBRARY->creh->doubledCreatures.size())
|
||||||
@ -551,7 +552,7 @@ std::tuple<EWeekType, CreatureID> NewTurnProcessor::pickWeekType(bool newMonth)
|
|||||||
std::pair<int, CreatureID> newMonster(54, CreatureID());
|
std::pair<int, CreatureID> newMonster(54, CreatureID());
|
||||||
do
|
do
|
||||||
{
|
{
|
||||||
newMonster.second = LIBRARY->creh->pickRandomMonster(gameHandler->getRandomGenerator());
|
newMonster.second = gameHandler->randomizer->rollCreature();
|
||||||
} while (newMonster.second.toEntity(LIBRARY)->getFactionID().toFaction()->town == nullptr); // find first non neutral creature
|
} while (newMonster.second.toEntity(LIBRARY)->getFactionID().toFaction()->town == nullptr); // find first non neutral creature
|
||||||
|
|
||||||
return { EWeekType::BONUS_GROWTH, newMonster.second};
|
return { EWeekType::BONUS_GROWTH, newMonster.second};
|
||||||
|
@ -25,6 +25,9 @@ TimerPauseQuery::TimerPauseQuery(CGameHandler * owner, PlayerColor player):
|
|||||||
|
|
||||||
bool TimerPauseQuery::blocksPack(const CPackForServer *pack) const
|
bool TimerPauseQuery::blocksPack(const CPackForServer *pack) const
|
||||||
{
|
{
|
||||||
|
if(dynamic_cast<const SaveGame*>(pack) != nullptr)
|
||||||
|
return false;
|
||||||
|
|
||||||
return blockAllButReply(pack);
|
return blockAllButReply(pack);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -20,12 +20,13 @@
|
|||||||
#include "../../lib/networkPacks/PacksForClient.h"
|
#include "../../lib/networkPacks/PacksForClient.h"
|
||||||
#include "../../lib/networkPacks/PacksForClientBattle.h"
|
#include "../../lib/networkPacks/PacksForClientBattle.h"
|
||||||
#include "../../lib/networkPacks/SetStackEffect.h"
|
#include "../../lib/networkPacks/SetStackEffect.h"
|
||||||
|
#include "../../lib/CRandomGenerator.h"
|
||||||
#include "../../lib/StartInfo.h"
|
#include "../../lib/StartInfo.h"
|
||||||
#include "../../lib/TerrainHandler.h"
|
#include "../../lib/TerrainHandler.h"
|
||||||
|
|
||||||
#include "../../lib/battle/BattleInfo.h"
|
#include "../../lib/battle/BattleInfo.h"
|
||||||
#include "../../lib/battle/BattleLayout.h"
|
#include "../../lib/battle/BattleLayout.h"
|
||||||
#include "../../lib/CRandomGenerator.h"
|
#include "../../lib/callback/GameRandomizer.h"
|
||||||
#include "../../lib/CStack.h"
|
#include "../../lib/CStack.h"
|
||||||
|
|
||||||
#include "../../lib/filesystem/ResourcePath.h"
|
#include "../../lib/filesystem/ResourcePath.h"
|
||||||
@ -179,9 +180,9 @@ public:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
CRandomGenerator randomGenerator;
|
GameRandomizer randomizer(*gameState);
|
||||||
Load::ProgressAccumulator progressTracker;
|
Load::ProgressAccumulator progressTracker;
|
||||||
gameState->init(&mapService, &si, randomGenerator, progressTracker, false);
|
gameState->init(&mapService, &si, randomizer, progressTracker, false);
|
||||||
|
|
||||||
ASSERT_NE(map, nullptr);
|
ASSERT_NE(map, nullptr);
|
||||||
ASSERT_EQ(map->getHeroesOnMap().size(), 2);
|
ASSERT_EQ(map->getHeroesOnMap().size(), 2);
|
||||||
|
Reference in New Issue
Block a user