1
0
mirror of https://github.com/vcmi/vcmi.git synced 2025-11-25 22:42:04 +02:00

Extract library entity randomization logic to separate class

This commit is contained in:
Ivan Savenko
2025-05-16 17:20:56 +03:00
parent 184e841b16
commit 54a46b77a9
55 changed files with 605 additions and 441 deletions

View File

@@ -1357,31 +1357,6 @@ CCreatureHandler::~CCreatureHandler()
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()
{

View File

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

View File

@@ -477,6 +477,7 @@ set(lib_MAIN_HEADERS
callback/IGameEventCallback.h
callback/IGameEventsReceiver.h
callback/IGameInfoCallback.h
callback/IGameRandomizer.h
campaign/CampaignConstants.h
campaign/CampaignHandler.h

View File

@@ -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)
{
for (auto const & spellID : gameState().getMap().allowedSpells)

View File

@@ -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 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);
#if SCRIPTING_ENABLED

View File

@@ -162,10 +162,6 @@ public:
virtual bool isTeleportChannelUnidirectional(TeleportChannelID id, PlayerColor player = PlayerColor::UNFLAGGABLE) 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
virtual scripting::Pool * getGlobalContextPool() const = 0;
#endif

View File

@@ -0,0 +1,46 @@
/*
* 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
enum class EArtifactClass;
namespace vstd
{
class RNG;
}
/// Provides source of random rolls for game entities
/// Instance of this interface only exists on server
class IGameRandomizer : boost::noncopyable
{
public:
virtual ~IGameRandomizer();
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 HeroTypeID rollHero(PlayerColor player, FactionID faction) = 0;
virtual std::string rollTownName(FactionID faction) = 0;
virtual vstd::RNG & getDefault() = 0;
};
VCMI_LIB_NAMESPACE_END

View File

@@ -32,6 +32,7 @@
#include "../bonuses/Updaters.h"
#include "../battle/BattleInfo.h"
#include "../callback/IGameInfoCallback.h"
#include "../callback/IGameRandomizer.h"
#include "../campaign/CampaignState.h"
#include "../constants/StringConstants.h"
#include "../entities/artifact/ArtifactUtils.h"
@@ -173,13 +174,14 @@ void CGameState::preInit(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(cb);
scenarioOps = CMemorySerializer::deepCopy(*si);
initialOpts = CMemorySerializer::deepCopy(*si);
si = nullptr;
auto & randomGenerator = gameRandomizer.getDefault();
switch(scenarioOps->mode)
{
@@ -206,16 +208,16 @@ void CGameState::init(const IMapService * mapService, StartInfo * si, vstd::RNG
removeHeroPlaceholders();
initGrailPosition(randomGenerator);
initRandomFactionsForPlayers(randomGenerator);
randomizeMapObjects(randomGenerator);
randomizeMapObjects(gameRandomizer);
placeStartingHeroes(randomGenerator);
initOwnedObjects();
initDifficulty();
initHeroes(randomGenerator);
initStartingBonus(randomGenerator);
initHeroes(gameRandomizer);
initStartingBonus(gameRandomizer);
initTowns(randomGenerator);
initTownNames(randomGenerator);
placeHeroesInTowns();
initMapObjects(randomGenerator);
initMapObjects(gameRandomizer);
buildBonusSystemTree();
initVisitingAndGarrisonedHeroes();
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");
for(const auto & object : map->getObjects())
{
object->pickRandomObject(randomGenerator);
object->pickRandomObject(gameRandomizer);
//handle Favouring Winds - mark tiles under it
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
for (auto heroID : map->getHeroesOnMap())
@@ -605,7 +607,7 @@ void CGameState::initHeroes(vstd::RNG & randomGenerator)
logGlobal->warn("Hero with uninitialized owner!");
continue;
}
hero->initHero(randomGenerator);
hero->initHero(gameRandomizer.getDefault());
}
// 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 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->appearance = handler->getTemplates().front();
@@ -648,7 +650,7 @@ void CGameState::initHeroes(vstd::RNG & randomGenerator)
heroInPool = newHeroPtr.get();
}
map->generateUniqueInstanceName(heroInPool);
heroInPool->initHero(randomGenerator);
heroInPool->initHero(gameRandomizer.getDefault());
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)
return;
@@ -697,25 +699,25 @@ void CGameState::initStartingBonus(vstd::RNG & randomGenerator)
{
//starting bonus
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)
{
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;
case PlayerStartingBonus::RESOURCE:
{
auto res = (*LIBRARY->townh)[scenarioOps->playerInfos[elem.first].castle]->town->primaryRes;
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::ORE] += amount;
}
else
{
elem.second.resources[res] += randomGenerator.nextInt(3, 6);
elem.second.resources[res] += gameRandomizer.getDefault().nextInt(3, 6);
}
break;
}
@@ -726,7 +728,7 @@ void CGameState::initStartingBonus(vstd::RNG & randomGenerator)
logGlobal->error("Cannot give starting artifact - no heroes!");
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];
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");
for(auto & obj : map->getObjects())
obj->initObj(randomGenerator);
obj->initObj(gameRandomizer);
logGlobal->debug("\tObject initialization done");
for(auto & q : map->getObjects<CGSeerHut>())
@@ -1611,71 +1613,6 @@ TeamState::TeamState()
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)
{
return map->createScroll(spellId);

View File

@@ -22,6 +22,7 @@ VCMI_LIB_NAMESPACE_BEGIN
class EVictoryLossCheckResult;
class Services;
class IGameRandomizer;
class IMapService;
class CMap;
class CSaveFile;
@@ -76,7 +77,7 @@ public:
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);
ui32 day; //total number of days in game
@@ -103,12 +104,6 @@ public:
void calculatePaths(const std::shared_ptr<PathfinderConfig> & config) const override;
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
CArtifactInstance * createScroll(const SpellID & spellId);
@@ -201,19 +196,19 @@ private:
void initGrailPosition(vstd::RNG & randomGenerator);
void initRandomFactionsForPlayers(vstd::RNG & randomGenerator);
void initOwnedObjects();
void randomizeMapObjects(vstd::RNG & randomGenerator);
void randomizeMapObjects(IGameRandomizer & gameRandomizer);
void initPlayerStates();
void placeStartingHeroes(vstd::RNG & randomGenerator);
void placeStartingHero(const PlayerColor & playerColor, const HeroTypeID & heroTypeId, int3 townPos);
void removeHeroPlaceholders();
void initDifficulty();
void initHeroes(vstd::RNG & randomGenerator);
void initHeroes(IGameRandomizer & gameRandomizer);
void placeHeroesInTowns();
void initFogOfWar();
void initStartingBonus(vstd::RNG & randomGenerator);
void initStartingBonus(IGameRandomizer & gameRandomizer);
void initTowns(vstd::RNG & randomGenerator);
void initTownNames(vstd::RNG & randomGenerator);
void initMapObjects(vstd::RNG & randomGenerator);
void initMapObjects(IGameRandomizer & gameRandomizer);
void initVisitingAndGarrisonedHeroes();
void initCampaign();

View File

@@ -19,6 +19,7 @@
#include "JsonBonus.h"
#include "../callback/IGameInfoCallback.h"
#include "../callback/IGameRandomizer.h"
#include "../constants/StringConstants.h"
#include "../GameLibrary.h"
#include "../CCreatureHandler.h"
@@ -68,7 +69,7 @@ JsonRandomizationException::JsonRandomizationException(const std::string & messa
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())
return defaultValue;
@@ -82,14 +83,14 @@ JsonRandomizationException::JsonRandomizationException(const std::string & messa
const auto & vector = value.Vector();
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["amount"].isNull())
return loadValue(value["amount"], rng, variables, defaultValue);
si32 min = loadValue(value["min"], rng, variables, 0);
si32 max = loadValue(value["max"], rng, variables, 0);
return loadValue(value["amount"], variables, defaultValue);
si32 min = loadValue(value["min"], variables, 0);
si32 max = loadValue(value["max"], variables, 0);
return rng.nextInt64(min, max);
}
return defaultValue;
@@ -280,25 +281,25 @@ JsonRandomizationException::JsonRandomizationException(const std::string & messa
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;
if (value.isVector())
{
for (const auto & entry : value.Vector())
ret += loadResource(entry, rng, variables);
ret += loadResource(entry, variables);
return ret;
}
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;
}
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{
GameResID::WOOD,
@@ -312,14 +313,14 @@ JsonRandomizationException::JsonRandomizationException(const std::string & messa
std::set<GameResID> potentialPicks = filterKeys(value, defaultResources, variables);
GameResID resourceID = *RandomGeneratorUtil::nextItem(potentialPicks, rng);
si32 resourceAmount = loadValue(value, rng, variables, 0);
si32 resourceAmount = loadValue(value, variables, 0);
TResources ret;
ret[resourceID] = resourceAmount;
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{
PrimarySkill::ATTACK,
@@ -331,7 +332,7 @@ JsonRandomizationException::JsonRandomizationException(const std::string & messa
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::set<PrimarySkill> defaultSkills{
@@ -346,7 +347,7 @@ JsonRandomizationException::JsonRandomizationException(const std::string & messa
for(const auto & pair : value.Struct())
{
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())
@@ -357,13 +358,13 @@ JsonRandomizationException::JsonRandomizationException(const std::string & messa
PrimarySkill skillID = *RandomGeneratorUtil::nextItem(potentialPicks, rng);
defaultSkills.erase(skillID);
ret[skillID.getNum()] += loadValue(element, rng, variables);
ret[skillID.getNum()] += loadValue(element, variables);
}
}
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;
for(const auto & skill : LIBRARY->skillh->objects)
@@ -374,7 +375,7 @@ JsonRandomizationException::JsonRandomizationException(const std::string & messa
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;
if(value.isStruct())
@@ -382,7 +383,7 @@ JsonRandomizationException::JsonRandomizationException(const std::string & messa
for(const auto & pair : value.Struct())
{
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())
@@ -398,13 +399,13 @@ JsonRandomizationException::JsonRandomizationException(const std::string & messa
SecondarySkill skillID = *RandomGeneratorUtil::nextItem(potentialPicks, rng);
defaultSkills.erase(skillID); //avoid dupicates
ret[skillID] = loadValue(element, rng, variables);
ret[skillID] = loadValue(element, variables);
}
}
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;
for(const auto & artifact : LIBRARY->arth->objects)
@@ -413,20 +414,20 @@ JsonRandomizationException::JsonRandomizationException(const std::string & messa
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;
for (const JsonNode & entry : value.Vector())
{
ret.push_back(loadArtifact(entry, rng, variables));
ret.push_back(loadArtifact(entry, variables));
}
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;
for(ArtifactPosition pos(0); pos < ArtifactPosition::BACKPACK_START; ++pos)
@@ -441,7 +442,7 @@ JsonRandomizationException::JsonRandomizationException(const std::string & messa
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;
for(const auto & spell : LIBRARY->spellh->objects)
@@ -458,17 +459,17 @@ JsonRandomizationException::JsonRandomizationException(const std::string & messa
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;
for (const JsonNode & entry : value.Vector())
{
ret.push_back(loadSpell(entry, rng, variables));
ret.push_back(loadSpell(entry, variables));
}
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::set<PlayerColor> defaultPlayers;
@@ -484,7 +485,7 @@ JsonRandomizationException::JsonRandomizationException(const std::string & messa
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;
for(auto & entry : value.Vector())
@@ -494,7 +495,7 @@ JsonRandomizationException::JsonRandomizationException(const std::string & messa
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;
for(auto & entry : value.Vector())
@@ -504,7 +505,7 @@ JsonRandomizationException::JsonRandomizationException(const std::string & messa
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;
@@ -525,7 +526,7 @@ JsonRandomizationException::JsonRandomizationException(const std::string & messa
throw JsonRandomizationException("Invalid creature picked!", value);
stack.setType(pickedCreature.toCreature());
stack.setCount(loadValue(value, rng, variables));
stack.setCount(loadValue(value, variables));
if (!value["upgradeChance"].isNull() && !stack.getCreature()->upgrades.empty())
{
if (int(value["upgradeChance"].Float()) > rng.nextInt(99)) // select random upgrade
@@ -536,12 +537,12 @@ JsonRandomizationException::JsonRandomizationException(const std::string & messa
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;
for (const JsonNode & node : value.Vector())
{
ret.push_back(loadCreature(node, rng, variables));
ret.push_back(loadCreature(node, variables));
}
return ret;
}

View File

@@ -26,6 +26,7 @@ using JsonVector = std::vector<JsonNode>;
struct Bonus;
struct Component;
class CStackBasicDescriptor;
class IGameRandomizer;
class JsonRandomizationException : public std::runtime_error
{
@@ -36,6 +37,8 @@ public:
class JsonRandom : public GameCallbackHolder
{
IGameRandomizer & gameRandomizer;
vstd::RNG & rng;
public:
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);
public:
using GameCallbackHolder::GameCallbackHolder;
JsonRandom(IGameInfoCallback *cb, IGameRandomizer & gameRandomizer);
struct RandomStackInfo
{
@@ -64,29 +67,29 @@ public:
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 loadResource(const JsonNode & value, vstd::RNG & rng, const Variables & variables);
PrimarySkill loadPrimary(const JsonNode & value, vstd::RNG & rng, const Variables & variables);
std::vector<si32> loadPrimaries(const JsonNode & value, vstd::RNG & rng, const Variables & variables);
SecondarySkill loadSecondary(const JsonNode & value, vstd::RNG & rng, const Variables & variables);
std::map<SecondarySkill, si32> loadSecondaries(const JsonNode & value, vstd::RNG & rng, const Variables & variables);
TResources loadResources(const JsonNode & value, const Variables & variables);
TResources loadResource(const JsonNode & value, const Variables & variables);
PrimarySkill loadPrimary(const JsonNode & value, const Variables & variables);
std::vector<si32> loadPrimaries(const JsonNode & value, const Variables & variables);
SecondarySkill loadSecondary(const JsonNode & value, 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);
std::vector<ArtifactID> loadArtifacts(const JsonNode & value, vstd::RNG & rng, const Variables & variables);
std::vector<ArtifactPosition> loadArtifactSlots(const JsonNode & value, vstd::RNG & rng, const Variables & variables);
ArtifactID loadArtifact(const JsonNode & value, const Variables & variables);
std::vector<ArtifactID> loadArtifacts(const JsonNode & value, const Variables & variables);
std::vector<ArtifactPosition> loadArtifactSlots(const JsonNode & value, const Variables & variables);
SpellID loadSpell(const JsonNode & value, vstd::RNG & rng, const Variables & variables);
std::vector<SpellID> loadSpells(const JsonNode & value, vstd::RNG & rng, const Variables & variables);
SpellID loadSpell(const JsonNode & value, const Variables & variables);
std::vector<SpellID> loadSpells(const JsonNode & value, const Variables & variables);
CStackBasicDescriptor loadCreature(const JsonNode & value, vstd::RNG & rng, const Variables & variables);
std::vector<CStackBasicDescriptor> loadCreatures(const JsonNode & value, vstd::RNG & rng, const Variables & variables);
CStackBasicDescriptor loadCreature(const JsonNode & value, 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<PlayerColor> loadColors(const JsonNode & value, vstd::RNG & rng, const Variables & variables);
std::vector<HeroTypeID> loadHeroes(const JsonNode & value, vstd::RNG & rng);
std::vector<HeroClassID> loadHeroClasses(const JsonNode & value, vstd::RNG & rng);
std::vector<PlayerColor> loadColors(const JsonNode & value, const Variables & variables);
std::vector<HeroTypeID> loadHeroes(const JsonNode & value);
std::vector<HeroClassID> loadHeroClasses(const JsonNode & value);
static std::vector<Bonus> loadBonuses(const JsonNode & value);
};

View File

@@ -24,6 +24,7 @@ class ObjectTemplate;
class CGObjectInstance;
class IObjectInfo;
class IGameInfoCallback;
class IGameRandomizer;
/// Class responsible for creation of objects of specific type & subtype
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
/// 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
virtual std::unique_ptr<IObjectInfo> getObjectInfo(std::shared_ptr<const ObjectTemplate> tmpl) const;

View File

@@ -17,14 +17,14 @@ VCMI_LIB_NAMESPACE_BEGIN
template<class ObjectType>
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);
if(castedObject == nullptr)
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
@@ -43,7 +43,7 @@ class CDefaultObjectTypeHandler : public AObjectTypeHandler
protected:
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
{
return std::make_shared<ObjectType>(cb);

View File

@@ -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;
result.variables.preset = presetVariables;
try {
objectInfo.configureObject(result, rand, cb);
objectInfo.configureObject(result, gameRandomizer, cb);
}
catch (const JsonRandomizationException & e)
{
@@ -78,14 +78,14 @@ Rewardable::Configuration CRewardableConstructor::generateConfiguration(IGameInf
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);
if (!rewardableObject)
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();
if (rewardableObject->configuration.info.empty())

View File

@@ -30,11 +30,11 @@ public:
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;
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

View File

@@ -79,16 +79,16 @@ int ResourceInstanceConstructor::getAmountMultiplier() const
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)
return;
JsonRandom randomizer(object->cb);
JsonRandom randomizer(object->cb, gameRandomizer);
JsonRandom::Variables dummy;
if (!config["amounts"].isNull())
object->amount = randomizer.loadValue(config["amounts"], rng, dummy, 0) * getAmountMultiplier();
object->amount = randomizer.loadValue(config["amounts"], dummy, 0) * getAmountMultiplier();
else
object->amount = 5 * getAmountMultiplier();
}
@@ -136,7 +136,7 @@ void CTownInstanceConstructor::initializeObject(CGTownInstance * obj) const
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);
if(templ)
@@ -227,7 +227,7 @@ std::shared_ptr<const ObjectTemplate> CHeroInstanceConstructor::getOverride(Terr
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;
}
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;
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);
}
}

View File

@@ -61,7 +61,7 @@ public:
int getAmountMultiplier() 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>
@@ -76,7 +76,7 @@ public:
std::map<std::string, LogicalExpression<BuildingID>> filters;
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;
bool hasNameTextID() const override;
@@ -99,7 +99,7 @@ class CHeroInstanceConstructor : public CDefaultObjectTypeHandler<CGHeroInstance
void initTypeData(const JsonNode & input) override;
public:
void randomizeObject(CGHeroInstance * object, vstd::RNG & rng) const override;
void randomizeObject(CGHeroInstance * object, IGameRandomizer & gameRandomizer) const override;
bool hasNameTextID() const override;
std::string getNameTextID() const override;
@@ -138,7 +138,7 @@ class MarketInstanceConstructor : public CDefaultObjectTypeHandler<CGMarket>
void initTypeData(const JsonNode & config) override;
public:
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;
bool hasDescription() const;

View File

@@ -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.reserve(availableCreatures.size());
@@ -128,7 +128,7 @@ void DwellingInstanceConstructor::randomizeObject(CGDwelling * dwelling, vstd::R
{
//custom guards (eg. Elemental Conflux)
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()));
}

View File

@@ -34,7 +34,7 @@ public:
bool hasNameTextID() 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 producesCreature(const CCreature * crea) const;

View File

@@ -17,6 +17,7 @@
#include "../IGameSettings.h"
#include "../callback/IGameInfoCallback.h"
#include "../callback/IGameEventCallback.h"
#include "../callback/IGameRandomizer.h"
#include "../gameState/CGameState.h"
#include "../mapObjectConstructors/CObjectClassesHandler.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);
}
void CGCreature::pickRandomObject(vstd::RNG & rand)
void CGCreature::pickRandomObject(IGameRandomizer & gameRandomizer)
{
switch(ID.toEnum())
{
case MapObjectID::RANDOM_MONSTER:
subID = LIBRARY->creh->pickRandomMonster(rand);
subID = gameRandomizer.rollCreature();
break;
case MapObjectID::RANDOM_MONSTER_L1:
subID = LIBRARY->creh->pickRandomMonster(rand, 1);
subID = gameRandomizer.rollCreature(1);
break;
case MapObjectID::RANDOM_MONSTER_L2:
subID = LIBRARY->creh->pickRandomMonster(rand, 2);
subID = gameRandomizer.rollCreature(2);
break;
case MapObjectID::RANDOM_MONSTER_L3:
subID = LIBRARY->creh->pickRandomMonster(rand, 3);
subID = gameRandomizer.rollCreature(3);
break;
case MapObjectID::RANDOM_MONSTER_L4:
subID = LIBRARY->creh->pickRandomMonster(rand, 4);
subID = gameRandomizer.rollCreature(4);
break;
case MapObjectID::RANDOM_MONSTER_L5:
subID = LIBRARY->creh->pickRandomMonster(rand, 5);
subID = gameRandomizer.rollCreature(5);
break;
case MapObjectID::RANDOM_MONSTER_L6:
subID = LIBRARY->creh->pickRandomMonster(rand, 6);
subID = gameRandomizer.rollCreature(6);
break;
case MapObjectID::RANDOM_MONSTER_L7:
subID = LIBRARY->creh->pickRandomMonster(rand, 7);
subID = gameRandomizer.rollCreature(7);
break;
}
@@ -266,7 +267,7 @@ void CGCreature::pickRandomObject(vstd::RNG & rand)
setType(ID, subID);
}
void CGCreature::initObj(vstd::RNG & rand)
void CGCreature::initObj(IGameRandomizer & gameRandomizer)
{
blockVisit = true;
switch(character)
@@ -275,13 +276,13 @@ void CGCreature::initObj(vstd::RNG & rand)
character = -4;
break;
case 1:
character = rand.nextInt(1, 7);
character = gameRandomizer.getDefault().nextInt(1, 7);
break;
case 2:
character = rand.nextInt(1, 10);
character = gameRandomizer.getDefault().nextInt(1, 10);
break;
case 3:
character = rand.nextInt(4, 10);
character = gameRandomizer.getDefault().nextInt(4, 10);
break;
case 4:
character = 10;
@@ -292,7 +293,7 @@ void CGCreature::initObj(vstd::RNG & rand)
const Creature * c = getCreature();
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
{
@@ -305,7 +306,7 @@ void CGCreature::initObj(vstd::RNG & rand)
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
if (!notGrowingTeam)
{

View File

@@ -44,9 +44,9 @@ public:
std::string getPopupText(PlayerColor player) const override;
std::string getPopupText(const CGHeroInstance * hero) const override;
std::vector<Component> getPopupComponents(PlayerColor player) const override;
void initObj(vstd::RNG & rand) override;
void pickRandomObject(vstd::RNG & rand) override;
void newTurn(IGameEventCallback & gameEvents) const override;
void initObj(IGameRandomizer & gameRandomizer) override;
void pickRandomObject(IGameRandomizer & gameRandomizer) override;
void newTurn(IGameEventCallback & gameEvents, IGameRandomizer & gameRandomizer) 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;
CreatureID getCreatureID() const;

View File

@@ -12,6 +12,7 @@
#include "CGDwelling.h"
#include "../callback/IGameInfoCallback.h"
#include "../callback/IGameEventCallback.h"
#include "../callback/IGameRandomizer.h"
#include "../serializer/JsonSerializeFormat.h"
#include "../entities/faction/CTownHandler.h"
#include "../mapping/CMap.h"
@@ -53,7 +54,7 @@ CGDwelling::CGDwelling(IGameInfoCallback *cb):
CGDwelling::~CGDwelling() = default;
FactionID CGDwelling::randomizeFaction(vstd::RNG & rand)
FactionID CGDwelling::randomizeFaction(IGameRandomizer & gameRandomizer)
{
if (ID == Obj::RANDOM_DWELLING_FACTION)
return FactionID(subID.getNum());
@@ -90,7 +91,7 @@ FactionID CGDwelling::randomizeFaction(vstd::RNG & rand)
if (linkedTown)
{
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);
if(linkedTown->ID==Obj::TOWN)
@@ -98,7 +99,7 @@ FactionID CGDwelling::randomizeFaction(vstd::RNG & rand)
}
if(!randomizationInfo->allowedFactions.empty())
return *RandomGeneratorUtil::nextItem(randomizationInfo->allowedFactions, rand);
return *RandomGeneratorUtil::nextItem(randomizationInfo->allowedFactions, gameRandomizer.getDefault());
std::vector<FactionID> potentialPicks;
@@ -108,7 +109,7 @@ FactionID CGDwelling::randomizeFaction(vstd::RNG & rand)
potentialPicks.push_back(faction);
assert(!potentialPicks.empty());
return *RandomGeneratorUtil::nextItem(potentialPicks, rand);
return *RandomGeneratorUtil::nextItem(potentialPicks, gameRandomizer.getDefault());
}
int CGDwelling::randomizeLevel(vstd::RNG & rand)
@@ -128,12 +129,12 @@ int CGDwelling::randomizeLevel(vstd::RNG & rand)
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)
{
FactionID faction = randomizeFaction(rand);
int level = randomizeLevel(rand);
FactionID faction = randomizeFaction(gameRandomizer);
int level = randomizeLevel(gameRandomizer.getDefault());
assert(faction != FactionID::NONE && faction != FactionID::NEUTRAL);
assert(level >= 0 && level <= 6);
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));
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);
}
}
void CGDwelling::initObj(vstd::RNG & rand)
void CGDwelling::initObj(IGameRandomizer & gameRandomizer)
{
switch(ID.toEnum())
{
@@ -183,7 +184,7 @@ void CGDwelling::initObj(vstd::RNG & rand)
case Obj::CREATURE_GENERATOR4:
case Obj::WAR_MACHINE_FACTORY:
{
getObjectHandler()->configureObject(this, rand);
getObjectHandler()->configureObject(this, gameRandomizer);
assert(!creatures.empty());
assert(!creatures[0].second.empty());
break;
@@ -278,7 +279,7 @@ void CGDwelling::onHeroVisit(IGameEventCallback & gameEvents, const CGHeroInstan
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
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
{
gameEvents.setObjPropertyID(id, ObjProperty::AVAILABLE_CREATURE, LIBRARY->creh->pickRandomMonster(gameEvents.getRandomGenerator()));
gameEvents.setObjPropertyID(id, ObjProperty::AVAILABLE_CREATURE, gameRandomizer.rollCreature());
}
bool change = false;

View File

@@ -50,13 +50,13 @@ protected:
void serializeJsonOptions(JsonSerializeFormat & handler) override;
private:
FactionID randomizeFaction(vstd::RNG & rand);
FactionID randomizeFaction(IGameRandomizer & gameRandomizer);
int randomizeLevel(vstd::RNG & rand);
void pickRandomObject(vstd::RNG & rand) override;
void initObj(vstd::RNG & rand) override;
void pickRandomObject(IGameRandomizer & gameRandomizer) override;
void initObj(IGameRandomizer & gameRandomizer) 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 battleFinished(IGameEventCallback & gameEvents, const CGHeroInstance *hero, const BattleResult &result) const override;
void blockingDialogAnswered(IGameEventCallback & gameEvents, const CGHeroInstance *hero, int32_t answer) const override;

View File

@@ -17,6 +17,7 @@
#include "../callback/IGameInfoCallback.h"
#include "../callback/IGameEventCallback.h"
#include "../callback/IGameRandomizer.h"
#include "../texts/CGeneralTextHandler.h"
#include "../TerrainHandler.h"
#include "../RoadHandler.h"
@@ -347,7 +348,7 @@ CCommanderInstance * CGHeroInstance::getCommander()
return commander.get();
}
void CGHeroInstance::initObj(vstd::RNG & rand)
void CGHeroInstance::initObj(IGameRandomizer & gameRandomizer)
{
if (ID == Obj::HERO)
updateAppearance();
@@ -663,13 +664,13 @@ void CGHeroInstance::SecondarySkillsInfo::resetWisdomCounter()
wisdomCounter = 0;
}
void CGHeroInstance::pickRandomObject(vstd::RNG & randomGenerator)
void CGHeroInstance::pickRandomObject(IGameRandomizer & gameRandomizer)
{
assert(ID == Obj::HERO || ID == Obj::PRISON || 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;
subID = selectedHero;

View File

@@ -261,7 +261,7 @@ public:
const CCommanderInstance * getCommander() const;
CCommanderInstance * getCommander();
void initObj(vstd::RNG & rand) override;
void initObj(IGameRandomizer & gameRandomizer) override;
void initHero(vstd::RNG & rand);
void initHero(vstd::RNG & rand, const HeroTypeID & SUBID);
@@ -320,7 +320,7 @@ public:
void updateAppearance();
void pickRandomObject(vstd::RNG & rand) override;
void pickRandomObject(IGameRandomizer & gameRandomizer) override;
void onHeroVisit(IGameEventCallback & gameEvents, const CGHeroInstance * h) const override;
std::string getObjectName() const override;
std::string getHoverText(PlayerColor player) const override;

View File

@@ -13,6 +13,7 @@
#include "../callback/IGameInfoCallback.h"
#include "../callback/IGameEventCallback.h"
#include "../callback/IGameRandomizer.h"
#include "../texts/CGeneralTextHandler.h"
#include "../CCreatureHandler.h"
#include "CGTownInstance.h"
@@ -30,9 +31,9 @@ ObjectInstanceID CGMarket::getObjInstanceID() const
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
@@ -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);
@@ -111,7 +112,7 @@ void CGBlackMarket::newTurn(IGameEventCallback & gameEvents) const
SetAvailableArtifacts saa;
saa.id = id;
cb->pickAllowedArtsSet(saa.arts, gameEvents.getRandomGenerator());
saa.arts = gameRandomizer.rollMarketArtifactSet();
gameEvents.sendAndApply(saa);
}

View File

@@ -25,7 +25,7 @@ public:
CGMarket(IGameInfoCallback *cb);
///IObjectInterface
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(const CGHeroInstance * hero) const override;
@@ -44,7 +44,7 @@ public:
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;
template <typename Handler> void serialize(Handler &h)

View File

@@ -160,12 +160,12 @@ void CGObjectInstance::setType(MapObjectID newID, MapObjectSubID newSubID)
cb->gameState().getMap().showObject(this);
}
void CGObjectInstance::pickRandomObject(vstd::RNG & rand)
void CGObjectInstance::pickRandomObject(IGameRandomizer & gameRandomizer)
{
// no-op
}
void CGObjectInstance::initObj(vstd::RNG & rand)
void CGObjectInstance::initObj(IGameRandomizer & gameRandomizer)
{
// no-op
}

View File

@@ -131,8 +131,8 @@ public:
/** OVERRIDES OF IObjectInterface **/
void initObj(vstd::RNG & rand) override;
void pickRandomObject(vstd::RNG & rand) override;
void initObj(IGameRandomizer & gameRandomizer) override;
void pickRandomObject(IGameRandomizer & gameRandomizer) override;
void onHeroVisit(IGameEventCallback & gameEvents, const CGHeroInstance * h) const override;
/// method for synchronous update. Note: For new properties classes should override setPropertyDer instead
void setProperty(ObjProperty what, ObjPropertyID identifier) final;

View File

@@ -42,11 +42,11 @@ void CGPandoraBox::init()
}
}
void CGPandoraBox::initObj(vstd::RNG & rand)
void CGPandoraBox::initObj(IGameRandomizer & gameRandomizer)
{
init();
CRewardableObject::initObj(rand);
CRewardableObject::initObj(gameRandomizer);
}
void CGPandoraBox::grantRewardWithMessage(IGameEventCallback & gameEvents, const CGHeroInstance * h, int index, bool markAsVisit) const

View File

@@ -23,7 +23,7 @@ public:
MetaString message;
void initObj(vstd::RNG & rand) override;
void initObj(IGameRandomizer & gameRandomizer) override;
void onHeroVisit(IGameEventCallback & gameEvents, const CGHeroInstance * h) 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;

View File

@@ -13,6 +13,7 @@
#include "../callback/IGameInfoCallback.h"
#include "../callback/IGameEventCallback.h"
#include "../callback/IGameRandomizer.h"
#include "../mapObjectConstructors/CommonConstructors.h"
#include "../texts/CGeneralTextHandler.h"
#include "../networkPacks/PacksForClient.h"
@@ -52,24 +53,24 @@ std::string CGResource::getHoverText(PlayerColor player) const
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);
if (ID == Obj::RANDOM_RESOURCE)
{
ID = Obj::RESOURCE;
subID = rand.nextInt(EGameResID::WOOD, EGameResID::GOLD);
subID = gameRandomizer.getDefault().nextInt(EGameResID::WOOD, EGameResID::GOLD);
setType(ID, subID);
amount *= getAmountMultiplier();
}
}
void CGResource::initObj(vstd::RNG & rand)
void CGResource::initObj(IGameRandomizer & gameRandomizer)
{
blockVisit = true;
getResourceHandler()->randomizeObject(this, rand);
getResourceHandler()->randomizeObject(this, gameRandomizer);
}
void CGResource::onHeroVisit(IGameEventCallback & gameEvents, const CGHeroInstance * h) const

View File

@@ -35,8 +35,8 @@ public:
using CArmedInstance::CArmedInstance;
void onHeroVisit(IGameEventCallback & gameEvents, const CGHeroInstance * h) const override;
void initObj(vstd::RNG & rand) override;
void pickRandomObject(vstd::RNG & rand) override;
void initObj(IGameRandomizer & gameRandomizer) override;
void pickRandomObject(IGameRandomizer & gameRandomizer) 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;
std::string getHoverText(PlayerColor player) const override;

View File

@@ -26,6 +26,7 @@
#include "../TerrainHandler.h"
#include "../callback/IGameInfoCallback.h"
#include "../callback/IGameEventCallback.h"
#include "../callback/IGameRandomizer.h"
#include "../entities/building/CBuilding.h"
#include "../entities/faction/CTownHandler.h"
#include "../mapObjectConstructors/AObjectTypeHandler.h"
@@ -389,7 +390,7 @@ bool CGTownInstance::townEnvisagesBuilding(BuildingSubID::EBuildingSubID subId)
return getTown()->getBuildingType(subId) != BuildingID::NONE;
}
void CGTownInstance::initializeConfigurableBuildings(vstd::RNG & rand)
void CGTownInstance::initializeConfigurableBuildings(IGameRandomizer & gameRandomizer)
{
for(const auto & kvp : getTown()->buildings)
{
@@ -397,7 +398,7 @@ void CGTownInstance::initializeConfigurableBuildings(vstd::RNG & rand)
continue;
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)
{
@@ -458,13 +459,13 @@ FactionID CGTownInstance::randomizeFaction(vstd::RNG & 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);
if (ID == MapObjectID::RANDOM_TOWN)
{
ID = MapObjectID::TOWN;
subID = randomizeFaction(rand);
subID = randomizeFaction(gameRandomizer.getDefault());
}
assert(ID == Obj::TOWN); // just in case
@@ -473,7 +474,7 @@ void CGTownInstance::pickRandomObject(vstd::RNG & rand)
updateAppearance();
}
void CGTownInstance::initObj(vstd::RNG & rand) ///initialize town structures
void CGTownInstance::initObj(IGameRandomizer & gameRandomizer) ///initialize town structures
{
blockVisit = true;
@@ -493,8 +494,8 @@ void CGTownInstance::initObj(vstd::RNG & rand) ///initialize town structures
creatures[level].second.push_back(getTown()->creatures[level][upgradeNum]);
}
}
initializeConfigurableBuildings(rand);
initializeNeutralTownGarrison(rand);
initializeConfigurableBuildings(gameRandomizer.getDefault());
initializeNeutralTownGarrison(gameRandomizer.getDefault());
recreateBuildingsBonuses();
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)
building.second->newTurn(gameEvents);
building.second->newTurn(gameEvents, gameRandomizer);
if(hasBuilt(BuildingSubID::BANK) && bonusValue.second > 0)
{

View File

@@ -211,11 +211,11 @@ public:
virtual ~CGTownInstance();
///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 onHeroLeave(IGameEventCallback & gameEvents, const CGHeroInstance * h) const override;
void initObj(vstd::RNG & rand) override;
void pickRandomObject(vstd::RNG & rand) override;
void initObj(IGameRandomizer & gameRandomizer) override;
void pickRandomObject(IGameRandomizer & gameRandomizer) override;
void battleFinished(IGameEventCallback & gameEvents, const CGHeroInstance * hero, const BattleResult & result) const override;
std::string getObjectName() const override;
@@ -238,7 +238,7 @@ private:
void onTownCaptured(IGameEventCallback & gameEvents, const PlayerColor & winner) const;
int getDwellingBonus(const std::vector<CreatureID>& creatureIds, const std::vector<const CGObjectInstance* >& dwellings) const;
bool townEnvisagesBuilding(BuildingSubID::EBuildingSubID bid) const;
void initializeConfigurableBuildings(vstd::RNG & rand);
void initializeConfigurableBuildings(IGameRandomizer & gameRandomizer);
void initializeNeutralTownGarrison(vstd::RNG & rand);
};

View File

@@ -19,6 +19,7 @@
#include "../IGameSettings.h"
#include "../callback/IGameInfoCallback.h"
#include "../callback/IGameEventCallback.h"
#include "../callback/IGameRandomizer.h"
#include "../entities/artifact/CArtifact.h"
#include "../entities/hero/CHeroHandler.h"
#include "../mapObjectConstructors/CObjectClassesHandler.h"
@@ -444,11 +445,11 @@ void CGSeerHut::init(vstd::RNG & rand)
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();
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
{
gameEvents.setObjPropertyValue(id, ObjProperty::SEERHUT_COMPLETE, true);
@@ -815,7 +816,7 @@ void CGKeymasterTent::onHeroVisit(IGameEventCallback & gameEvents, const CGHeroI
h->showInfoDialog(gameEvents, txt_id);
}
void CGBorderGuard::initObj(vstd::RNG & rand)
void CGBorderGuard::initObj(IGameRandomizer & gameRandomizer)
{
blockVisit = true;
}

View File

@@ -140,14 +140,14 @@ public:
std::string seerName;
void initObj(vstd::RNG & rand) override;
void initObj(IGameRandomizer & gameRandomizer) override;
std::string getHoverText(PlayerColor player) const override;
std::string getHoverText(const CGHeroInstance * hero) const override;
std::string getPopupText(PlayerColor player) const override;
std::string getPopupText(const CGHeroInstance * hero) const override;
std::vector<Component> getPopupComponents(PlayerColor player) 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 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;
@@ -227,7 +227,7 @@ class DLL_LINKAGE CGBorderGuard : public CGKeys, public IQuestObject
public:
using CGKeys::CGKeys;
void initObj(vstd::RNG & rand) override;
void initObj(IGameRandomizer & gameRandomizer) override;
void onHeroVisit(IGameEventCallback & gameEvents, const CGHeroInstance * h) const override;
void blockingDialogAnswered(IGameEventCallback & gameEvents, const CGHeroInstance *hero, int32_t answer) const override;

View File

@@ -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.rewards)
{
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);
}
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)

View File

@@ -61,7 +61,7 @@ public:
void garrisonDialogClosed(IGameEventCallback & gameEvents, const CGHeroInstance *hero) const override;
///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
void heroLevelUpDone(IGameEventCallback & gameEvents, const CGHeroInstance *hero) const override;
@@ -69,7 +69,7 @@ public:
/// applies player selection of reward
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;

View File

@@ -50,7 +50,7 @@ void FlaggableMapObject::onHeroVisit(IGameEventCallback & gameEvents, const CGHe
gameEvents.showInfoDialog(&iw);
}
void FlaggableMapObject::initObj(vstd::RNG & rand)
void FlaggableMapObject::initObj(IGameRandomizer & gameRandomizer)
{
initBonuses();
}

View File

@@ -28,7 +28,7 @@ public:
using CGObjectInstance::CGObjectInstance;
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;
ResourceSet dailyIncome() const override;

View File

@@ -39,13 +39,13 @@ void IObjectInterface::onHeroVisit(IGameEventCallback & gameEvents, const CGHero
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)

View File

@@ -30,6 +30,7 @@ class CStackInstance;
class CGHeroInstance;
class IGameInfoCallback;
class IGameEventCallback;
class IGameRandomizer;
class ResourceSet;
class int3;
class MetaString;
@@ -55,9 +56,9 @@ public:
/// Called on new turn by server. This method can not modify object state on its own
/// Instead all changes must be propagated via netpacks
virtual void newTurn(IGameEventCallback & gameEvents) const;
virtual void initObj(vstd::RNG & rand); //synchr
virtual void pickRandomObject(vstd::RNG & rand);
virtual void newTurn(IGameEventCallback & gameEvents, IGameRandomizer & gameRandomizer) const;
virtual void initObj(IGameRandomizer & gameRandomizer); //synchr
virtual void pickRandomObject(IGameRandomizer & gameRandomizer);
virtual void setProperty(ObjProperty what, ObjPropertyID identifier);//synchr
//Called when queries created DURING HERO VISIT are resolved

View File

@@ -14,6 +14,7 @@
#include "../bonuses/Propagators.h"
#include "../callback/IGameInfoCallback.h"
#include "../callback/IGameEventCallback.h"
#include "../callback/IGameRandomizer.h"
#include "../constants/StringConstants.h"
#include "../entities/artifact/ArtifactUtils.h"
#include "../entities/artifact/CArtifact.h"
@@ -96,19 +97,19 @@ void CGMine::onHeroVisit(IGameEventCallback & gameEvents, const CGHeroInstance *
flagMine(gameEvents, h->tempOwner);
}
void CGMine::initObj(vstd::RNG & rand)
void CGMine::initObj(IGameRandomizer & gameRandomizer)
{
if(isAbandoned())
{
//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);
putStack(SlotID(0), std::move(troglodytes));
assert(!abandonedMineResources.empty());
if (!abandonedMineResources.empty())
{
producedResource = *RandomGeneratorUtil::nextItem(abandonedMineResources, rand);
producedResource = *RandomGeneratorUtil::nextItem(abandonedMineResources, gameRandomizer.getDefault());
}
else
{
@@ -431,7 +432,7 @@ void CGMonolith::teleportDialogAnswered(IGameEventCallback & gameEvents, const C
gameEvents.moveHero(hero->id, hero->convertFromVisitablePos(dPos), EMovementMode::MONOLITH);
}
void CGMonolith::initObj(vstd::RNG & rand)
void CGMonolith::initObj(IGameRandomizer & gameRandomizer)
{
std::vector<Obj> IDs;
IDs.push_back(ID);
@@ -476,7 +477,7 @@ void CGSubterraneanGate::onHeroVisit(IGameEventCallback & gameEvents, const CGHe
gameEvents.showTeleportDialog(&td);
}
void CGSubterraneanGate::initObj(vstd::RNG & rand)
void CGSubterraneanGate::initObj(IGameRandomizer & gameRandomizer)
{
type = BOTH;
}
@@ -625,24 +626,24 @@ ArtifactID CGArtifact::getArtifactType() const
return getObjTypeIndex().getNum();
}
void CGArtifact::pickRandomObject(vstd::RNG & rand)
void CGArtifact::pickRandomObject(IGameRandomizer & gameRandomizer)
{
switch(ID.toEnum())
{
case MapObjectID::RANDOM_ART:
subID = cb->gameState().pickRandomArtifact(rand, std::nullopt);
subID = gameRandomizer.rollArtifact();
break;
case MapObjectID::RANDOM_TREASURE_ART:
subID = cb->gameState().pickRandomArtifact(rand, EArtifactClass::ART_TREASURE);
subID = gameRandomizer.rollArtifact(EArtifactClass::ART_TREASURE);
break;
case MapObjectID::RANDOM_MINOR_ART:
subID = cb->gameState().pickRandomArtifact(rand, EArtifactClass::ART_MINOR);
subID = gameRandomizer.rollArtifact(EArtifactClass::ART_MINOR);
break;
case MapObjectID::RANDOM_MAJOR_ART:
subID = cb->gameState().pickRandomArtifact(rand, EArtifactClass::ART_MAJOR);
subID = gameRandomizer.rollArtifact(EArtifactClass::ART_MAJOR);
break;
case MapObjectID::RANDOM_RELIC_ART:
subID = cb->gameState().pickRandomArtifact(rand, EArtifactClass::ART_RELIC);
subID = gameRandomizer.rollArtifact(EArtifactClass::ART_RELIC);
break;
}
@@ -660,7 +661,7 @@ void CGArtifact::setArtifactInstance(const CArtifactInstance * instance)
storedArtifact = instance->getId();
}
void CGArtifact::initObj(vstd::RNG & rand)
void CGArtifact::initObj(IGameRandomizer & gameRandomizer)
{
blockVisit = true;
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(message.empty())
{
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);
}
@@ -917,7 +918,7 @@ void CGGarrison::serializeJsonOptions(JsonSerializeFormat& handler)
CArmedInstance::serializeJsonOptions(handler);
}
void CGGarrison::initObj(vstd::RNG &rand)
void CGGarrison::initObj(IGameRandomizer & gameRandomizer)
{
if(this->subID == MapObjectSubID::decode(this->ID, "antiMagic"))
addAntimagicGarrisonBonus();
@@ -934,7 +935,7 @@ void CGGarrison::addAntimagicGarrisonBonus()
this->addNewBonus(bonus);
}
void CGMagi::initObj(vstd::RNG & rand)
void CGMagi::initObj(IGameRandomizer & gameRandomizer)
{
if (ID == Obj::EYE_OF_MAGI)
blockVisit = true;
@@ -1012,7 +1013,7 @@ const CGHeroInstance * CGBoat::getBoardedHero() const
return nullptr;
}
void CGSirens::initObj(vstd::RNG & rand)
void CGSirens::initObj(IGameRandomizer & gameRandomizer)
{
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++;
}

View File

@@ -50,7 +50,7 @@ public:
MetaString message;
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)
{
@@ -68,7 +68,7 @@ public:
bool removableUnits;
void initObj(vstd::RNG &rand) override;
void initObj(IGameRandomizer & gameRandomizer) override;
bool passableFor(PlayerColor color) const override;
void onHeroVisit(IGameEventCallback & gameEvents, const CGHeroInstance * h) 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;
void pick(IGameEventCallback & gameEvents, const CGHeroInstance * h) const;
void initObj(vstd::RNG & rand) override;
void pickRandomObject(vstd::RNG & rand) override;
void initObj(IGameRandomizer & gameRandomizer) override;
void pickRandomObject(IGameRandomizer & gameRandomizer) override;
BattleField getBattlefield() const override;
@@ -151,7 +151,7 @@ private:
void blockingDialogAnswered(IGameEventCallback & gameEvents, const CGHeroInstance *hero, int32_t answer) const override;
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 getHoverText(PlayerColor player) const override;
@@ -238,7 +238,7 @@ class DLL_LINKAGE CGMonolith : public CGTeleport
protected:
void onHeroVisit(IGameEventCallback & gameEvents, const CGHeroInstance * h) 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:
using CGTeleport::CGTeleport;
@@ -252,7 +252,7 @@ public:
class DLL_LINKAGE CGSubterraneanGate : public CGMonolith
{
void onHeroVisit(IGameEventCallback & gameEvents, const CGHeroInstance * h) const override;
void initObj(vstd::RNG & rand) override;
void initObj(IGameRandomizer & gameRandomizer) override;
public:
using CGMonolith::CGMonolith;
@@ -287,7 +287,7 @@ public:
void onHeroVisit(IGameEventCallback & gameEvents, const CGHeroInstance * h) 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)
{
@@ -377,7 +377,7 @@ class DLL_LINKAGE CGMagi : public CGObjectInstance
public:
using CGObjectInstance::CGObjectInstance;
void initObj(vstd::RNG & rand) override;
void initObj(IGameRandomizer & gameRandomizer) override;
void onHeroVisit(IGameEventCallback & gameEvents, const CGHeroInstance * h) const override;
template <typename Handler> void serialize(Handler &h)
@@ -399,7 +399,7 @@ public:
using CTeamVisited::CTeamVisited;
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 getObjectDescription(PlayerColor player) const;

View File

@@ -66,11 +66,11 @@ TownRewardableBuildingInstance::TownRewardableBuildingInstance(IGameInfoCallback
: TownBuildingInstance(cb)
{}
TownRewardableBuildingInstance::TownRewardableBuildingInstance(CGTownInstance * town, const BuildingID & index, vstd::RNG & rand)
TownRewardableBuildingInstance::TownRewardableBuildingInstance(CGTownInstance * town, const BuildingID & index, IGameRandomizer & gameRandomizer)
: TownBuildingInstance(town, index)
{
assert(town && town->getTown());
configuration = generateConfiguration(rand);
configuration = generateConfiguration(gameRandomizer);
}
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;
const auto & building = town->getTown()->buildings.at(getBuildingType());
// force modal info window instead of displaying in inactive info box on adventure map
result.infoWindowType = EInfoWindowMode::MODAL;
building->rewardableObjectInfo.configureObject(result, rand, cb);
building->rewardableObjectInfo.configureObject(result, gameRandomizer, cb);
for(auto & rewardInfo : result.info)
{
assignBonuses(rewardInfo.reward.heroBonuses);
@@ -109,11 +109,11 @@ Rewardable::Configuration TownRewardableBuildingInstance::generateConfiguration(
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)
{
auto newConfiguration = generateConfiguration(gameEvents.getRandomGenerator());
auto newConfiguration = generateConfiguration(gameRandomizer);
gameEvents.setRewardableObjectConfiguration(town->id, getBuildingType(), newConfiguration);
if(configuration.resetParameters.visitors)

View File

@@ -57,7 +57,7 @@ class DLL_LINKAGE TownRewardableBuildingInstance : public TownBuildingInstance,
bool wasVisitedBefore(const CGHeroInstance * contextHero) 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;
const IObjectInterface * getObject() const override;
@@ -69,7 +69,7 @@ public:
void onHeroVisit(IGameEventCallback & gameEvents, const CGHeroInstance * h) 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
void heroLevelUpDone(IGameEventCallback & gameEvents, const CGHeroInstance *hero) const override;
@@ -77,7 +77,7 @@ public:
/// applies player selection of reward
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);
template <typename Handler> void serialize(Handler &h)

View File

@@ -15,6 +15,7 @@
#include "Limiter.h"
#include "Reward.h"
#include "../callback/IGameRandomizer.h"
#include "../texts/CGeneralTextHandler.h"
#include "../json/JsonRandom.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"));
}
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;
for (const auto & input : source.Vector())
{
auto newLimiter = std::make_shared<Rewardable::Limiter>();
configureLimiter(object, rng, cb, *newLimiter, input);
configureLimiter(object, gameRandomizer, cb, *newLimiter, input);
result.push_back(newLimiter);
}
@@ -123,81 +124,81 @@ Rewardable::LimitersList Rewardable::Info::configureSublimiters(Rewardable::Conf
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;
JsonRandom randomizer(cb);
JsonRandom randomizer(cb, gameRandomizer);
limiter.dayOfWeek = randomizer.loadValue(source["dayOfWeek"], rng, variables);
limiter.daysPassed = randomizer.loadValue(source["daysPassed"], rng, variables);
limiter.heroExperience = randomizer.loadValue(source["heroExperience"], rng, variables);
limiter.heroLevel = randomizer.loadValue(source["heroLevel"], rng, variables);
limiter.dayOfWeek = randomizer.loadValue(source["dayOfWeek"], variables);
limiter.daysPassed = randomizer.loadValue(source["daysPassed"], variables);
limiter.heroExperience = randomizer.loadValue(source["heroExperience"], variables);
limiter.heroLevel = randomizer.loadValue(source["heroLevel"], variables);
limiter.canLearnSkills = source["canLearnSkills"].Bool();
limiter.commanderAlive = source["commanderAlive"].Bool();
limiter.hasExtraCreatures = source["hasExtraCreatures"].Bool();
limiter.manaPercentage = randomizer.loadValue(source["manaPercentage"], rng, variables);
limiter.manaPoints = randomizer.loadValue(source["manaPoints"], rng, variables);
limiter.manaPercentage = randomizer.loadValue(source["manaPercentage"], 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.secondary = randomizer.loadSecondaries(source["secondary"], rng, variables);
limiter.artifacts = randomizer.loadArtifacts(source["artifacts"], rng, variables);
limiter.availableSlots = randomizer.loadArtifactSlots(source["availableSlots"], rng, variables);
limiter.spells = randomizer.loadSpells(source["spells"], rng, variables);
limiter.scrolls = randomizer.loadSpells(source["scrolls"], rng, variables);
limiter.canLearnSpells = randomizer.loadSpells(source["canLearnSpells"], rng, variables);
limiter.creatures = randomizer.loadCreatures(source["creatures"], rng, variables);
limiter.canReceiveCreatures = randomizer.loadCreatures(source["canReceiveCreatures"], rng, variables);
limiter.primary = randomizer.loadPrimaries(source["primary"], variables);
limiter.secondary = randomizer.loadSecondaries(source["secondary"], variables);
limiter.artifacts = randomizer.loadArtifacts(source["artifacts"], variables);
limiter.availableSlots = randomizer.loadArtifactSlots(source["availableSlots"], variables);
limiter.spells = randomizer.loadSpells(source["spells"], variables);
limiter.scrolls = randomizer.loadSpells(source["scrolls"], variables);
limiter.canLearnSpells = randomizer.loadSpells(source["canLearnSpells"], variables);
limiter.creatures = randomizer.loadCreatures(source["creatures"], variables);
limiter.canReceiveCreatures = randomizer.loadCreatures(source["canReceiveCreatures"], variables);
limiter.players = randomizer.loadColors(source["colors"], rng, variables);
limiter.heroes = randomizer.loadHeroes(source["heroes"], rng);
limiter.heroClasses = randomizer.loadHeroClasses(source["heroClasses"], rng);
limiter.players = randomizer.loadColors(source["colors"], variables);
limiter.heroes = randomizer.loadHeroes(source["heroes"]);
limiter.heroClasses = randomizer.loadHeroClasses(source["heroClasses"]);
limiter.allOf = configureSublimiters(object, rng, cb, source["allOf"] );
limiter.anyOf = configureSublimiters(object, rng, cb, source["anyOf"] );
limiter.noneOf = configureSublimiters(object, rng, cb, source["noneOf"] );
limiter.allOf = configureSublimiters(object, gameRandomizer, cb, source["allOf"]);
limiter.anyOf = configureSublimiters(object, gameRandomizer, cb, source["anyOf"]);
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;
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.heroLevel = randomizer.loadValue(source["heroLevel"], rng, variables);
reward.heroExperience = randomizer.loadValue(source["heroExperience"], variables);
reward.heroLevel = randomizer.loadValue(source["heroLevel"], variables);
reward.manaDiff = randomizer.loadValue(source["manaPoints"], rng, variables);
reward.manaOverflowFactor = randomizer.loadValue(source["manaOverflowFactor"], rng, variables);
reward.manaPercentage = randomizer.loadValue(source["manaPercentage"], rng, variables, -1);
reward.manaDiff = randomizer.loadValue(source["manaPoints"], variables);
reward.manaOverflowFactor = randomizer.loadValue(source["manaOverflowFactor"], variables);
reward.manaPercentage = randomizer.loadValue(source["manaPercentage"], variables, -1);
reward.movePoints = randomizer.loadValue(source["movePoints"], rng, variables);
reward.movePercentage = randomizer.loadValue(source["movePercentage"], rng, variables, -1);
reward.movePoints = randomizer.loadValue(source["movePoints"], variables);
reward.movePercentage = randomizer.loadValue(source["movePercentage"], variables, -1);
reward.removeObject = source["removeObject"].Bool();
reward.heroBonuses = randomizer.loadBonuses(source["bonuses"]);
reward.commanderBonuses = randomizer.loadBonuses(source["commanderBonuses"]);
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.secondary = randomizer.loadSecondaries(source["secondary"], rng, variables);
reward.primary = randomizer.loadPrimaries(source["primary"], variables);
reward.secondary = randomizer.loadSecondaries(source["secondary"], variables);
reward.grantedArtifacts = randomizer.loadArtifacts(source["artifacts"], rng, variables);
reward.takenArtifacts = randomizer.loadArtifacts(source["takenArtifacts"], rng, variables);
reward.takenArtifactSlots = randomizer.loadArtifactSlots(source["takenArtifactSlots"], rng, variables);
reward.grantedScrolls = randomizer.loadSpells(source["scrolls"], rng, variables);
reward.takenScrolls = randomizer.loadSpells(source["takenScrolls"], rng, variables);
reward.spells = randomizer.loadSpells(source["spells"], rng, variables);
reward.creatures = randomizer.loadCreatures(source["creatures"], rng, variables);
reward.takenCreatures = randomizer.loadCreatures(source["takenCreatures"], rng, variables);
reward.grantedArtifacts = randomizer.loadArtifacts(source["artifacts"], variables);
reward.takenArtifacts = randomizer.loadArtifacts(source["takenArtifacts"], variables);
reward.takenArtifactSlots = randomizer.loadArtifactSlots(source["takenArtifactSlots"], variables);
reward.grantedScrolls = randomizer.loadSpells(source["scrolls"], variables);
reward.takenScrolls = randomizer.loadSpells(source["takenScrolls"], variables);
reward.spells = randomizer.loadSpells(source["spells"], variables);
reward.creatures = randomizer.loadCreatures(source["creatures"], variables);
reward.takenCreatures = randomizer.loadCreatures(source["takenCreatures"], variables);
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();
}
@@ -206,13 +207,13 @@ void Rewardable::Info::configureReward(Rewardable::Configuration & object, vstd:
auto const & entry = source["revealTiles"];
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->scoreSurface = randomizer.loadValue(entry["surface"], rng, variables);
reward.revealTiles->scoreSubterra = randomizer.loadValue(entry["subterra"], rng, variables);
reward.revealTiles->scoreWater = randomizer.loadValue(entry["water"], rng, variables);
reward.revealTiles->scoreRock = randomizer.loadValue(entry["rock"], rng, variables);
reward.revealTiles->scoreSurface = randomizer.loadValue(entry["surface"], variables);
reward.revealTiles->scoreSubterra = randomizer.loadValue(entry["subterra"], variables);
reward.revealTiles->scoreWater = randomizer.loadValue(entry["water"], variables);
reward.revealTiles->scoreRock = randomizer.loadValue(entry["rock"], variables);
}
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.visitors = source["visitors"].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())
{
@@ -246,19 +247,19 @@ void Rewardable::Info::configureVariables(Rewardable::Configuration & object, vs
int32_t value = -1;
if (category.first == "number")
value = randomizer.loadValue(input, rng, object.variables.values);
value = randomizer.loadValue(input, object.variables.values);
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")
value = randomizer.loadSpell(input, rng, object.variables.values).getNum();
value = randomizer.loadSpell(input, object.variables.values).getNum();
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")
value = randomizer.loadSecondary(input, rng, object.variables.values).getNum();
value = randomizer.loadSecondary(input, object.variables.values).getNum();
object.initVariable(category.first, entry.first, value);
}
@@ -350,7 +351,7 @@ void Rewardable::Info::replaceTextPlaceholders(MetaString & target, const Variab
void Rewardable::Info::configureRewards(
Rewardable::Configuration & object,
vstd::RNG & rng,
IGameRandomizer & gameRandomizer,
IGameInfoCallback * cb,
const JsonNode & source,
Rewardable::EEventType event,
@@ -371,7 +372,7 @@ void Rewardable::Info::configureRewards(
{
const JsonNode & preset = object.getPresetVariable("dice", diceID);
if (preset.isNull())
object.initVariable("dice", diceID, rng.nextInt(0, 99));
object.initVariable("dice", diceID, gameRandomizer.getDefault().nextInt(0, 99));
else
object.initVariable("dice", diceID, preset.Integer());
@@ -394,8 +395,8 @@ void Rewardable::Info::configureRewards(
}
Rewardable::VisitInfo info;
configureLimiter(object, rng, cb, info.limiter, reward["limiter"]);
configureReward(object, rng, cb, info.reward, reward);
configureLimiter(object, gameRandomizer, cb, info.limiter, reward["limiter"]);
configureReward(object, gameRandomizer, cb, info.reward, reward);
info.visitType = event;
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.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, rng, 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["rewards"], Rewardable::EEventType::EVENT_FIRST_VISIT, "rewards");
configureRewards(object, gameRandomizer, cb, parameters["onVisited"], Rewardable::EEventType::EVENT_ALREADY_VISITED, "onVisited");
configureRewards(object, gameRandomizer, cb, parameters["onEmpty"], Rewardable::EEventType::EVENT_NOT_AVAILABLE, "onEmpty");
object.onSelect = loadMessage(parameters["onSelectMessage"], TextIdentifier(objectTextID, "onSelect"));
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);
}
configureResetInfo(object, rng, object.resetParameters, parameters["resetParameters"]);
configureResetInfo(object, gameRandomizer, object.resetParameters, parameters["resetParameters"]);
object.canRefuse = parameters["canRefuse"].Bool();
object.showScoutedPreview = parameters["showScoutedPreview"].Bool();
@@ -494,7 +495,7 @@ void Rewardable::Info::configureObject(Rewardable::Configuration & object, vstd:
}
if (object.visitMode == Rewardable::VISIT_LIMITER)
configureLimiter(object, rng, cb, object.visitLimiter, parameters["visitLimiter"]);
configureLimiter(object, gameRandomizer, cb, object.visitLimiter, parameters["visitLimiter"]);
}

View File

@@ -15,13 +15,9 @@
VCMI_LIB_NAMESPACE_BEGIN
namespace vstd
{
class RNG;
}
class MetaString;
class IGameInfoCallback;
class IGameRandomizer;
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 VisitInfo & info) const;
void configureVariables(Rewardable::Configuration & object, vstd::RNG & rng, 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 configureVariables(Rewardable::Configuration & object, IGameRandomizer & gameRandomizer, IGameInfoCallback * cb, const JsonNode & source) 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;
Rewardable::LimitersList configureSublimiters(Rewardable::Configuration & object, vstd::RNG & rng, IGameInfoCallback * cb, 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, 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 configureResetInfo(Rewardable::Configuration & object, vstd::RNG & rng, Rewardable::ResetInfo & 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, IGameRandomizer & gameRandomizer, Rewardable::ResetInfo & info, const JsonNode & source) const;
public:
const JsonNode & getParameters() const;
@@ -70,7 +66,7 @@ public:
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);

View File

@@ -18,6 +18,7 @@
#include "processors/HeroPoolProcessor.h"
#include "processors/NewTurnProcessor.h"
#include "processors/PlayerMessageProcessor.h"
#include "processors/RandomizationProcessor.h"
#include "processors/TurnOrderProcessor.h"
#include "queries/QueriesProcessor.h"
#include "queries/MapQueries.h"
@@ -510,7 +511,7 @@ CGameHandler::CGameHandler(CVCMIServer * lobby)
, turnOrder(std::make_unique<TurnOrderProcessor>(this))
, queries(std::make_unique<QueriesProcessor>())
, playerMessages(std::make_unique<PlayerMessageProcessor>(this))
, randomNumberGenerator(std::make_unique<CRandomGenerator>())
, randomizationProcessor(std::make_unique<RandomizationProcessor>())
, complainNoCreatures("No creatures to split")
, complainNotEnoughCreatures("Cannot split that stack, not enough creatures!")
, complainInvalidSlot("Invalid slot accessed!")
@@ -539,14 +540,14 @@ void CGameHandler::init(StartInfo *si, Load::ProgressAccumulator & progressTrack
{
int requestedSeed = settings["server"]["seed"].Integer();
if (requestedSeed != 0)
randomNumberGenerator->setSeed(requestedSeed);
logGlobal->info("Using random seed: %d", randomNumberGenerator->nextInt());
randomizationProcessor->setSeed(requestedSeed);
logGlobal->info("Using random seed: %d", randomizationProcessor->getDefault().nextInt());
CMapService mapService;
gs = std::make_shared<CGameState>(this);
gs->preInit(LIBRARY);
logGlobal->info("Gamestate created!");
gs->init(&mapService, si, getRandomGenerator(), progressTracking);
gs->init(&mapService, si, *randomizationProcessor, progressTracking);
logGlobal->info("Gamestate initialized!");
for (const auto & elem : gameState().players)
@@ -687,7 +688,7 @@ void CGameHandler::onNewTurn()
{
SetAvailableArtifacts saa;
saa.id = ObjectInstanceID::NONE;
pickAllowedArtsSet(saa.arts, getRandomGenerator());
saa.arts = randomizationProcessor->rollMarketArtifactSet();
sendAndApply(saa);
}
@@ -700,7 +701,7 @@ void CGameHandler::onNewTurn()
for (auto & elem : gameState().getMap().getObjects())
{
if (elem)
elem->newTurn(*this);
elem->newTurn(*this, *randomizationProcessor);
}
synchronizeArtifactHandlerLists(); //new day events may have changed them. TODO better of managing that
@@ -4234,7 +4235,7 @@ void CGameHandler::showInfoDialog(InfoWindow * iw)
vstd::RNG & CGameHandler::getRandomGenerator()
{
return *randomNumberGenerator;
return randomizationProcessor->getDefault();
}
#if SCRIPTING_ENABLED
@@ -4263,7 +4264,7 @@ std::shared_ptr<CGObjectInstance> CGameHandler::createNewObject(const int3 & vis
auto handler = LIBRARY->objtypeh->getHandlerFor(objectID, subID);
auto o = handler->create(gameState().cb, nullptr);
handler->configureObject(o.get(), getRandomGenerator());
handler->configureObject(o.get(), *randomizationProcessor);
assert(o->ID == objectID);
gameState().getMap().generateUniqueInstanceName(o.get());
@@ -4314,7 +4315,7 @@ void CGameHandler::createHole(const int3 & visitablePosition, PlayerColor initia
void CGameHandler::newObject(std::shared_ptr<CGObjectInstance> object, PlayerColor initiator)
{
object->initObj(getRandomGenerator());
object->initObj(*randomizationProcessor);
NewObject no;
no.newObject = object;

View File

@@ -53,6 +53,7 @@ class TurnTimerHandler;
class QueriesProcessor;
class CObjectVisitQuery;
class NewTurnProcessor;
class RandomizationProcessor;
class CGameHandler : public CGameInfoCallback, public Environment, public IGameEventCallback
{
@@ -65,7 +66,7 @@ public:
std::unique_ptr<TurnOrderProcessor> turnOrder;
std::unique_ptr<TurnTimerHandler> turnTimerHandler;
std::unique_ptr<NewTurnProcessor> newTurnProcessor;
std::unique_ptr<CRandomGenerator> randomNumberGenerator;
std::unique_ptr<RandomizationProcessor> randomizationProcessor;
std::shared_ptr<CGameState> gs;
//use enums as parameters, because doMove(sth, true, false, true) is not readable
@@ -246,7 +247,7 @@ public:
template <typename Handler> void serialize(Handler &h)
{
h & QID;
h & *randomNumberGenerator;
h & *randomizationProcessor;
h & *battles;
h & *heroPool;
h & *playerMessages;

View File

@@ -15,6 +15,7 @@ set(vcmiservercommon_SRCS
processors/HeroPoolProcessor.cpp
processors/NewTurnProcessor.cpp
processors/PlayerMessageProcessor.cpp
processors/RandomizationProcessor.cpp
processors/TurnOrderProcessor.cpp
CGameHandler.cpp
@@ -43,6 +44,7 @@ set(vcmiservercommon_HEADERS
processors/HeroPoolProcessor.h
processors/NewTurnProcessor.h
processors/PlayerMessageProcessor.h
processors/RandomizationProcessor.h
processors/TurnOrderProcessor.h
CGameHandler.h

View File

@@ -11,6 +11,7 @@
#include "NewTurnProcessor.h"
#include "HeroPoolProcessor.h"
#include "RandomizationProcessor.h"
#include "../CGameHandler.h"
@@ -524,7 +525,7 @@ std::tuple<EWeekType, CreatureID> NewTurnProcessor::pickWeekType(bool newMonth)
{
if (gameHandler->getSettings().getBoolean(EGameSettings::CREATURES_ALLOW_ALL_FOR_DOUBLE_MONTH))
{
CreatureID creatureID = LIBRARY->creh->pickRandomMonster(gameHandler->getRandomGenerator());
CreatureID creatureID = gameHandler->randomizationProcessor->rollCreature();
return { EWeekType::DOUBLE_GROWTH, creatureID};
}
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());
do
{
newMonster.second = LIBRARY->creh->pickRandomMonster(gameHandler->getRandomGenerator());
newMonster.second = gameHandler->randomizationProcessor->rollCreature();
} while (newMonster.second.toEntity(LIBRARY)->getFactionID().toFaction()->town == nullptr); // find first non neutral creature
return { EWeekType::BONUS_GROWTH, newMonster.second};

View File

@@ -0,0 +1,126 @@
/*
* RandomizationProcessor.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 "RandomizationProcessor.h"
#include <vstd/RNG.h>
bool BiasedRandomizer::roll(vstd::RNG &generator, int successChance, int biasValue)
{
int failChance = 100 - successChance;
int newRoll = generator.nextInt(0,99);
bool success = newRoll + accumulatedBias >= successChance;
if (success)
accumulatedBias -= failChance * biasValue / 100;
else
accumulatedBias += successChance * biasValue / 100;
return success;
}
//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));
//}
//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;
//
// const auto * 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; });
//}
//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);
//}

View File

@@ -0,0 +1,84 @@
/*
* RandomizationProcessor.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 "../lib/callback/IGameRandomizer.h"
VCMI_LIB_NAMESPACE_BEGIN
class CRandomGenerator;
class CGHeroInstance;
VCMI_LIB_NAMESPACE_END
/// Biased randomizer that has following properties:
/// - at bias value of 0 it acts as statistical random generator
/// - 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 BiasedRandomizer
{
int accumulatedBias;
public:
/// Performs coin flip with specified success chance
/// Returns true with probability successChance percents, and false with probability 100-successChance percents
bool roll(vstd::RNG & generator, int successChance, int biasValue);
};
class RandomizationProcessor final : public IGameRandomizer
{
std::unique_ptr<CRandomGenerator> globalRandomNumberGenerator;
std::map<HeroTypeID, std::unique_ptr<CRandomGenerator>> heroSeed;
std::map<PlayerColor, std::unique_ptr<CRandomGenerator>> playerTavern;
std::map<ObjectInstanceID, BiasedRandomizer> goodMoraleSeed;
std::map<ObjectInstanceID, BiasedRandomizer> badMoraleSeed;
std::map<ObjectInstanceID, BiasedRandomizer> goodLuckSeed;
std::map<ObjectInstanceID, BiasedRandomizer> badLuckSeed;
std::map<ObjectInstanceID, BiasedRandomizer> combatAbilitySeed;
public:
RandomizationProcessor();
PrimarySkill rollPrimarySkillForLevelup(const CGHeroInstance * hero);
SecondarySkill rollSecondarySkillForLevelup(const CGHeroInstance * hero, const std::vector<SecondarySkill> & candidates);
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);
HeroTypeID rollHero(PlayerColor player, FactionID faction) override;
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;
std::string rollTownName(FactionID faction) override;
vstd::RNG & getDefault() override;
void setSeed(int newSeed);
template<typename Handler>
void serialize(Handler & h)
{
h & *globalRandomNumberGenerator;
}
};