diff --git a/AI/BattleAI/BattleAI.cpp b/AI/BattleAI/BattleAI.cpp index 753375b1b..c65f30f02 100644 --- a/AI/BattleAI/BattleAI.cpp +++ b/AI/BattleAI/BattleAI.cpp @@ -653,7 +653,7 @@ AttackPossibility AttackPossibility::evaluate(const BattleAttackInfo &AttackInfo for(int i = 0; i < totalAttacks; i++) { std::pair retaliation(0,0); - auto attackDmg = cbc->battleEstimateDamage(curBai, &retaliation); + auto attackDmg = cbc->battleEstimateDamage(CRandomGenerator::getDefault(), curBai, &retaliation); ap.damageDealt = (attackDmg.first + attackDmg.second) / 2; ap.damageReceived = (retaliation.first + retaliation.second) / 2; @@ -737,7 +737,7 @@ int PotentialTargets::bestActionValue() const void EnemyInfo::calcDmg(const CStack * ourStack) { - TDmgRange retal, dmg = cbc->battleEstimateDamage(ourStack, s, &retal); + TDmgRange retal, dmg = cbc->battleEstimateDamage(CRandomGenerator::getDefault(), ourStack, s, &retal); adi = (dmg.first + dmg.second) / 2; adr = (retal.first + retal.second) / 2; } diff --git a/AI/StupidAI/StupidAI.cpp b/AI/StupidAI/StupidAI.cpp index 207f0e357..7f74bf433 100644 --- a/AI/StupidAI/StupidAI.cpp +++ b/AI/StupidAI/StupidAI.cpp @@ -44,7 +44,7 @@ struct EnemyInfo {} void calcDmg(const CStack * ourStack) { - TDmgRange retal, dmg = cbc->battleEstimateDamage(ourStack, s, &retal); + TDmgRange retal, dmg = cbc->battleEstimateDamage(CRandomGenerator::getDefault(), ourStack, s, &retal); adi = (dmg.first + dmg.second) / 2; adr = (retal.first + retal.second) / 2; } diff --git a/client/battle/CBattleInterface.cpp b/client/battle/CBattleInterface.cpp index f90ea56ab..0929505ca 100644 --- a/client/battle/CBattleInterface.cpp +++ b/client/battle/CBattleInterface.cpp @@ -1587,7 +1587,7 @@ void CBattleInterface::activateStack() if(randomSpellcaster) creatureSpellToCast = -1; //spell will be set later on cast - creatureSpellToCast = curInt->cb->battleGetRandomStackSpell(s, CBattleInfoCallback::RANDOM_AIMED); //faerie dragon can cast only one spell until their next move + creatureSpellToCast = curInt->cb->battleGetRandomStackSpell(CRandomGenerator::getDefault(), s, CBattleInfoCallback::RANDOM_AIMED); //faerie dragon can cast only one spell until their next move //TODO: what if creature can cast BOTH random genie spell and aimed spell? } else @@ -2101,7 +2101,7 @@ void CBattleInterface::handleHex(BattleHex myNumber, int eventType) { if (shere && ourStack && shere != sactive) //only positive spells for other allied creatures { - int spellID = curInt->cb->battleGetRandomStackSpell(shere, CBattleInfoCallback::RANDOM_GENIE); + int spellID = curInt->cb->battleGetRandomStackSpell(CRandomGenerator::getDefault(), shere, CBattleInfoCallback::RANDOM_GENIE); if (spellID > -1) { legalAction = true; @@ -2264,7 +2264,7 @@ void CBattleInterface::handleHex(BattleHex myNumber, int eventType) } }; - std::string estDmgText = formatDmgRange(curInt->cb->battleEstimateDamage(sactive, shere)); //calculating estimated dmg + std::string estDmgText = formatDmgRange(curInt->cb->battleEstimateDamage(CRandomGenerator::getDefault(), sactive, shere)); //calculating estimated dmg consoleMsg = (boost::format(CGI->generaltexth->allTexts[36]) % shere->getName() % estDmgText).str(); //Attack %s (%s damage) } break; @@ -2276,7 +2276,7 @@ void CBattleInterface::handleHex(BattleHex myNumber, int eventType) cursorFrame = ECursor::COMBAT_SHOOT; realizeAction = [=] {giveCommand(Battle::SHOOT, myNumber, activeStack->ID);}; - std::string estDmgText = formatDmgRange(curInt->cb->battleEstimateDamage(sactive, shere)); //calculating estimated dmg + std::string estDmgText = formatDmgRange(curInt->cb->battleEstimateDamage(CRandomGenerator::getDefault(), sactive, shere)); //calculating estimated dmg //printing - Shoot %s (%d shots left, %s damage) consoleMsg = (boost::format(CGI->generaltexth->allTexts[296]) % shere->getName() % sactive->shots % estDmgText).str(); } @@ -2425,7 +2425,7 @@ void CBattleInterface::handleHex(BattleHex myNumber, int eventType) } else //unknown random spell { - giveCommand(Battle::MONSTER_SPELL, myNumber, sactive->ID, curInt->cb->battleGetRandomStackSpell(shere, CBattleInfoCallback::RANDOM_GENIE)); + giveCommand(Battle::MONSTER_SPELL, myNumber, sactive->ID, curInt->cb->battleGetRandomStackSpell(CRandomGenerator::getDefault(), shere, CBattleInfoCallback::RANDOM_GENIE)); } } else diff --git a/lib/CBattleCallback.cpp b/lib/CBattleCallback.cpp index f6b885f4d..d2da9d392 100644 --- a/lib/CBattleCallback.cpp +++ b/lib/CBattleCallback.cpp @@ -521,15 +521,15 @@ std::set CBattleInfoCallback::battleGetAttackedHexes(const CStack* at return attackedHexes; } -SpellID CBattleInfoCallback::battleGetRandomStackSpell(const CStack * stack, ERandomSpell mode) const +SpellID CBattleInfoCallback::battleGetRandomStackSpell(CRandomGenerator & rand, const CStack * stack, ERandomSpell mode) const { switch (mode) { case RANDOM_GENIE: - return getRandomBeneficialSpell(stack); //target + return getRandomBeneficialSpell(rand, stack); //target break; case RANDOM_AIMED: - return getRandomCastedSpell(stack); //caster + return getRandomCastedSpell(rand, stack); //caster break; default: logGlobal->errorStream() << "Incorrect mode of battleGetRandomSpell (" << mode <<")"; @@ -1076,15 +1076,15 @@ TDmgRange CBattleInfoCallback::calculateDmgRange( const CStack* attacker, const return calculateDmgRange(bai); } -TDmgRange CBattleInfoCallback::battleEstimateDamage(const CStack * attacker, const CStack * defender, TDmgRange * retaliationDmg) const +TDmgRange CBattleInfoCallback::battleEstimateDamage(CRandomGenerator & rand, const CStack * attacker, const CStack * defender, TDmgRange * retaliationDmg) const { RETURN_IF_NOT_BATTLE(std::make_pair(0, 0)); const bool shooting = battleCanShoot(attacker, defender->position); const BattleAttackInfo bai(attacker, defender, shooting); - return battleEstimateDamage(bai, retaliationDmg); + return battleEstimateDamage(rand, bai, retaliationDmg); } -std::pair CBattleInfoCallback::battleEstimateDamage(const BattleAttackInfo &bai, std::pair * retaliationDmg /*= nullptr*/) const +std::pair CBattleInfoCallback::battleEstimateDamage(CRandomGenerator & rand, const BattleAttackInfo &bai, std::pair * retaliationDmg /*= nullptr*/) const { RETURN_IF_NOT_BATTLE(std::make_pair(0, 0)); @@ -1106,7 +1106,7 @@ std::pair CBattleInfoCallback::battleEstimateDamage(const BattleAtta { BattleStackAttacked bsa; bsa.damageAmount = ret.*pairElems[i]; - bai.defender->prepareAttacked(bsa, gs->getRandomGenerator(), bai.defenderCount); + bai.defender->prepareAttacked(bsa, rand, bai.defenderCount); auto retaliationAttack = bai.reverse(); retaliationAttack.attackerCount = bsa.newAmount; @@ -1923,7 +1923,7 @@ std::set CBattleInfoCallback:: batteAdjacentCreatures(const CStac return stacks; } -SpellID CBattleInfoCallback::getRandomBeneficialSpell(const CStack * subject) const +SpellID CBattleInfoCallback::getRandomBeneficialSpell(CRandomGenerator & rand, const CStack * subject) const { RETURN_IF_NOT_BATTLE(SpellID::NONE); //This is complete list. No spells from mods. @@ -2045,7 +2045,7 @@ SpellID CBattleInfoCallback::getRandomBeneficialSpell(const CStack * subject) co if(!beneficialSpells.empty()) { - return *RandomGeneratorUtil::nextItem(beneficialSpells, gs->getRandomGenerator()); + return *RandomGeneratorUtil::nextItem(beneficialSpells, rand); } else { @@ -2053,7 +2053,7 @@ SpellID CBattleInfoCallback::getRandomBeneficialSpell(const CStack * subject) co } } -SpellID CBattleInfoCallback::getRandomCastedSpell(const CStack * caster) const +SpellID CBattleInfoCallback::getRandomCastedSpell(CRandomGenerator & rand,const CStack * caster) const { RETURN_IF_NOT_BATTLE(SpellID::NONE); @@ -2065,7 +2065,7 @@ SpellID CBattleInfoCallback::getRandomCastedSpell(const CStack * caster) const { totalWeight += std::max(b->additionalInfo, 1); //minimal chance to cast is 1 } - int randomPos = gs->getRandomGenerator().nextInt(totalWeight - 1); + int randomPos = rand.nextInt(totalWeight - 1); for(Bonus * b : *bl) { randomPos -= std::max(b->additionalInfo, 1); diff --git a/lib/CBattleCallback.h b/lib/CBattleCallback.h index fdf7be1df..138813fd9 100644 --- a/lib/CBattleCallback.h +++ b/lib/CBattleCallback.h @@ -22,6 +22,7 @@ struct CObstacleInstance; class IBonusBearer; struct InfoAboutHero; class CArmedInstance; +class CRandomGenerator; namespace boost {class shared_mutex;} @@ -266,8 +267,8 @@ public: TDmgRange calculateDmgRange(const CStack* attacker, const CStack* defender, bool shooting, ui8 charge, bool lucky, bool unlucky, bool deathBlow, bool ballistaDoubleDmg) const; //charge - number of hexes travelled before attack (for champion's jousting); returns pair //hextowallpart //int battleGetWallUnderHex(BattleHex hex) const; //returns part of destructible wall / gate / keep under given hex or -1 if not found - std::pair battleEstimateDamage(const BattleAttackInfo &bai, std::pair * retaliationDmg = nullptr) const; //estimates damage dealt by attacker to defender; it may be not precise especially when stack has randomly working bonuses; returns pair - std::pair battleEstimateDamage(const CStack * attacker, const CStack * defender, std::pair * retaliationDmg = nullptr) const; //estimates damage dealt by attacker to defender; it may be not precise especially when stack has randomly working bonuses; returns pair + std::pair battleEstimateDamage(CRandomGenerator & rand, const BattleAttackInfo &bai, std::pair * retaliationDmg = nullptr) const; //estimates damage dealt by attacker to defender; it may be not precise especially when stack has randomly working bonuses; returns pair + std::pair battleEstimateDamage(CRandomGenerator & rand, const CStack * attacker, const CStack * defender, std::pair * retaliationDmg = nullptr) const; //estimates damage dealt by attacker to defender; it may be not precise especially when stack has randomly working bonuses; returns pair si8 battleHasDistancePenalty( const CStack * stack, BattleHex destHex ) const; si8 battleHasDistancePenalty(const IBonusBearer *bonusBearer, BattleHex shooterPosition, BattleHex destHex ) const; si8 battleHasWallPenalty(const CStack * stack, BattleHex destHex) const; //checks if given stack has wall penalty @@ -286,9 +287,9 @@ public: ESpellCastProblem::ESpellCastProblem battleCanCastThisSpellHere(const ISpellCaster * caster, const CSpell * spell, ECastingMode::ECastingMode mode, BattleHex dest) const; //checks if given player can cast given spell at given tile in given mode std::vector battleGetPossibleTargets(PlayerColor player, const CSpell *spell) const; - SpellID battleGetRandomStackSpell(const CStack * stack, ERandomSpell mode) const; - SpellID getRandomBeneficialSpell(const CStack * subject) const; - SpellID getRandomCastedSpell(const CStack * caster) const; //called at the beginning of turn for Faerie Dragon + SpellID battleGetRandomStackSpell(CRandomGenerator & rand, const CStack * stack, ERandomSpell mode) const; + SpellID getRandomBeneficialSpell(CRandomGenerator & rand, const CStack * subject) const; + SpellID getRandomCastedSpell(CRandomGenerator & rand, const CStack * caster) const; //called at the beginning of turn for Faerie Dragon const CStack * getStackIf(std::function pred) const; diff --git a/lib/CGameState.cpp b/lib/CGameState.cpp index b1f2cc034..ebbc0eee9 100644 --- a/lib/CGameState.cpp +++ b/lib/CGameState.cpp @@ -476,13 +476,13 @@ int CGameState::pickUnusedHeroTypeRandomly(PlayerColor owner) // select random hero native to "our" faction if(!factionHeroes.empty()) { - return RandomGeneratorUtil::nextItem(factionHeroes, rand)->getNum(); + return RandomGeneratorUtil::nextItem(factionHeroes, getRandomGenerator())->getNum(); } logGlobal->warnStream() << "Cannot find free hero of appropriate faction for player " << owner << " - trying to get first available..."; if(!otherHeroes.empty()) { - return RandomGeneratorUtil::nextItem(otherHeroes, rand)->getNum(); + return RandomGeneratorUtil::nextItem(otherHeroes, getRandomGenerator())->getNum(); } logGlobal->error("No free allowed heroes!"); @@ -500,29 +500,29 @@ std::pair CGameState::pickObject (CGObjectInstance *obj) switch(obj->ID) { case Obj::RANDOM_ART: - return std::make_pair(Obj::ARTIFACT, VLC->arth->pickRandomArtifact(rand, CArtifact::ART_TREASURE | CArtifact::ART_MINOR | CArtifact::ART_MAJOR | CArtifact::ART_RELIC)); + return std::make_pair(Obj::ARTIFACT, VLC->arth->pickRandomArtifact(getRandomGenerator(), CArtifact::ART_TREASURE | CArtifact::ART_MINOR | CArtifact::ART_MAJOR | CArtifact::ART_RELIC)); case Obj::RANDOM_TREASURE_ART: - return std::make_pair(Obj::ARTIFACT, VLC->arth->pickRandomArtifact(rand, CArtifact::ART_TREASURE)); + return std::make_pair(Obj::ARTIFACT, VLC->arth->pickRandomArtifact(getRandomGenerator(), CArtifact::ART_TREASURE)); case Obj::RANDOM_MINOR_ART: - return std::make_pair(Obj::ARTIFACT, VLC->arth->pickRandomArtifact(rand, CArtifact::ART_MINOR)); + return std::make_pair(Obj::ARTIFACT, VLC->arth->pickRandomArtifact(getRandomGenerator(), CArtifact::ART_MINOR)); case Obj::RANDOM_MAJOR_ART: - return std::make_pair(Obj::ARTIFACT, VLC->arth->pickRandomArtifact(rand, CArtifact::ART_MAJOR)); + return std::make_pair(Obj::ARTIFACT, VLC->arth->pickRandomArtifact(getRandomGenerator(), CArtifact::ART_MAJOR)); case Obj::RANDOM_RELIC_ART: - return std::make_pair(Obj::ARTIFACT, VLC->arth->pickRandomArtifact(rand, CArtifact::ART_RELIC)); + return std::make_pair(Obj::ARTIFACT, VLC->arth->pickRandomArtifact(getRandomGenerator(), CArtifact::ART_RELIC)); case Obj::RANDOM_HERO: return std::make_pair(Obj::HERO, pickNextHeroType(obj->tempOwner)); case Obj::RANDOM_MONSTER: - return std::make_pair(Obj::MONSTER, VLC->creh->pickRandomMonster(rand)); + return std::make_pair(Obj::MONSTER, VLC->creh->pickRandomMonster(getRandomGenerator())); case Obj::RANDOM_MONSTER_L1: - return std::make_pair(Obj::MONSTER, VLC->creh->pickRandomMonster(rand, 1)); + return std::make_pair(Obj::MONSTER, VLC->creh->pickRandomMonster(getRandomGenerator(), 1)); case Obj::RANDOM_MONSTER_L2: - return std::make_pair(Obj::MONSTER, VLC->creh->pickRandomMonster(rand, 2)); + return std::make_pair(Obj::MONSTER, VLC->creh->pickRandomMonster(getRandomGenerator(), 2)); case Obj::RANDOM_MONSTER_L3: - return std::make_pair(Obj::MONSTER, VLC->creh->pickRandomMonster(rand, 3)); + return std::make_pair(Obj::MONSTER, VLC->creh->pickRandomMonster(getRandomGenerator(), 3)); case Obj::RANDOM_MONSTER_L4: - return std::make_pair(Obj::MONSTER, VLC->creh->pickRandomMonster(rand, 4)); + return std::make_pair(Obj::MONSTER, VLC->creh->pickRandomMonster(getRandomGenerator(), 4)); case Obj::RANDOM_RESOURCE: - return std::make_pair(Obj::RESOURCE,rand.nextInt(6)); //now it's OH3 style, use %8 for mithril + return std::make_pair(Obj::RESOURCE,getRandomGenerator().nextInt(6)); //now it's OH3 style, use %8 for mithril case Obj::RANDOM_TOWN: { PlayerColor align = PlayerColor((static_cast(obj))->alignment); @@ -542,18 +542,18 @@ std::pair CGameState::pickObject (CGObjectInstance *obj) { do { - f = rand.nextInt(VLC->townh->factions.size() - 1); + f = getRandomGenerator().nextInt(VLC->townh->factions.size() - 1); } while (VLC->townh->factions[f]->town == nullptr); // find playable faction } return std::make_pair(Obj::TOWN,f); } case Obj::RANDOM_MONSTER_L5: - return std::make_pair(Obj::MONSTER, VLC->creh->pickRandomMonster(rand, 5)); + return std::make_pair(Obj::MONSTER, VLC->creh->pickRandomMonster(getRandomGenerator(), 5)); case Obj::RANDOM_MONSTER_L6: - return std::make_pair(Obj::MONSTER, VLC->creh->pickRandomMonster(rand, 6)); + return std::make_pair(Obj::MONSTER, VLC->creh->pickRandomMonster(getRandomGenerator(), 6)); case Obj::RANDOM_MONSTER_L7: - return std::make_pair(Obj::MONSTER, VLC->creh->pickRandomMonster(rand, 7)); + return std::make_pair(Obj::MONSTER, VLC->creh->pickRandomMonster(getRandomGenerator(), 7)); case Obj::RANDOM_DWELLING: case Obj::RANDOM_DWELLING_LVL: case Obj::RANDOM_DWELLING_FACTION: @@ -564,7 +564,7 @@ std::pair CGameState::pickObject (CGObjectInstance *obj) //if castle alignment available if (auto info = dynamic_cast(dwl->info)) { - faction = rand.nextInt(VLC->townh->factions.size() - 1); + faction = getRandomGenerator().nextInt(VLC->townh->factions.size() - 1); if (info->asCastle) { for(auto & elem : map->objects) @@ -593,7 +593,7 @@ std::pair CGameState::pickObject (CGObjectInstance *obj) { if((faction>7) && (info->castles[1]&(1<<(faction-8)))) break; - faction = rand.nextInt(GameConstants::F_NUMBER - 1); + faction = getRandomGenerator().nextInt(GameConstants::F_NUMBER - 1); } } } @@ -605,7 +605,7 @@ std::pair CGameState::pickObject (CGObjectInstance *obj) //if level set to range if (auto info = dynamic_cast(dwl->info)) { - level = rand.nextInt(info->minLevel, info->maxLevel); + level = getRandomGenerator().nextInt(info->minLevel, info->maxLevel); } else // fixed level { @@ -639,7 +639,7 @@ std::pair CGameState::pickObject (CGObjectInstance *obj) if (result.first == Obj::NO_OBJ) { logGlobal->errorStream() << "Error: failed to find dwelling for "<< VLC->townh->factions[faction]->name << " of level " << int(level); - result = std::make_pair(Obj::CREATURE_GENERATOR1, *RandomGeneratorUtil::nextItem(VLC->objtypeh->knownSubObjects(Obj::CREATURE_GENERATOR1), rand)); + result = std::make_pair(Obj::CREATURE_GENERATOR1, *RandomGeneratorUtil::nextItem(VLC->objtypeh->knownSubObjects(Obj::CREATURE_GENERATOR1), getRandomGenerator())); } return result; @@ -727,23 +727,10 @@ CGameState::~CGameState() ptr.second.dellNull(); } -BattleInfo * CGameState::setupBattle(int3 tile, const CArmedInstance *armies[2], const CGHeroInstance * heroes[2], bool creatureBank, const CGTownInstance *town) -{ - const TerrainTile &t = map->getTile(tile); - ETerrainType terrain = t.terType; - if(map->isCoastalTile(tile)) //coastal tile is always ground - terrain = ETerrainType::SAND; - - BFieldType terType = battleGetBattlefieldType(tile); - if (heroes[0] && heroes[0]->boat && heroes[1] && heroes[1]->boat) - terType = BFieldType::SHIP_TO_SHIP; - return BattleInfo::setupBattle(tile, terrain, terType, armies, heroes, creatureBank, town); -} - void CGameState::init(StartInfo * si) { logGlobal->infoStream() << "\tUsing random seed: "<< si->seedToBeUsed; - rand.setSeed(si->seedToBeUsed); + getRandomGenerator().setSeed(si->seedToBeUsed); scenarioOps = CMemorySerializer::deepCopy(*si).release(); initialOpts = CMemorySerializer::deepCopy(*si).release(); si = nullptr; @@ -800,7 +787,7 @@ void CGameState::init(StartInfo * si) logGlobal->debug("\tChecking objectives"); map->checkForObjectives(); //needs to be run when all objects are properly placed - auto seedAfterInit = rand.nextInt(); + auto seedAfterInit = getRandomGenerator().nextInt(); logGlobal->infoStream() << "Seed after init is " << seedAfterInit << " (before was " << scenarioOps->seedToBeUsed << ")"; if(scenarioOps->seedPostInit > 0) { @@ -925,8 +912,8 @@ void CGameState::initDuel() for(TSecSKill secSkill : ss.heroSecSkills) h->setSecSkillLevel(SecondarySkill(secSkill.first), secSkill.second, 1); - h->initHero(HeroTypeID(h->subID)); - obj->initObj(); + h->initHero(getRandomGenerator(), HeroTypeID(h->subID)); + obj->initObj(getRandomGenerator()); } else { @@ -1026,7 +1013,7 @@ void CGameState::initGrailPosition() if(!allowedPos.empty()) { - map->grailPos = *RandomGeneratorUtil::nextItem(allowedPos, rand); + map->grailPos = *RandomGeneratorUtil::nextItem(allowedPos, getRandomGenerator()); } else { @@ -1042,7 +1029,7 @@ void CGameState::initRandomFactionsForPlayers() { if(elem.second.castle==-1) { - auto randomID = rand.nextInt(map->players[elem.first.getNum()].allowedFactions.size() - 1); + auto randomID = getRandomGenerator().nextInt(map->players[elem.first.getNum()].allowedFactions.size() - 1); auto iter = map->players[elem.first.getNum()].allowedFactions.begin(); std::advance(iter, randomID); @@ -1171,7 +1158,7 @@ void CGameState::placeCampaignHeroes() auto unusedHeroTypeIds = getUnusedAllowedHeroes(); if(!unusedHeroTypeIds.empty()) { - heroTypeId = (*RandomGeneratorUtil::nextItem(unusedHeroTypeIds, rand)).getNum(); + heroTypeId = (*RandomGeneratorUtil::nextItem(unusedHeroTypeIds, getRandomGenerator())).getNum(); } else { @@ -1252,7 +1239,7 @@ CGameState::CrossoverHeroesList CGameState::getCrossoverHeroesFromPreviousScenar return crossoverHeroes; } -void CGameState::prepareCrossoverHeroes(std::vector & campaignHeroReplacements, const CScenarioTravel & travelOptions) const +void CGameState::prepareCrossoverHeroes(std::vector & campaignHeroReplacements, const CScenarioTravel & travelOptions) { // create heroes list for convenience iterating std::vector crossoverHeroes; @@ -1268,7 +1255,7 @@ void CGameState::prepareCrossoverHeroes(std::vectorinitExp(); + cgh->initExp(getRandomGenerator()); } } @@ -1475,7 +1462,7 @@ void CGameState::initHeroes() continue; } - hero->initHero(); + hero->initHero(getRandomGenerator()); getPlayer(hero->getOwner())->heroes.push_back(hero); map->allHeroes[hero->type->ID.getNum()] = hero; } @@ -1491,7 +1478,7 @@ void CGameState::initHeroes() { if(!vstd::contains(heroesToCreate, HeroTypeID(ph->subID))) continue; - ph->initHero(); + ph->initHero(getRandomGenerator()); hpool.heroesPool[ph->subID] = ph; hpool.pavailable[ph->subID] = 0xff; heroesToCreate.erase(ph->type->ID); @@ -1502,7 +1489,7 @@ void CGameState::initHeroes() for(HeroTypeID htype : heroesToCreate) //all not used allowed heroes go with default state into the pool { auto vhi = new CGHeroInstance(); - vhi->initHero(htype); + vhi->initHero(getRandomGenerator(), htype); int typeID = htype.getNum(); map->allHeroes[typeID] = vhi; @@ -1660,24 +1647,24 @@ void CGameState::initStartingBonus() { //starting bonus if(scenarioOps->playerInfos[elem.first].bonus==PlayerSettings::RANDOM) - scenarioOps->playerInfos[elem.first].bonus = static_cast(rand.nextInt(2)); + scenarioOps->playerInfos[elem.first].bonus = static_cast(getRandomGenerator().nextInt(2)); switch(scenarioOps->playerInfos[elem.first].bonus) { case PlayerSettings::GOLD: - elem.second.resources[Res::GOLD] += rand.nextInt(5, 10) * 100; + elem.second.resources[Res::GOLD] += getRandomGenerator().nextInt(5, 10) * 100; break; case PlayerSettings::RESOURCE: { int res = VLC->townh->factions[scenarioOps->playerInfos[elem.first].castle]->town->primaryRes; if(res == Res::WOOD_AND_ORE) { - int amount = rand.nextInt(5, 10); + int amount = getRandomGenerator().nextInt(5, 10); elem.second.resources[Res::WOOD] += amount; elem.second.resources[Res::ORE] += amount; } else { - elem.second.resources[res] += rand.nextInt(3, 6); + elem.second.resources[res] += getRandomGenerator().nextInt(3, 6); } break; } @@ -1689,7 +1676,7 @@ void CGameState::initStartingBonus() break; } CArtifact *toGive; - toGive = VLC->arth->artifacts[VLC->arth->pickRandomArtifact(rand, CArtifact::ART_TREASURE)]; + toGive = VLC->arth->artifacts[VLC->arth->pickRandomArtifact(getRandomGenerator(), CArtifact::ART_TREASURE)]; CGHeroInstance *hero = elem.second.heroes[0]; giveHeroArtifact(hero, toGive->id); @@ -1742,7 +1729,7 @@ void CGameState::initTowns() } if(vti->name.empty()) { - vti->name = *RandomGeneratorUtil::nextItem(vti->town->names, rand); + vti->name = *RandomGeneratorUtil::nextItem(vti->town->names, getRandomGenerator()); } //init buildings @@ -1754,7 +1741,7 @@ void CGameState::initTowns() vti->builtBuildings.insert(BuildingID::TAVERN); vti->builtBuildings.insert(BuildingID::DWELL_FIRST); - if(rand.nextInt(1) == 1) + if(getRandomGenerator().nextInt(1) == 1) { vti->builtBuildings.insert(BuildingID::DWELL_LVL_2); } @@ -1826,7 +1813,7 @@ void CGameState::initTowns() if (total == 0) // remaining spells have 0 probability break; - auto r = rand.nextInt(total - 1); + auto r = getRandomGenerator().nextInt(total - 1); for(ui32 ps=0; pspossibleSpells.size();ps++) { r -= vti->possibleSpells[ps].toSpell()->getProbability(vti->subID); @@ -1859,7 +1846,7 @@ void CGameState::initMapObjects() if(obj) { logGlobal->traceStream() << boost::format ("Calling Init for object %d, %s, %s") % obj->id.getNum() % obj->typeName % obj->subTypeName; - obj->initObj(); + obj->initObj(getRandomGenerator()); } } for(CGObjectInstance *obj : map->objects) @@ -1919,7 +1906,7 @@ void CGameState::initVisitingAndGarrisonedHeroes() } } -BFieldType CGameState::battleGetBattlefieldType(int3 tile) +BFieldType CGameState::battleGetBattlefieldType(int3 tile, CRandomGenerator & rand) { if(tile==int3() && curB) tile = curB->tile; @@ -3263,9 +3250,12 @@ TeamState::TeamState(TeamState && other): std::swap(fogOfWarMap, other.fogOfWarMap); } - CRandomGenerator & CGameState::getRandomGenerator() { + //if(scenarioOps && scenarioOps->seedPostInit) + //{ + // logGlobal->trace("CGameState::getRandomGenerator used after initialization!"); + //} //logGlobal->traceStream() << "Fetching CGameState::rand with seed " << rand.nextInt(); return rand; } diff --git a/lib/CGameState.h b/lib/CGameState.h index 783c468c2..69de689f8 100644 --- a/lib/CGameState.h +++ b/lib/CGameState.h @@ -218,7 +218,7 @@ public: void giveHeroArtifact(CGHeroInstance *h, ArtifactID aid); void apply(CPack *pack); - BFieldType battleGetBattlefieldType(int3 tile); + BFieldType battleGetBattlefieldType(int3 tile, CRandomGenerator & rand); UpgradeInfo getUpgradeInfo(const CStackInstance &stack); PlayerRelations::PlayerRelations getPlayerRelations(PlayerColor color1, PlayerColor color2); bool checkForVisitableDir(const int3 & src, const int3 & dst) const; //check if src tile is visitable from dst tile @@ -236,7 +236,6 @@ public: void obtainPlayersStats(SThievesGuildInfo & tgi, int level); //fills tgi with info about other players that is available at given level of thieves' guild std::map > unusedHeroesFromPool(); //heroes pool without heroes that are available in taverns - BattleInfo * setupBattle(int3 tile, const CArmedInstance *armies[2], const CGHeroInstance * heroes[2], bool creatureBank, const CGTownInstance *town); bool isVisible(int3 pos, PlayerColor player); bool isVisible(const CGObjectInstance *obj, boost::optional player); @@ -244,6 +243,14 @@ public: int getDate(Date::EDateType mode=Date::DAY) const; //mode=0 - total days in game, mode=1 - day of week, mode=2 - current week, mode=3 - current month // ----- getters, setters ----- + + /// This RNG should only be used inside GS or CPackForClient-derived applyGs + /// If this doesn't work for your code that mean you need a new netpack + /// + /// Client-side must use CRandomGenerator::getDefault which is not serialized + /// + /// CGameHandler have it's own getter for CRandomGenerator::getDefault + /// Any server-side code outside of GH must use CRandomGenerator::getDefault CRandomGenerator & getRandomGenerator(); template void serialize(Handler &h, const int version) @@ -292,7 +299,7 @@ private: std::vector generateCampaignHeroesToReplace(CrossoverHeroesList & crossoverHeroes); /// gets prepared and copied hero instances with crossover heroes from prev. scenario and travel options from current scenario - void prepareCrossoverHeroes(std::vector & campaignHeroReplacements, const CScenarioTravel & travelOptions) const; + void prepareCrossoverHeroes(std::vector & campaignHeroReplacements, const CScenarioTravel & travelOptions); void replaceHeroesPlaceholders(const std::vector & campaignHeroReplacements); void placeStartingHeroes(); diff --git a/lib/Connection.h b/lib/Connection.h index 307de34dd..95040a7a4 100644 --- a/lib/Connection.h +++ b/lib/Connection.h @@ -27,7 +27,7 @@ #include "mapping/CCampaignHandler.h" //for CCampaignState #include "rmg/CMapGenerator.h" // for CMapGenOptions -const ui32 version = 760; +const ui32 version = 761; const ui32 minSupportedVersion = 753; class CISer; diff --git a/lib/IGameCallback.cpp b/lib/IGameCallback.cpp index 7c3b7113f..bc0b57581 100644 --- a/lib/IGameCallback.cpp +++ b/lib/IGameCallback.cpp @@ -119,14 +119,14 @@ void CPrivilagedInfoCallback::getAllTiles (std::unordered_set & } } -void CPrivilagedInfoCallback::pickAllowedArtsSet(std::vector &out) +void CPrivilagedInfoCallback::pickAllowedArtsSet(std::vector &out, CRandomGenerator & rand) { for (int j = 0; j < 3 ; j++) - out.push_back(VLC->arth->artifacts[VLC->arth->pickRandomArtifact(gameState()->getRandomGenerator(), CArtifact::ART_TREASURE)]); + out.push_back(VLC->arth->artifacts[VLC->arth->pickRandomArtifact(rand, CArtifact::ART_TREASURE)]); for (int j = 0; j < 3 ; j++) - out.push_back(VLC->arth->artifacts[VLC->arth->pickRandomArtifact(gameState()->getRandomGenerator(), CArtifact::ART_MINOR)]); + out.push_back(VLC->arth->artifacts[VLC->arth->pickRandomArtifact(rand, CArtifact::ART_MINOR)]); - out.push_back(VLC->arth->artifacts[VLC->arth->pickRandomArtifact(gameState()->getRandomGenerator(), CArtifact::ART_MAJOR)]); + out.push_back(VLC->arth->artifacts[VLC->arth->pickRandomArtifact(rand, CArtifact::ART_MAJOR)]); } void CPrivilagedInfoCallback::getAllowedSpells(std::vector &out, ui16 level) diff --git a/lib/IGameCallback.h b/lib/IGameCallback.h index 43c24d05c..ed1115d1d 100644 --- a/lib/IGameCallback.h +++ b/lib/IGameCallback.h @@ -1,6 +1,7 @@ #pragma once #include "CGameInfoCallback.h" // for CGameInfoCallback +#include "CRandomGenerator.h" /* * IGameCallback.h, part of VCMI engine @@ -32,7 +33,7 @@ public: void getFreeTiles (std::vector &tiles) const; //used for random spawns void getTilesInRange(std::unordered_set &tiles, int3 pos, int radious, boost::optional player = boost::optional(), int mode = 0, bool patrolDistance = false) const; //mode 1 - only unrevealed tiles; mode 0 - all, mode -1 - only unrevealed void getAllTiles (std::unordered_set &tiles, boost::optional player = boost::optional(), int level=-1, int surface=0) const; //returns all tiles on given level (-1 - both levels, otherwise number of level); surface: 0 - land and water, 1 - only land, 2 - only water - void pickAllowedArtsSet(std::vector &out); //gives 3 treasures, 3 minors, 1 major -> used by Black Market and Artifact Merchant + void pickAllowedArtsSet(std::vector &out, CRandomGenerator & rand); //gives 3 treasures, 3 minors, 1 major -> used by Black Market and Artifact Merchant void getAllowedSpells(std::vector &out, ui16 level); template diff --git a/lib/NetPacks.h b/lib/NetPacks.h index 6ba9dee6b..085349bbc 100644 --- a/lib/NetPacks.h +++ b/lib/NetPacks.h @@ -1093,6 +1093,23 @@ struct ChangeObjectVisitors : public CPackForClient // 1003 } }; +struct PrepareHeroLevelUp : public CPackForClient//1999 +{ + DLL_LINKAGE void applyGs(CGameState *gs); + + const CGHeroInstance *hero; + + /// Do not serialize, used by server only + std::vector skills; + + PrepareHeroLevelUp(){type = 1999;}; + + template void serialize(Handler &h, const int version) + { + h & hero; + } +}; + struct HeroLevelUp : public Query//2000 { void applyCl(CClient *cl); diff --git a/lib/NetPacksLib.cpp b/lib/NetPacksLib.cpp index 38d428fa9..45ebd819c 100644 --- a/lib/NetPacksLib.cpp +++ b/lib/NetPacksLib.cpp @@ -624,7 +624,7 @@ DLL_LINKAGE void HeroRecruited::applyGs( CGameState *gs ) h->attachTo(p); if(fresh) { - h->initObj(); + h->initObj(gs->getRandomGenerator()); } gs->map->addBlockVisTiles(h); @@ -689,7 +689,7 @@ DLL_LINKAGE void NewObject::applyGs( CGameState *gs ) gs->map->objects.push_back(o); gs->map->addBlockVisTiles(o); - o->initObj(); + o->initObj(gs->getRandomGenerator()); gs->map->calculateGuardingGreaturePositions(); logGlobal->debugStream() << "added object id=" << id << "; address=" << (intptr_t)o << "; name=" << o->getObjectName(); @@ -1162,6 +1162,21 @@ DLL_LINKAGE void SetObjectProperty::applyGs( CGameState *gs ) } } +DLL_LINKAGE void PrepareHeroLevelUp::applyGs(CGameState *gs) +{ + CGHeroInstance * h = gs->getHero(hero->id); + auto proposedSkills = h->getLevelUpProposedSecondarySkills(); + + if(skills.size() == 1 || hero->tempOwner == PlayerColor::NEUTRAL) //choose skill automatically + { + skills.push_back(*RandomGeneratorUtil::nextItem(proposedSkills, h->skillsInfo.rand)); + } + else + { + skills = proposedSkills; + } +} + DLL_LINKAGE void HeroLevelUp::applyGs( CGameState *gs ) { CGHeroInstance * h = gs->getHero(hero->id); diff --git a/lib/mapObjects/CBank.cpp b/lib/mapObjects/CBank.cpp index 65a4fcd46..e6eea5130 100644 --- a/lib/mapObjects/CBank.cpp +++ b/lib/mapObjects/CBank.cpp @@ -34,11 +34,11 @@ CBank::~CBank() { } -void CBank::initObj() +void CBank::initObj(CRandomGenerator & rand) { daycounter = 0; resetDuration = 0; - VLC->objtypeh->getHandlerFor(ID, subID)->configureObject(this, cb->gameState()->getRandomGenerator()); + VLC->objtypeh->getHandlerFor(ID, subID)->configureObject(this, rand); } std::string CBank::getHoverText(PlayerColor player) const @@ -64,7 +64,8 @@ void CBank::setPropertyDer (ui8 what, ui32 val) daycounter+=val; break; case ObjProperty::BANK_RESET: - initObj(); + // FIXME: Object reset must be done by separate netpack from server + initObj(cb->gameState()->getRandomGenerator()); daycounter = 1; //yes, 1 since "today" daycounter won't be incremented break; case ObjProperty::BANK_CLEAR: @@ -73,7 +74,7 @@ void CBank::setPropertyDer (ui8 what, ui32 val) } } -void CBank::newTurn() const +void CBank::newTurn(CRandomGenerator & rand) const { if (bc == nullptr) { diff --git a/lib/mapObjects/CBank.h b/lib/mapObjects/CBank.h index 59b954116..cd057b14e 100644 --- a/lib/mapObjects/CBank.h +++ b/lib/mapObjects/CBank.h @@ -31,9 +31,9 @@ public: void setConfig(const BankConfig & bc); - void initObj() override; + void initObj(CRandomGenerator & rand) override; std::string getHoverText(PlayerColor player) const override; - void newTurn() const override; + void newTurn(CRandomGenerator & rand) const override; bool wasVisited (PlayerColor player) const override; void onHeroVisit(const CGHeroInstance * h) const override; void battleFinished(const CGHeroInstance *hero, const BattleResult &result) const override; diff --git a/lib/mapObjects/CGHeroInstance.cpp b/lib/mapObjects/CGHeroInstance.cpp index e66a62677..c94e58834 100644 --- a/lib/mapObjects/CGHeroInstance.cpp +++ b/lib/mapObjects/CGHeroInstance.cpp @@ -244,10 +244,10 @@ CGHeroInstance::CGHeroInstance() secSkills.push_back(std::make_pair(SecondarySkill::DEFAULT, -1)); } -void CGHeroInstance::initHero(HeroTypeID SUBID) +void CGHeroInstance::initHero(CRandomGenerator & rand, HeroTypeID SUBID) { subID = SUBID.getNum(); - initHero(); + initHero(rand); } void CGHeroInstance::setType(si32 ID, si32 subID) @@ -259,7 +259,7 @@ void CGHeroInstance::setType(si32 ID, si32 subID) randomizeArmy(type->heroClass->faction); } -void CGHeroInstance::initHero() +void CGHeroInstance::initHero(CRandomGenerator & rand) { assert(validTypes(true)); if(!type) @@ -302,17 +302,17 @@ void CGHeroInstance::initHero() setFormation(false); if (!stacksCount()) //standard army//initial army { - initArmy(); + initArmy(rand); } assert(validTypes()); if(exp == 0xffffffff) { - initExp(); + initExp(rand); } else { - levelUpAutomatically(); + levelUpAutomatically(rand); } if (VLC->modh->modules.COMMANDERS && !commander) @@ -326,13 +326,13 @@ void CGHeroInstance::initHero() mana = manaLimit(); } -void CGHeroInstance::initArmy(IArmyDescriptor *dst /*= nullptr*/) +void CGHeroInstance::initArmy(CRandomGenerator & rand, IArmyDescriptor *dst /*= nullptr*/) { if(!dst) dst = this; int howManyStacks = 0; //how many stacks will hero receives <1 - 3> - int pom = cb->gameState()->getRandomGenerator().nextInt(99); + int pom = rand.nextInt(99); int warMachinesGiven = 0; if(pom < 9) @@ -348,7 +348,7 @@ void CGHeroInstance::initArmy(IArmyDescriptor *dst /*= nullptr*/) { auto & stack = type->initialArmy[stackNo]; - int count = cb->gameState()->getRandomGenerator().nextInt(stack.minAmount, stack.maxAmount); + int count = rand.nextInt(stack.minAmount, stack.maxAmount); if(stack.creature >= CreatureID::CATAPULT && stack.creature <= CreatureID::ARROW_TOWERS) //war machine @@ -483,7 +483,7 @@ void CGHeroInstance::SecondarySkillsInfo::resetWisdomCounter() wisdomCounter = 1; } -void CGHeroInstance::initObj() +void CGHeroInstance::initObj(CRandomGenerator & rand) { blockVisit = true; auto hs = new HeroSpecial(); @@ -491,9 +491,9 @@ void CGHeroInstance::initObj() attachTo(hs); //do we ever need to detach it? if(!type) - initHero(); //TODO: set up everything for prison before specialties are configured + initHero(rand); //TODO: set up everything for prison before specialties are configured - skillsInfo.rand.setSeed(cb->gameState()->getRandomGenerator().nextInt()); + skillsInfo.rand.setSeed(rand.nextInt()); skillsInfo.resetMagicSchoolCounter(); skillsInfo.resetWisdomCounter(); @@ -1056,10 +1056,10 @@ CStackBasicDescriptor CGHeroInstance::calculateNecromancy (const BattleResult &b * @param raisedStack Pair where the first element represents ID of the raised creature * and the second element the amount. */ -void CGHeroInstance::showNecromancyDialog(const CStackBasicDescriptor &raisedStack) const +void CGHeroInstance::showNecromancyDialog(const CStackBasicDescriptor &raisedStack, CRandomGenerator & rand) const { InfoWindow iw; - iw.soundID = soundBase::pickup01 + cb->gameState()->getRandomGenerator().nextInt(6); + iw.soundID = soundBase::pickup01 + rand.nextInt(6); iw.player = tempOwner; iw.components.push_back(Component(raisedStack)); @@ -1162,9 +1162,9 @@ EAlignment::EAlignment CGHeroInstance::getAlignment() const return type->heroClass->getAlignment(); } -void CGHeroInstance::initExp() +void CGHeroInstance::initExp(CRandomGenerator & rand) { - exp = cb->gameState()->getRandomGenerator().nextInt(40, 89); + exp = rand.nextInt(40, 89); } std::string CGHeroInstance::nodeName() const @@ -1337,10 +1337,10 @@ std::vector CGHeroInstance::getLevelUpProposedSecondarySkills() return skills; } -PrimarySkill::PrimarySkill CGHeroInstance::nextPrimarySkill() const +PrimarySkill::PrimarySkill CGHeroInstance::nextPrimarySkill(CRandomGenerator & rand) const { assert(gainsLevel()); - int randomValue = cb->gameState()->getRandomGenerator().nextInt(99), pom = 0, primarySkill = 0; + int randomValue = rand.nextInt(99), pom = 0, primarySkill = 0; const auto & skillChances = (level > 9) ? type->heroClass->primarySkillLowLevel : type->heroClass->primarySkillHighLevel; for(; primarySkill < GameConstants::PRIMARY_SKILLS; ++primarySkill) @@ -1356,7 +1356,7 @@ PrimarySkill::PrimarySkill CGHeroInstance::nextPrimarySkill() const return static_cast(primarySkill); } -boost::optional CGHeroInstance::nextSecondarySkill() const +boost::optional CGHeroInstance::nextSecondarySkill(CRandomGenerator & rand) const { assert(gainsLevel()); @@ -1373,7 +1373,6 @@ boost::optional CGHeroInstance::nextSecondarySkill() const } } - auto & rand = cb->gameState()->getRandomGenerator(); if(learnedSecondarySkills.empty()) { // there are only new skills to learn, so choose anyone of them @@ -1452,16 +1451,16 @@ void CGHeroInstance::levelUp(std::vector skills) Updatespecialty(); } -void CGHeroInstance::levelUpAutomatically() +void CGHeroInstance::levelUpAutomatically(CRandomGenerator & rand) { while(gainsLevel()) { - const auto primarySkill = nextPrimarySkill(); + const auto primarySkill = nextPrimarySkill(rand); setPrimarySkill(primarySkill, 1, false); auto proposedSecondarySkills = getLevelUpProposedSecondarySkills(); - const auto secondarySkill = nextSecondarySkill(); + const auto secondarySkill = nextSecondarySkill(rand); if(secondarySkill) { setSecSkillLevel(*secondarySkill, 1, false); diff --git a/lib/mapObjects/CGHeroInstance.h b/lib/mapObjects/CGHeroInstance.h index fb8c1256c..dd9d4bffb 100644 --- a/lib/mapObjects/CGHeroInstance.h +++ b/lib/mapObjects/CGHeroInstance.h @@ -163,10 +163,10 @@ public: bool gainsLevel() const; /// Returns the next primary skill on level up. Can only be called if hero can gain a level up. - PrimarySkill::PrimarySkill nextPrimarySkill() const; + PrimarySkill::PrimarySkill nextPrimarySkill(CRandomGenerator & rand) const; /// Returns the next secondary skill randomly on level up. Can only be called if hero can gain a level up. - boost::optional nextSecondarySkill() const; + boost::optional nextSecondarySkill(CRandomGenerator & rand) const; /// Gets 0, 1 or 2 secondary skills which are proposed on hero level up. std::vector getLevelUpProposedSecondarySkills() const; @@ -192,20 +192,20 @@ public: bool canCastThisSpell(const CSpell * spell) const; //determines if this hero can cast given spell; takes into account existing spell in spellbook, existing spellbook and artifact bonuses CStackBasicDescriptor calculateNecromancy (const BattleResult &battleResult) const; - void showNecromancyDialog(const CStackBasicDescriptor &raisedStack) const; + void showNecromancyDialog(const CStackBasicDescriptor &raisedStack, CRandomGenerator & rand) const; EDiggingStatus diggingStatus() const; ////////////////////////////////////////////////////////////////////////// void setType(si32 ID, si32 subID) override; - void initHero(); - void initHero(HeroTypeID SUBID); + void initHero(CRandomGenerator & rand); + void initHero(CRandomGenerator & rand, HeroTypeID SUBID); void putArtifact(ArtifactPosition pos, CArtifactInstance *art); void putInBackpack(CArtifactInstance *art); - void initExp(); - void initArmy(IArmyDescriptor *dst = nullptr); + void initExp(CRandomGenerator & rand); + void initArmy(CRandomGenerator & rand, IArmyDescriptor *dst = nullptr); //void giveArtifact (ui32 aid); void pushPrimSkill(PrimarySkill::PrimarySkill which, int val); ui8 maxlevelsToMagicSchool() const; @@ -248,7 +248,7 @@ public: void deserializationFix(); - void initObj() override; + void initObj(CRandomGenerator & rand) override; void onHeroVisit(const CGHeroInstance * h) const override; std::string getObjectName() const override; protected: @@ -256,7 +256,7 @@ protected: void serializeJsonOptions(JsonSerializeFormat & handler) override; private: - void levelUpAutomatically(); + void levelUpAutomatically(CRandomGenerator & rand); public: template void serialize(Handler &h, const int version) diff --git a/lib/mapObjects/CGMarket.cpp b/lib/mapObjects/CGMarket.cpp index fbbf35c46..4bf20f03c 100644 --- a/lib/mapObjects/CGMarket.cpp +++ b/lib/mapObjects/CGMarket.cpp @@ -282,18 +282,18 @@ std::vector CGBlackMarket::availableItemsIds(EMarketMode::EMarketMode mode) } } -void CGBlackMarket::newTurn() const +void CGBlackMarket::newTurn(CRandomGenerator & rand) const { if(cb->getDate(Date::DAY_OF_MONTH) != 1) //new month return; SetAvailableArtifacts saa; saa.id = id.getNum(); - cb->pickAllowedArtsSet(saa.arts); + cb->pickAllowedArtsSet(saa.arts, rand); cb->sendAndApply(&saa); } -void CGUniversity::initObj() +void CGUniversity::initObj(CRandomGenerator & rand) { std::vector toChoose; for(int i = 0; i < GameConstants::SKILL_QUANTITY; ++i) @@ -313,7 +313,7 @@ void CGUniversity::initObj() for(int i = 0; i < 4; ++i) { // move randomly one skill to selected and remove from list - auto it = RandomGeneratorUtil::nextItem(toChoose, cb->gameState()->getRandomGenerator()); + auto it = RandomGeneratorUtil::nextItem(toChoose, rand); skills.push_back(*it); toChoose.erase(it); } diff --git a/lib/mapObjects/CGMarket.h b/lib/mapObjects/CGMarket.h index d458f9dbd..e5bae62ae 100644 --- a/lib/mapObjects/CGMarket.h +++ b/lib/mapObjects/CGMarket.h @@ -61,7 +61,7 @@ class DLL_LINKAGE CGBlackMarket : public CGMarket public: std::vector artifacts; //available artifacts - void newTurn() const override; //reset artifacts for black market every month + void newTurn(CRandomGenerator & rand) const override; //reset artifacts for black market every month std::vector availableItemsIds(EMarketMode::EMarketMode mode) const override; template void serialize(Handler &h, const int version) @@ -77,7 +77,7 @@ public: std::vector skills; //available skills std::vector availableItemsIds(EMarketMode::EMarketMode mode) const override; - void initObj() override;//set skills for trade + void initObj(CRandomGenerator & rand) override;//set skills for trade void onHeroVisit(const CGHeroInstance * h) const override; //open window template void serialize(Handler &h, const int version) diff --git a/lib/mapObjects/CGPandoraBox.cpp b/lib/mapObjects/CGPandoraBox.cpp index ba3917de1..8568772d3 100644 --- a/lib/mapObjects/CGPandoraBox.cpp +++ b/lib/mapObjects/CGPandoraBox.cpp @@ -34,7 +34,7 @@ static void showInfoDialog(const CGHeroInstance* h, const ui32 txtID, const ui16 showInfoDialog(playerID,txtID,soundID); } -void CGPandoraBox::initObj() +void CGPandoraBox::initObj(CRandomGenerator & rand) { blockVisit = (ID==Obj::PANDORAS_BOX); //block only if it's really pandora's box (events also derive from that class) hasGuardians = stacks.size(); diff --git a/lib/mapObjects/CGPandoraBox.h b/lib/mapObjects/CGPandoraBox.h index 13be53add..dd851dac4 100644 --- a/lib/mapObjects/CGPandoraBox.h +++ b/lib/mapObjects/CGPandoraBox.h @@ -36,7 +36,7 @@ public: CCreatureSet creatures; //gained creatures CGPandoraBox() : gainedExp(0), manaDiff(0), moraleDiff(0), luckDiff(0){}; - void initObj() override; + void initObj(CRandomGenerator & rand) override; void onHeroVisit(const CGHeroInstance * h) const override; void battleFinished(const CGHeroInstance *hero, const BattleResult &result) const override; void blockingDialogAnswered(const CGHeroInstance *hero, ui32 answer) const override; diff --git a/lib/mapObjects/CGTownInstance.cpp b/lib/mapObjects/CGTownInstance.cpp index f15707f65..b007d4cf1 100644 --- a/lib/mapObjects/CGTownInstance.cpp +++ b/lib/mapObjects/CGTownInstance.cpp @@ -25,14 +25,14 @@ std::vector CGTownInstance::merchantArtifacts; std::vector CGTownInstance::universitySkills; -void CGDwelling::initObj() +void CGDwelling::initObj(CRandomGenerator & rand) { switch(ID) { case Obj::CREATURE_GENERATOR1: case Obj::CREATURE_GENERATOR4: { - VLC->objtypeh->getHandlerFor(ID, subID)->configureObject(this, cb->gameState()->getRandomGenerator()); + VLC->objtypeh->getHandlerFor(ID, subID)->configureObject(this, rand); if (getOwner() != PlayerColor::NEUTRAL) cb->gameState()->players[getOwner()].dwellings.push_back (this); @@ -141,7 +141,7 @@ void CGDwelling::onHeroVisit( const CGHeroInstance * h ) const cb->showBlockingDialog(&bd); } -void CGDwelling::newTurn() const +void CGDwelling::newTurn(CRandomGenerator & rand) const { if(cb->getDate(Date::DAY_OF_WEEK) != 1) //not first day of week return; @@ -152,7 +152,7 @@ void CGDwelling::newTurn() const if(ID == Obj::REFUGEE_CAMP) //if it's a refugee camp, we need to pick an available creature { - cb->setObjProperty(id, ObjProperty::AVAILABLE_CREATURE, VLC->creh->pickRandomMonster(cb->gameState()->getRandomGenerator())); + cb->setObjProperty(id, ObjProperty::AVAILABLE_CREATURE, VLC->creh->pickRandomMonster(rand)); } bool change = false; @@ -595,7 +595,7 @@ std::string CGTownInstance::getObjectName() const return name + ", " + town->faction->name; } -void CGTownInstance::initObj() +void CGTownInstance::initObj(CRandomGenerator & rand) ///initialize town structures { blockVisit = true; @@ -637,12 +637,10 @@ void CGTownInstance::initObj() updateAppearance(); } -void CGTownInstance::newTurn() const +void CGTownInstance::newTurn(CRandomGenerator & rand) const { if (cb->getDate(Date::DAY_OF_WEEK) == 1) //reset on new week { - auto & rand = cb->gameState()->getRandomGenerator(); - //give resources for Rampart, Mystic Pond if (hasBuilt(BuildingID::MYSTIC_POND, ETownType::RAMPART) && cb->getDate(Date::DAY) != 1 && (tempOwner < PlayerColor::PLAYER_LIMIT)) diff --git a/lib/mapObjects/CGTownInstance.h b/lib/mapObjects/CGTownInstance.h index 620b37794..451426b2e 100644 --- a/lib/mapObjects/CGTownInstance.h +++ b/lib/mapObjects/CGTownInstance.h @@ -56,9 +56,9 @@ protected: void serializeJsonOptions(JsonSerializeFormat & handler) override; private: - void initObj() override; + void initObj(CRandomGenerator & rand) override; void onHeroVisit(const CGHeroInstance * h) const override; - void newTurn() const override; + void newTurn(CRandomGenerator & rand) const override; void setPropertyDer(ui8 what, ui32 val) override; void battleFinished(const CGHeroInstance *hero, const BattleResult &result) const override; void blockingDialogAnswered(const CGHeroInstance *hero, ui32 answer) const override; @@ -249,10 +249,10 @@ public: virtual ~CGTownInstance(); ///IObjectInterface overrides - void newTurn() const override; + void newTurn(CRandomGenerator & rand) const override; void onHeroVisit(const CGHeroInstance * h) const override; void onHeroLeave(const CGHeroInstance * h) const override; - void initObj() override; + void initObj(CRandomGenerator & rand) override; void battleFinished(const CGHeroInstance *hero, const BattleResult &result) const override; std::string getObjectName() const override; protected: diff --git a/lib/mapObjects/CObjectHandler.cpp b/lib/mapObjects/CObjectHandler.cpp index aad56a1a2..c18099cb8 100644 --- a/lib/mapObjects/CObjectHandler.cpp +++ b/lib/mapObjects/CObjectHandler.cpp @@ -66,7 +66,7 @@ void IObjectInterface::onHeroVisit(const CGHeroInstance * h) const void IObjectInterface::onHeroLeave(const CGHeroInstance * h) const {} -void IObjectInterface::newTurn () const +void IObjectInterface::newTurn(CRandomGenerator & rand) const {} IObjectInterface::~IObjectInterface() @@ -75,7 +75,7 @@ IObjectInterface::~IObjectInterface() IObjectInterface::IObjectInterface() {} -void IObjectInterface::initObj() +void IObjectInterface::initObj(CRandomGenerator & rand) {} void IObjectInterface::setProperty( ui8 what, ui32 val ) @@ -207,7 +207,7 @@ void CGObjectInstance::setType(si32 ID, si32 subID) cb->gameState()->map->addBlockVisTiles(this); } -void CGObjectInstance::initObj() +void CGObjectInstance::initObj(CRandomGenerator & rand) { switch(ID) { diff --git a/lib/mapObjects/CObjectHandler.h b/lib/mapObjects/CObjectHandler.h index 0c18f97d1..da0e60d5c 100644 --- a/lib/mapObjects/CObjectHandler.h +++ b/lib/mapObjects/CObjectHandler.h @@ -22,6 +22,7 @@ class CGObjectInstance; struct MetaString; struct BattleResult; class JsonSerializeFormat; +class CRandomGenerator; // This one teleport-specific, but has to be available everywhere in callbacks and netpacks // For now it's will be there till teleports code refactored and moved into own file @@ -37,8 +38,8 @@ public: virtual void onHeroVisit(const CGHeroInstance * h) const; virtual void onHeroLeave(const CGHeroInstance * h) const; - virtual void newTurn() const; - virtual void initObj(); //synchr + virtual void newTurn(CRandomGenerator & rand) const; + virtual void initObj(CRandomGenerator & rand); //synchr virtual void setProperty(ui8 what, ui32 val);//synchr //Called when queries created DURING HERO VISIT are resolved @@ -166,7 +167,7 @@ public: /** OVERRIDES OF IObjectInterface **/ - void initObj() override; + void initObj(CRandomGenerator & rand) override; void onHeroVisit(const CGHeroInstance * h) const override; /// method for synchronous update. Note: For new properties classes should override setPropertyDer instead void setProperty(ui8 what, ui32 val) override final; diff --git a/lib/mapObjects/CQuest.cpp b/lib/mapObjects/CQuest.cpp index 4290a9368..2a565d0b6 100644 --- a/lib/mapObjects/CQuest.cpp +++ b/lib/mapObjects/CQuest.cpp @@ -420,16 +420,16 @@ void CGSeerHut::setObjToKill() } } -void CGSeerHut::init() +void CGSeerHut::init(CRandomGenerator & rand) { - seerName = *RandomGeneratorUtil::nextItem(VLC->generaltexth->seerNames, cb->gameState()->getRandomGenerator()); - quest->textOption = cb->gameState()->getRandomGenerator().nextInt(2); - quest->completedOption = cb->gameState()->getRandomGenerator().nextInt(1, 3); + seerName = *RandomGeneratorUtil::nextItem(VLC->generaltexth->seerNames, rand); + quest->textOption = rand.nextInt(2); + quest->completedOption = rand.nextInt(1, 3); } -void CGSeerHut::initObj() +void CGSeerHut::initObj(CRandomGenerator & rand) { - init(); + init(rand); quest->progress = CQuest::NOT_ACTIVE; if(quest->missionType) @@ -549,7 +549,7 @@ void CGSeerHut::setPropertyDer (ui8 what, ui32 val) } } -void CGSeerHut::newTurn() const +void CGSeerHut::newTurn(CRandomGenerator & rand) const { if(quest->lastDay >= 0 && quest->lastDay <= cb->getDate() - 1) //time is up { @@ -761,11 +761,11 @@ void CGSeerHut::blockingDialogAnswered(const CGHeroInstance *hero, ui32 answer) finishQuest(hero, answer); } -void CGQuestGuard::init() +void CGQuestGuard::init(CRandomGenerator & rand) { blockVisit = true; - quest->textOption = cb->gameState()->getRandomGenerator().nextInt(3, 5); - quest->completedOption = cb->gameState()->getRandomGenerator().nextInt(4, 5); + quest->textOption = rand.nextInt(3, 5); + quest->completedOption = rand.nextInt(4, 5); } void CGQuestGuard::completeQuest(const CGHeroInstance *h) const @@ -825,7 +825,7 @@ void CGKeymasterTent::onHeroVisit( const CGHeroInstance * h ) const showInfoDialog(h,txt_id,soundBase::CAVEHEAD); } -void CGBorderGuard::initObj() +void CGBorderGuard::initObj(CRandomGenerator & rand) { //ui32 m13489val = subID; //store color as quest info blockVisit = true; diff --git a/lib/mapObjects/CQuest.h b/lib/mapObjects/CQuest.h index 0b83cb293..6a65e6f33 100644 --- a/lib/mapObjects/CQuest.h +++ b/lib/mapObjects/CQuest.h @@ -108,13 +108,13 @@ public: std::string seerName; CGSeerHut(); - void initObj() override; + void initObj(CRandomGenerator & rand) override; std::string getHoverText(PlayerColor player) const override; - void newTurn() const override; + void newTurn(CRandomGenerator & rand) const override; void onHeroVisit(const CGHeroInstance * h) const override; void blockingDialogAnswered(const CGHeroInstance *hero, ui32 answer) const override; - virtual void init(); + virtual void init(CRandomGenerator & rand); int checkDirection() const; //calculates the region of map where monster is placed void setObjToKill(); //remember creatures / heroes to kill after they are initialized const CGHeroInstance *getHeroToKill(bool allowNull = false) const; @@ -139,7 +139,7 @@ class DLL_LINKAGE CGQuestGuard : public CGSeerHut { public: CGQuestGuard() : CGSeerHut(){}; - void init() override; + void init(CRandomGenerator & rand) override; void completeQuest (const CGHeroInstance * h) const override; template void serialize(Handler &h, const int version) @@ -185,7 +185,7 @@ class DLL_LINKAGE CGBorderGuard : public CGKeys, public IQuestObject { public: CGBorderGuard() : IQuestObject(){}; - void initObj() override; + void initObj(CRandomGenerator & rand) override; void onHeroVisit(const CGHeroInstance * h) const override; void blockingDialogAnswered(const CGHeroInstance *hero, ui32 answer) const override; diff --git a/lib/mapObjects/CRewardableObject.cpp b/lib/mapObjects/CRewardableObject.cpp index 1c8163f4b..9f488cda3 100644 --- a/lib/mapObjects/CRewardableObject.cpp +++ b/lib/mapObjects/CRewardableObject.cpp @@ -158,7 +158,7 @@ void CRewardableObject::onHeroVisit(const CGHeroInstance *h) const grantRewardWithMessage(rewards[0]); break; case SELECT_RANDOM: // select one randomly //TODO: use weights - grantRewardWithMessage(rewards[cb->gameState()->getRandomGenerator().nextInt(rewards.size()-1)]); + grantRewardWithMessage(rewards[CRandomGenerator::getDefault().nextInt(rewards.size()-1)]); break; } return; @@ -431,7 +431,7 @@ void CRewardableObject::setPropertyDer(ui8 what, ui32 val) } } -void CRewardableObject::newTurn() const +void CRewardableObject::newTurn(CRandomGenerator & rand) const { if (resetDuration != 0 && cb->getDate(Date::DAY) > 1 && (cb->getDate(Date::DAY) % resetDuration) == 1) cb->setObjProperty(id, ObjProperty::REWARD_RESET, 0); @@ -450,11 +450,11 @@ CRewardableObject::CRewardableObject(): /////////////////////////////////////////////////////////////////////////////////////////////////// /// Helper, selects random art class based on weights -static int selectRandomArtClass(int treasure, int minor, int major, int relic) +static int selectRandomArtClass(CRandomGenerator & rand, int treasure, int minor, int major, int relic) { int total = treasure + minor + major + relic; assert(total != 0); - int hlp = IObjectInterface::cb->gameState()->getRandomGenerator().nextInt(total - 1); + int hlp = rand.nextInt(total - 1); if(hlp < treasure) return CArtifact::ART_TREASURE; @@ -466,10 +466,10 @@ static int selectRandomArtClass(int treasure, int minor, int major, int relic) } /// Helper, adds random artifact to reward selecting class based on weights -static void loadRandomArtifact(CVisitInfo & info, int treasure, int minor, int major, int relic) +static void loadRandomArtifact(CRandomGenerator & rand, CVisitInfo & info, int treasure, int minor, int major, int relic) { - int artClass = selectRandomArtClass(treasure, minor, major, relic); - ArtifactID artID = VLC->arth->pickRandomArtifact(IObjectInterface::cb->gameState()->getRandomGenerator(), artClass); + int artClass = selectRandomArtClass(rand, treasure, minor, major, relic); + ArtifactID artID = VLC->arth->pickRandomArtifact(rand, artClass); info.reward.artifacts.push_back(artID); } @@ -479,7 +479,7 @@ CGPickable::CGPickable() selectMode = SELECT_PLAYER; } -void CGPickable::initObj() +void CGPickable::initObj(CRandomGenerator & rand) { blockVisit = true; switch(ID) @@ -487,8 +487,8 @@ void CGPickable::initObj() case Obj::CAMPFIRE: { soundID = soundBase::experience; - int givenRes = cb->gameState()->getRandomGenerator().nextInt(5); - int givenAmm = cb->gameState()->getRandomGenerator().nextInt(4, 6); + int givenRes = rand.nextInt(5); + int givenAmm = rand.nextInt(4, 6); info.resize(1); info[0].reward.resources[givenRes] = givenAmm; @@ -499,7 +499,7 @@ void CGPickable::initObj() } case Obj::FLOTSAM: { - int type = cb->gameState()->getRandomGenerator().nextInt(3); + int type = rand.nextInt(3); soundID = soundBase::GENIE; switch(type) { @@ -540,7 +540,7 @@ void CGPickable::initObj() case Obj::SEA_CHEST: { soundID = soundBase::chest; - int hlp = cb->gameState()->getRandomGenerator().nextInt(99); + int hlp = rand.nextInt(99); if(hlp < 20) { info.resize(1); @@ -557,7 +557,7 @@ void CGPickable::initObj() else { info.resize(1); - loadRandomArtifact(info[0], 100, 0, 0, 0); + loadRandomArtifact(rand, info[0], 100, 0, 0, 0); info[0].reward.resources[Res::GOLD] = 1000; info[0].message.addTxt(MetaString::ADVOB_TXT, 117); info[0].message.addReplacement(MetaString::ART_NAMES, info[0].reward.artifacts.back()); @@ -569,7 +569,7 @@ void CGPickable::initObj() { soundID = soundBase::experience; info.resize(1); - loadRandomArtifact(info[0], 55, 20, 20, 5); + loadRandomArtifact(rand, info[0], 55, 20, 20, 5); info[0].message.addTxt(MetaString::ADVOB_TXT, 125); info[0].message.addReplacement(MetaString::ART_NAMES, info[0].reward.artifacts.back()); info[0].reward.removeObject = true; @@ -577,12 +577,12 @@ void CGPickable::initObj() break; case Obj::TREASURE_CHEST: { - int hlp = cb->gameState()->getRandomGenerator().nextInt(99); + int hlp = rand.nextInt(99); if(hlp >= 95) { soundID = soundBase::treasure; info.resize(1); - loadRandomArtifact(info[0], 100, 0, 0, 0); + loadRandomArtifact(rand, info[0], 100, 0, 0, 0); info[0].message.addTxt(MetaString::ADVOB_TXT,145); info[0].message.addReplacement(MetaString::ART_NAMES, info[0].reward.artifacts.back()); info[0].reward.removeObject = true; @@ -631,7 +631,7 @@ CGBonusingObject::CGBonusingObject() selectMode = SELECT_FIRST; } -void CGBonusingObject::initObj() +void CGBonusingObject::initObj(CRandomGenerator & rand) { auto configureBonusDuration = [&](CVisitInfo & visit, Bonus::BonusDuration duration, Bonus::BonusType type, si32 value, si32 descrID) { @@ -817,7 +817,7 @@ CGOnceVisitable::CGOnceVisitable() selectMode = SELECT_FIRST; } -void CGOnceVisitable::initObj() +void CGOnceVisitable::initObj(CRandomGenerator & rand) { switch(ID) { @@ -826,10 +826,10 @@ void CGOnceVisitable::initObj() onEmpty.addTxt(MetaString::ADVOB_TXT, 38); soundID = soundBase::MYSTERY; blockVisit = true; - if(cb->gameState()->getRandomGenerator().nextInt(99) < 20) + if(rand.nextInt(99) < 20) { info.resize(1); - loadRandomArtifact(info[0], 10, 10, 10, 0); + loadRandomArtifact(rand, info[0], 10, 10, 10, 0); info[0].message.addTxt(MetaString::ADVOB_TXT, 37); } } @@ -839,8 +839,8 @@ void CGOnceVisitable::initObj() soundID = soundBase::GENIE; onEmpty.addTxt(MetaString::ADVOB_TXT, 65); info.resize(1); - int type = cb->gameState()->getRandomGenerator().nextInt(5); //any basic resource without gold - int value = cb->gameState()->getRandomGenerator().nextInt(1, 4); + int type = rand.nextInt(5); //any basic resource without gold + int value = rand.nextInt(1, 4); info[0].reward.resources[type] = value; info[0].message.addTxt(MetaString::ADVOB_TXT, 64); } @@ -851,7 +851,7 @@ void CGOnceVisitable::initObj() onSelect.addTxt(MetaString::ADVOB_TXT, 161); info.resize(2); - loadRandomArtifact(info[0], 30, 50, 25, 5); + loadRandomArtifact(rand, info[0], 30, 50, 25, 5); Bonus bonus(Bonus::ONE_BATTLE, Bonus::MORALE, Bonus::OBJECT, -3, ID); info[0].reward.bonuses.push_back(bonus); @@ -866,19 +866,19 @@ void CGOnceVisitable::initObj() soundID = soundBase::GENIE; onVisited.addTxt(MetaString::ADVOB_TXT, 156); - int hlp = cb->gameState()->getRandomGenerator().nextInt(99); + int hlp = rand.nextInt(99); if(hlp < 40) //minor or treasure art { info.resize(1); - loadRandomArtifact(info[0], 10, 10, 0, 0); + loadRandomArtifact(rand, info[0], 10, 10, 0, 0); info[0].message.addTxt(MetaString::ADVOB_TXT, 155); } else if(hlp < 90) //2 - 5 of non-gold resource { info.resize(1); - int type = cb->gameState()->getRandomGenerator().nextInt(5); - int value = cb->gameState()->getRandomGenerator().nextInt(2, 5); + int type = rand.nextInt(5); + int value = rand.nextInt(2, 5); info[0].reward.resources[type] = value; info[0].message.addTxt(MetaString::ADVOB_TXT, 154); } @@ -896,7 +896,7 @@ CGVisitableOPH::CGVisitableOPH() selectMode = SELECT_PLAYER; } -void CGVisitableOPH::initObj() +void CGVisitableOPH::initObj(CRandomGenerator & rand) { switch(ID) { @@ -951,7 +951,7 @@ void CGVisitableOPH::initObj() info[0].reward.gainedLevels = 1; onVisited.addTxt(MetaString::ADVOB_TXT, 147); info.resize(1); - switch (cb->gameState()->getRandomGenerator().nextInt(2)) + switch (rand.nextInt(2)) { case 0: // free onSelect.addTxt(MetaString::ADVOB_TXT, 148); @@ -1029,7 +1029,7 @@ CGVisitableOPW::CGVisitableOPW() resetDuration = 7; } -void CGVisitableOPW::initObj() +void CGVisitableOPW::initObj(CRandomGenerator & rand) { switch (ID) { @@ -1087,7 +1087,7 @@ void CGVisitableOPW::setPropertyDer(ui8 what, ui32 val) /////////////////////////////////////////////////////////////////////////////////////////////////// -void CGMagicSpring::initObj() +void CGMagicSpring::initObj(CRandomGenerator & rand) { CVisitInfo visit; // TODO: "player above max mana" limiter. Use logical expressions for limiters? visit.reward.manaPercentage = 200; diff --git a/lib/mapObjects/CRewardableObject.h b/lib/mapObjects/CRewardableObject.h index 763a34333..2a539d2d8 100644 --- a/lib/mapObjects/CRewardableObject.h +++ b/lib/mapObjects/CRewardableObject.h @@ -227,7 +227,7 @@ public: void onHeroVisit(const CGHeroInstance *h) const override; ///possibly resets object state - void newTurn() const override; + void newTurn(CRandomGenerator & rand) const override; /// gives second part of reward after hero level-ups for proper granting of spells/mana void heroLevelUpDone(const CGHeroInstance *hero) const override; @@ -255,7 +255,7 @@ public: class DLL_LINKAGE CGPickable : public CRewardableObject //campfire, treasure chest, Flotsam, Shipwreck Survivor, Sea Chest { public: - void initObj() override; + void initObj(CRandomGenerator & rand) override; CGPickable(); @@ -273,7 +273,7 @@ protected: void grantReward(ui32 rewardID, const CGHeroInstance * hero) const override; public: - void initObj() override; + void initObj(CRandomGenerator & rand) override; CGBonusingObject(); @@ -290,7 +290,7 @@ public: class DLL_LINKAGE CGOnceVisitable : public CRewardableObject // wagon, corpse, lean to, warriors tomb { public: - void initObj() override; + void initObj(CRandomGenerator & rand) override; CGOnceVisitable(); @@ -303,7 +303,7 @@ public: class DLL_LINKAGE CGVisitableOPH : public CRewardableObject //objects visitable only once per hero { public: - void initObj() override; + void initObj(CRandomGenerator & rand) override; CGVisitableOPH(); @@ -316,7 +316,7 @@ public: class DLL_LINKAGE CGVisitableOPW : public CRewardableObject //objects visitable once per week { public: - void initObj() override; + void initObj(CRandomGenerator & rand) override; CGVisitableOPW(); @@ -335,7 +335,7 @@ protected: std::vector getAvailableRewards(const CGHeroInstance * hero) const override; public: - void initObj() override; + void initObj(CRandomGenerator & rand) override; std::vector getVisitableOffsets() const; int3 getVisitableOffset() const override; diff --git a/lib/mapObjects/MiscObjects.cpp b/lib/mapObjects/MiscObjects.cpp index fd63509d0..cf6d66909 100644 --- a/lib/mapObjects/MiscObjects.cpp +++ b/lib/mapObjects/MiscObjects.cpp @@ -201,7 +201,7 @@ void CGCreature::onHeroVisit( const CGHeroInstance * h ) const } -void CGCreature::initObj() +void CGCreature::initObj(CRandomGenerator & rand) { blockVisit = true; switch(character) @@ -210,13 +210,13 @@ void CGCreature::initObj() character = -4; break; case 1: - character = cb->gameState()->getRandomGenerator().nextInt(1, 7); + character = rand.nextInt(1, 7); break; case 2: - character = cb->gameState()->getRandomGenerator().nextInt(1, 10); + character = rand.nextInt(1, 10); break; case 3: - character = cb->gameState()->getRandomGenerator().nextInt(4, 10); + character = rand.nextInt(4, 10); break; case 4: character = 10; @@ -228,7 +228,7 @@ void CGCreature::initObj() CCreature &c = *VLC->creh->creatures[subID]; if(amount == 0) { - amount = cb->gameState()->getRandomGenerator().nextInt(c.ammMin, c.ammMax); + amount = rand.nextInt(c.ammMin, c.ammMax); if(amount == 0) //armies with 0 creatures are illegal { @@ -241,7 +241,7 @@ void CGCreature::initObj() refusedJoining = false; } -void CGCreature::newTurn() const +void CGCreature::newTurn(CRandomGenerator & rand) const {//Works only for stacks of single type of size up to 2 millions if (!notGrowingTeam) { @@ -434,7 +434,7 @@ void CGCreature::fight( const CGHeroInstance *h ) const const auto & upgrades = getStack(slotID).type->upgrades; if(!upgrades.empty()) { - auto it = RandomGeneratorUtil::nextItem(upgrades, cb->gameState()->getRandomGenerator()); + auto it = RandomGeneratorUtil::nextItem(upgrades, CRandomGenerator::getDefault()); cb->changeStackType(StackLocation(this, slotID), VLC->creh->creatures[*it]); } } @@ -676,7 +676,7 @@ void CGMine::onHeroVisit( const CGHeroInstance * h ) const } -void CGMine::newTurn() const +void CGMine::newTurn(CRandomGenerator & rand) const { if(cb->getDate() == 1) return; @@ -687,12 +687,12 @@ void CGMine::newTurn() const cb->giveResource(tempOwner, producedResource, producedQuantity); } -void CGMine::initObj() +void CGMine::initObj(CRandomGenerator & rand) { if(isAbandoned()) { //set guardians - int howManyTroglodytes = cb->gameState()->getRandomGenerator().nextInt(100, 199); + int howManyTroglodytes = rand.nextInt(100, 199); auto troglodytes = new CStackInstance(CreatureID::TROGLODYTES, howManyTroglodytes); putStack(SlotID(0), troglodytes); @@ -703,7 +703,7 @@ void CGMine::initObj() possibleResources.push_back(static_cast(i)); assert(!possibleResources.empty()); - producedResource = *RandomGeneratorUtil::nextItem(possibleResources, cb->gameState()->getRandomGenerator()); + producedResource = *RandomGeneratorUtil::nextItem(possibleResources, rand); tempOwner = PlayerColor::NEUTRAL; } else @@ -859,7 +859,7 @@ CGResource::CGResource() amount = 0; } -void CGResource::initObj() +void CGResource::initObj(CRandomGenerator & rand) { blockVisit = true; @@ -868,13 +868,13 @@ void CGResource::initObj() switch(subID) { case 6: - amount = cb->gameState()->getRandomGenerator().nextInt(500, 1000); + amount = rand.nextInt(500, 1000); break; case 0: case 2: - amount = cb->gameState()->getRandomGenerator().nextInt(6, 10); + amount = rand.nextInt(6, 10); break; default: - amount = cb->gameState()->getRandomGenerator().nextInt(3, 5); + amount = rand.nextInt(3, 5); break; } } @@ -987,7 +987,7 @@ ObjectInstanceID CGTeleport::getRandomExit(const CGHeroInstance * h) const { auto passableExits = getPassableExits(cb->gameState(), h, getAllExits(true)); if(passableExits.size()) - return *RandomGeneratorUtil::nextItem(passableExits, cb->gameState()->getRandomGenerator()); + return *RandomGeneratorUtil::nextItem(passableExits, CRandomGenerator::getDefault()); return ObjectInstanceID(); } @@ -1119,7 +1119,7 @@ void CGMonolith::teleportDialogAnswered(const CGHeroInstance *hero, ui32 answer, cb->moveHero(hero->id, dPos, true); } -void CGMonolith::initObj() +void CGMonolith::initObj(CRandomGenerator & rand) { std::vector IDs; IDs.push_back(ID); @@ -1164,7 +1164,7 @@ void CGSubterraneanGate::onHeroVisit( const CGHeroInstance * h ) const cb->showTeleportDialog(&td); } -void CGSubterraneanGate::initObj() +void CGSubterraneanGate::initObj(CRandomGenerator & rand) { type = BOTH; } @@ -1283,7 +1283,7 @@ void CGWhirlpool::teleportDialogAnswered(const CGHeroInstance *hero, ui32 answer { auto obj = cb->getObj(getRandomExit(hero)); std::set tiles = obj->getBlockedPos(); - dPos = CGHeroInstance::convertPosition(*RandomGeneratorUtil::nextItem(tiles, cb->gameState()->getRandomGenerator()), true); + dPos = CGHeroInstance::convertPosition(*RandomGeneratorUtil::nextItem(tiles, CRandomGenerator::getDefault()), true); } cb->moveHero(hero->id, dPos, true); @@ -1299,7 +1299,7 @@ bool CGWhirlpool::isProtected(const CGHeroInstance * h) return false; } -void CGArtifact::initObj() +void CGArtifact::initObj(CRandomGenerator & rand) { blockVisit = true; if(ID == Obj::ARTIFACT) @@ -1441,14 +1441,14 @@ void CGArtifact::serializeJsonOptions(JsonSerializeFormat& handler) } } -void CGWitchHut::initObj() +void CGWitchHut::initObj(CRandomGenerator & rand) { if (allowedAbilities.empty()) //this can happen for RMG. regular maps load abilities from map file { for (int i = 0; i < GameConstants::SKILL_QUANTITY; i++) allowedAbilities.push_back(i); } - ability = *RandomGeneratorUtil::nextItem(allowedAbilities, cb->gameState()->getRandomGenerator()); + ability = *RandomGeneratorUtil::nextItem(allowedAbilities, rand); } void CGWitchHut::onHeroVisit( const CGHeroInstance * h ) const @@ -1627,7 +1627,7 @@ void CGShrine::onHeroVisit( const CGHeroInstance * h ) const cb->showInfoDialog(&iw); } -void CGShrine::initObj() +void CGShrine::initObj(CRandomGenerator & rand) { if(spell == SpellID::NONE) //spell not set { @@ -1641,7 +1641,7 @@ void CGShrine::initObj() return; } - spell = *RandomGeneratorUtil::nextItem(possibilities, cb->gameState()->getRandomGenerator()); + spell = *RandomGeneratorUtil::nextItem(possibilities, rand); } } @@ -1669,12 +1669,12 @@ void CGShrine::serializeJsonOptions(JsonSerializeFormat& handler) handler.serializeId("spell", &CSpellHandler::decodeSpell, &CSpellHandler::encodeSpell, SpellID(SpellID::NONE), spell); } -void CGSignBottle::initObj() +void CGSignBottle::initObj(CRandomGenerator & rand) { //if no text is set than we pick random from the predefined ones if(message.empty()) { - message = *RandomGeneratorUtil::nextItem(VLC->generaltexth->randsign, cb->gameState()->getRandomGenerator()); + message = *RandomGeneratorUtil::nextItem(VLC->generaltexth->randsign, rand); } if(ID == Obj::OCEAN_BOTTLE) @@ -1714,7 +1714,7 @@ void CGScholar::onHeroVisit( const CGHeroInstance * h ) const ))) //hero doesn't have a spellbook or already knows the spell or doesn't have Wisdom { type = PRIM_SKILL; - bid = cb->gameState()->getRandomGenerator().nextInt(GameConstants::PRIMARY_SKILLS - 1); + bid = CRandomGenerator::getDefault().nextInt(GameConstants::PRIMARY_SKILLS - 1); } InfoWindow iw; @@ -1749,25 +1749,25 @@ void CGScholar::onHeroVisit( const CGHeroInstance * h ) const cb->removeObject(this); } -void CGScholar::initObj() +void CGScholar::initObj(CRandomGenerator & rand) { blockVisit = true; if(bonusType == RANDOM) { - bonusType = static_cast(cb->gameState()->getRandomGenerator().nextInt(2)); + bonusType = static_cast(rand.nextInt(2)); switch(bonusType) { case PRIM_SKILL: - bonusID = cb->gameState()->getRandomGenerator().nextInt(GameConstants::PRIMARY_SKILLS -1); + bonusID = rand.nextInt(GameConstants::PRIMARY_SKILLS -1); break; case SECONDARY_SKILL: - bonusID = cb->gameState()->getRandomGenerator().nextInt(GameConstants::SKILL_QUANTITY -1); + bonusID = rand.nextInt(GameConstants::SKILL_QUANTITY -1); break; case SPELL: std::vector possibilities; for (int i = 1; i < 6; ++i) cb->getAllowedSpells (possibilities, i); - bonusID = *RandomGeneratorUtil::nextItem(possibilities, cb->gameState()->getRandomGenerator()); + bonusID = *RandomGeneratorUtil::nextItem(possibilities, rand); break; } } @@ -1874,7 +1874,7 @@ void CGMagi::reset() eyelist.clear(); } -void CGMagi::initObj() +void CGMagi::initObj(CRandomGenerator & rand) { if (ID == Obj::EYE_OF_MAGI) { @@ -1920,12 +1920,12 @@ void CGMagi::onHeroVisit(const CGHeroInstance * h) const } } -void CGBoat::initObj() +void CGBoat::initObj(CRandomGenerator & rand) { hero = nullptr; } -void CGSirens::initObj() +void CGSirens::initObj(CRandomGenerator & rand) { blockVisit = true; } @@ -2111,7 +2111,7 @@ void CGObelisk::onHeroVisit( const CGHeroInstance * h ) const } -void CGObelisk::initObj() +void CGObelisk::initObj(CRandomGenerator & rand) { obeliskCount++; } @@ -2172,7 +2172,7 @@ void CGLighthouse::onHeroVisit( const CGHeroInstance * h ) const } } -void CGLighthouse::initObj() +void CGLighthouse::initObj(CRandomGenerator & rand) { if(tempOwner < PlayerColor::PLAYER_LIMIT) { diff --git a/lib/mapObjects/MiscObjects.h b/lib/mapObjects/MiscObjects.h index 361d95b44..eb571faf4 100644 --- a/lib/mapObjects/MiscObjects.h +++ b/lib/mapObjects/MiscObjects.h @@ -59,8 +59,8 @@ public: void onHeroVisit(const CGHeroInstance * h) const override; std::string getHoverText(PlayerColor player) const override; std::string getHoverText(const CGHeroInstance * hero) const override; - void initObj() override; - void newTurn() const override; + void initObj(CRandomGenerator & rand) override; + void newTurn(CRandomGenerator & rand) const override; void battleFinished(const CGHeroInstance *hero, const BattleResult &result) const override; void blockingDialogAnswered(const CGHeroInstance *hero, ui32 answer) const override; @@ -105,7 +105,7 @@ public: std::string message; void onHeroVisit(const CGHeroInstance * h) const override; - void initObj() override; + void initObj(CRandomGenerator & rand) override; template void serialize(Handler &h, const int version) { @@ -125,7 +125,7 @@ public: std::string getHoverText(PlayerColor player) const override; std::string getHoverText(const CGHeroInstance * hero) const override; void onHeroVisit(const CGHeroInstance * h) const override; - void initObj() override; + void initObj(CRandomGenerator & rand) override; template void serialize(Handler &h, const int version) { h & static_cast(*this); @@ -144,7 +144,7 @@ public: CGScholar() : bonusType(EBonusType::RANDOM){}; void onHeroVisit(const CGHeroInstance * h) const override; - void initObj() override; + void initObj(CRandomGenerator & rand) override; template void serialize(Handler &h, const int version) { h & static_cast(*this); @@ -187,7 +187,7 @@ public: std::string getObjectName() const override; void pick( const CGHeroInstance * h ) const; - void initObj() override; + void initObj(CRandomGenerator & rand) override; template void serialize(Handler &h, const int version) { @@ -206,7 +206,7 @@ public: CGResource(); void onHeroVisit(const CGHeroInstance * h) const override; - void initObj() override; + void initObj(CRandomGenerator & rand) override; void battleFinished(const CGHeroInstance *hero, const BattleResult &result) const override; void blockingDialogAnswered(const CGHeroInstance *hero, ui32 answer) const override; std::string getHoverText(PlayerColor player) const override; @@ -227,7 +227,7 @@ class DLL_LINKAGE CGShrine : public CPlayersVisited public: SpellID spell; //id of spell or NONE if random void onHeroVisit(const CGHeroInstance * h) const override; - void initObj() override; + void initObj(CRandomGenerator & rand) override; std::string getHoverText(PlayerColor player) const override; std::string getHoverText(const CGHeroInstance * hero) const override; @@ -252,8 +252,8 @@ private: void blockingDialogAnswered(const CGHeroInstance *hero, ui32 answer) const override; void flagMine(PlayerColor player) const; - void newTurn() const override; - void initObj() override; + void newTurn(CRandomGenerator & rand) const override; + void initObj(CRandomGenerator & rand) override; std::string getObjectName() const override; std::string getHoverText(PlayerColor player) const override; @@ -329,7 +329,7 @@ class DLL_LINKAGE CGMonolith : public CGTeleport protected: void onHeroVisit(const CGHeroInstance * h) const override; void teleportDialogAnswered(const CGHeroInstance *hero, ui32 answer, TTeleportExitsList exits) const override; - void initObj() override; + void initObj(CRandomGenerator & rand) override; public: template void serialize(Handler &h, const int version) @@ -341,7 +341,7 @@ public: class DLL_LINKAGE CGSubterraneanGate : public CGMonolith { void onHeroVisit(const CGHeroInstance * h) const override; - void initObj() override; + void initObj(CRandomGenerator & rand) override; public: static void postInit(); @@ -382,7 +382,7 @@ class DLL_LINKAGE CGSirens : public CGObjectInstance public: void onHeroVisit(const CGHeroInstance * h) const override; std::string getHoverText(const CGHeroInstance * hero) const override; - void initObj() override; + void initObj(CRandomGenerator & rand) override; template void serialize(Handler &h, const int version) { @@ -407,7 +407,7 @@ public: ui8 direction; const CGHeroInstance *hero; //hero on board - void initObj() override; + void initObj(CRandomGenerator & rand) override; CGBoat() { @@ -443,7 +443,7 @@ public: static void reset(); - void initObj() override; + void initObj(CRandomGenerator & rand) override; void onHeroVisit(const CGHeroInstance * h) const override; template void serialize(Handler &h, const int version) @@ -478,7 +478,7 @@ public: static std::map visited; //map: team_id => how many obelisks has been visited void onHeroVisit(const CGHeroInstance * h) const override; - void initObj() override; + void initObj(CRandomGenerator & rand) override; std::string getHoverText(PlayerColor player) const override; static void reset(); @@ -494,7 +494,7 @@ class DLL_LINKAGE CGLighthouse : public CGObjectInstance { public: void onHeroVisit(const CGHeroInstance * h) const override; - void initObj() override; + void initObj(CRandomGenerator & rand) override; std::string getHoverText(PlayerColor player) const override; template void serialize(Handler &h, const int version) diff --git a/lib/registerTypes/RegisterTypes.h b/lib/registerTypes/RegisterTypes.h index b096fc9d9..b1982bb9a 100644 --- a/lib/registerTypes/RegisterTypes.h +++ b/lib/registerTypes/RegisterTypes.h @@ -253,6 +253,7 @@ void registerTypesClientPacks1(Serializer &s) s.template registerType(); s.template registerType(); s.template registerType(); + s.template registerType(); } template diff --git a/lib/spells/BattleSpellMechanics.cpp b/lib/spells/BattleSpellMechanics.cpp index 1958d3654..83c65bf07 100644 --- a/lib/spells/BattleSpellMechanics.cpp +++ b/lib/spells/BattleSpellMechanics.cpp @@ -428,7 +428,7 @@ void ObstacleMechanics::applyBattleEffects(const SpellCastEnvironment * env, con if(hex.getX() > 2 && hex.getX() < 14 && !(parameters.cb->battleGetStackByPos(hex, false)) && !(parameters.cb->battleGetObstacleOnPos(hex, false))) availableTiles.push_back(hex); } - boost::range::random_shuffle(availableTiles); + RandomGeneratorUtil::randomShuffle(availableTiles, env->getRandomGenerator()); const int patchesForSkill[] = {4, 4, 6, 8}; const int patchesToPut = std::min(patchesForSkill[parameters.spellLvl], availableTiles.size()); diff --git a/server/CGameHandler.cpp b/server/CGameHandler.cpp index ac23ccba6..ec8643a8e 100644 --- a/server/CGameHandler.cpp +++ b/server/CGameHandler.cpp @@ -204,7 +204,7 @@ void CGameHandler::levelUpHero(const CGHeroInstance * hero) // give primary skill logGlobal->trace("%s got level %d", hero->name, hero->level); - auto primarySkill = hero->nextPrimarySkill(); + auto primarySkill = hero->nextPrimarySkill(getRandomGenerator()); SetPrimSkill sps; sps.id = hero->id; @@ -213,20 +213,24 @@ void CGameHandler::levelUpHero(const CGHeroInstance * hero) sps.val = 1; sendAndApply(&sps); + PrepareHeroLevelUp pre; + pre.hero = hero; + sendAndApply(&pre); + HeroLevelUp hlu; hlu.hero = hero; hlu.primskill = primarySkill; - hlu.skills = hero->getLevelUpProposedSecondarySkills(); + hlu.skills = pre.skills; if(hlu.skills.size() == 0) { sendAndApply(&hlu); levelUpHero(hero); } - else if(hlu.skills.size() == 1 || hero->tempOwner == PlayerColor::NEUTRAL) //choose skill automatically + else if(hlu.skills.size() == 1) { sendAndApply(&hlu); - levelUpHero(hero, *RandomGeneratorUtil::nextItem(hlu.skills, hero->skillsInfo.rand)); + levelUpHero(hero, pre.skills.front()); } else if(hlu.skills.size() > 1) { @@ -364,7 +368,7 @@ void CGameHandler::levelUpCommander(const CCommanderInstance * c) else if(skillAmount == 1 || hero->tempOwner == PlayerColor::NEUTRAL) //choose skill automatically { sendAndApply(&clu); - levelUpCommander(c, *RandomGeneratorUtil::nextItem(clu.skills, gs->getRandomGenerator())); + levelUpCommander(c, *RandomGeneratorUtil::nextItem(clu.skills, getRandomGenerator())); } else if(skillAmount > 1) //apply and ask for secondary skill { @@ -525,7 +529,7 @@ void CGameHandler::endBattle(int3 tile, const CGHeroInstance *hero1, const CGHer int maxLevel = eagleEyeLevel + 1; double eagleEyeChance = finishingBattle->winnerHero->valOfBonuses(Bonus::SECONDARY_SKILL_PREMY, SecondarySkill::EAGLE_EYE); for(const CSpell *sp : gs->curB->sides.at(!battleResult.data->winner).usedSpellsHistory) - if(sp->level <= maxLevel && !vstd::contains(finishingBattle->winnerHero->spells, sp->id) && gs->getRandomGenerator().nextInt(99) < eagleEyeChance) + if(sp->level <= maxLevel && !vstd::contains(finishingBattle->winnerHero->spells, sp->id) && getRandomGenerator().nextInt(99) < eagleEyeChance) cs.spells.insert(sp->id); } } @@ -716,7 +720,7 @@ void CGameHandler::battleAfterLevelUp( const BattleResult &result ) if (necroSlot != SlotID()) { - finishingBattle->winnerHero->showNecromancyDialog(raisedStack); + finishingBattle->winnerHero->showNecromancyDialog(raisedStack, getRandomGenerator()); addToSlot(StackLocation(finishingBattle->winnerHero, necroSlot), raisedStack.type, raisedStack.count); } @@ -789,20 +793,20 @@ void CGameHandler::prepareAttack(BattleAttack &bat, const CStack *att, const CSt if(!vstd::contains_if(gs->curB->sides, sideHeroBlocksLuck)) { - if(attackerLuck > 0 && gs->getRandomGenerator().nextInt(23) < attackerLuck) + if(attackerLuck > 0 && getRandomGenerator().nextInt(23) < attackerLuck) { bat.flags |= BattleAttack::LUCKY; } if (VLC->modh->settings.data["hardcodedFeatures"]["NEGATIVE_LUCK"].Bool()) // negative luck enabled { - if (attackerLuck < 0 && gs->getRandomGenerator().nextInt(23) < abs(attackerLuck)) + if (attackerLuck < 0 && getRandomGenerator().nextInt(23) < abs(attackerLuck)) { bat.flags |= BattleAttack::UNLUCKY; } } } - if(gs->getRandomGenerator().nextInt(99) < att->valOfBonuses(Bonus::DOUBLE_DAMAGE_CHANCE)) + if(getRandomGenerator().nextInt(99) < att->valOfBonuses(Bonus::DOUBLE_DAMAGE_CHANCE)) { bat.flags |= BattleAttack::DEATH_BLOW; } @@ -812,7 +816,7 @@ void CGameHandler::prepareAttack(BattleAttack &bat, const CStack *att, const CSt static const int artilleryLvlToChance[] = {0, 50, 75, 100}; const CGHeroInstance * owner = gs->curB->getHero(att->owner); int chance = artilleryLvlToChance[owner->getSecSkillLevel(SecondarySkill::ARTILLERY)]; - if(chance > gs->getRandomGenerator().nextInt(99)) + if(chance > getRandomGenerator().nextInt(99)) { bat.flags |= BattleAttack::BALLISTA_DOUBLE_DMG; } @@ -875,8 +879,8 @@ void CGameHandler::applyBattleEffects(BattleAttack &bat, const CStack *att, cons bsa.attackerID = att->ID; bsa.stackAttacked = def->ID; bsa.damageAmount = gs->curB->calculateDmg(att, def, gs->curB->battleGetOwner(att), gs->curB->battleGetOwner(def), - bat.shot(), distance, bat.lucky(), bat.unlucky(), bat.deathBlow(), bat.ballistaDoubleDmg(), gs->getRandomGenerator()); - def->prepareAttacked(bsa, gs->getRandomGenerator()); //calculate casualties + bat.shot(), distance, bat.lucky(), bat.unlucky(), bat.deathBlow(), bat.ballistaDoubleDmg(), getRandomGenerator()); + def->prepareAttacked(bsa, getRandomGenerator()); //calculate casualties //life drain handling if (att->hasBonusOfType(Bonus::LIFE_DRAIN) && def->isLiving()) @@ -913,7 +917,7 @@ void CGameHandler::applyBattleEffects(BattleAttack &bat, const CStack *att, cons bsa2.effect = 11; bsa2.damageAmount = (std::min(def->totalHelth(), bsa.damageAmount) * def->valOfBonuses(Bonus::FIRE_SHIELD)) / 100; //TODO: scale with attack/defense - att->prepareAttacked(bsa2, gameState()->getRandomGenerator()); + att->prepareAttacked(bsa2, getRandomGenerator()); bat.bsa.push_back(bsa2); } } @@ -1337,7 +1341,7 @@ void CGameHandler::init(StartInfo *si) logGlobal->info("Gamestate initialized!"); // reset seed, so that clients can't predict any following random values - gs->getRandomGenerator().resetSeed(); + getRandomGenerator().resetSeed(); for(auto & elem : gs->players) { @@ -1373,10 +1377,10 @@ void CGameHandler::setPortalDwelling(const CGTownInstance * town, bool forced=fa return; } - auto dwelling = *RandomGeneratorUtil::nextItem(dwellings, gs->getRandomGenerator()); + auto dwelling = *RandomGeneratorUtil::nextItem(dwellings, getRandomGenerator()); // for multi-creature dwellings like Golem Factory - auto creatureId = RandomGeneratorUtil::nextItem(dwelling->creatures, gs->getRandomGenerator())->second[0]; + auto creatureId = RandomGeneratorUtil::nextItem(dwelling->creatures, getRandomGenerator())->second[0]; if(clear) { @@ -1436,7 +1440,7 @@ void CGameHandler::newTurn() } else { - int monthType = gs->getRandomGenerator().nextInt(99); + int monthType = getRandomGenerator().nextInt(99); if(newMonth) //new month { if (monthType < 40) //double growth @@ -1444,13 +1448,13 @@ void CGameHandler::newTurn() n.specialWeek = NewTurn::DOUBLE_GROWTH; if (VLC->modh->settings.ALL_CREATURES_GET_DOUBLE_MONTHS) { - std::pair newMonster(54, VLC->creh->pickRandomMonster(gs->getRandomGenerator())); + std::pair newMonster(54, VLC->creh->pickRandomMonster(getRandomGenerator())); n.creatureid = newMonster.second; } else if(VLC->creh->doubledCreatures.size()) { const std::vector doubledCreatures (VLC->creh->doubledCreatures.begin(), VLC->creh->doubledCreatures.end()); - n.creatureid = *RandomGeneratorUtil::nextItem(doubledCreatures, gs->getRandomGenerator()); + n.creatureid = *RandomGeneratorUtil::nextItem(doubledCreatures, getRandomGenerator()); } else { @@ -1466,7 +1470,7 @@ void CGameHandler::newTurn() if (monthType < 25) { n.specialWeek = NewTurn::BONUS_GROWTH; //+5 - std::pair newMonster(54, VLC->creh->pickRandomMonster(gs->getRandomGenerator())); + std::pair newMonster(54, VLC->creh->pickRandomMonster(getRandomGenerator())); //TODO do not pick neutrals n.creatureid = newMonster.second; } @@ -1515,10 +1519,10 @@ void CGameHandler::newTurn() for (int j = 0; j < GameConstants::AVAILABLE_HEROES_PER_PLAYER; j++) { //first hero - native if possible, second hero -> any other class - if(CGHeroInstance *h = gs->hpool.pickHeroFor(j == 0, elem.first, getNativeTown(elem.first), pool, gs->getRandomGenerator(), banned)) + if(CGHeroInstance *h = gs->hpool.pickHeroFor(j == 0, elem.first, getNativeTown(elem.first), pool, getRandomGenerator(), banned)) { sah.hid[j] = h->subID; - h->initArmy(&sah.army[j]); + h->initArmy(getRandomGenerator(), &sah.army[j]); banned = h->type->heroClass; } else @@ -1647,7 +1651,7 @@ void CGameHandler::newTurn() { SetAvailableArtifacts saa; saa.id = -1; - pickAllowedArtsSet(saa.arts); + pickAllowedArtsSet(saa.arts, getRandomGenerator()); sendAndApply(&saa); } sendAndApply(&n); @@ -1691,12 +1695,12 @@ void CGameHandler::newTurn() if (newMonth) { iw.text.addTxt(MetaString::ARRAY_TXT, (130)); - iw.text.addReplacement(MetaString::ARRAY_TXT, gs->getRandomGenerator().nextInt(32, 41)); + iw.text.addReplacement(MetaString::ARRAY_TXT, getRandomGenerator().nextInt(32, 41)); } else { iw.text.addTxt(MetaString::ARRAY_TXT, (133)); - iw.text.addReplacement(MetaString::ARRAY_TXT, gs->getRandomGenerator().nextInt(43, 57)); + iw.text.addReplacement(MetaString::ARRAY_TXT, getRandomGenerator().nextInt(43, 57)); } } for (auto & elem : gs->players) @@ -1713,7 +1717,7 @@ void CGameHandler::newTurn() for(auto & elem : gs->map->objects) { if(elem) - elem->newTurn(); + elem->newTurn(getRandomGenerator()); } synchronizeArtifactHandlerLists(); //new day events may have changed them. TODO better of managing that @@ -1859,9 +1863,18 @@ void CGameHandler::setupBattle( int3 tile, const CArmedInstance *armies[2], cons { battleResult.set(nullptr); + const auto t = gs->getTile(tile); + ETerrainType terrain = t->terType; + if(gs->map->isCoastalTile(tile)) //coastal tile is always ground + terrain = ETerrainType::SAND; + + BFieldType terType = gs->battleGetBattlefieldType(tile, getRandomGenerator()); + if (heroes[0] && heroes[0]->boat && heroes[1] && heroes[1]->boat) + terType = BFieldType::SHIP_TO_SHIP; + //send info about battles BattleStart bs; - bs.info = gs->setupBattle(tile, armies, heroes, creatureBank, town); + bs.info = BattleInfo::setupBattle(tile, terrain, terType, armies, heroes, creatureBank, town); sendAndApply(&bs); } @@ -3569,7 +3582,7 @@ bool CGameHandler::hireHero(const CGObjectInstance *obj, ui8 hid, PlayerColor pl const CGHeroInstance *newHero = nullptr; if (theOtherHero) //on XXL maps all heroes can be imprisoned :( { - newHero = gs->hpool.pickHeroFor(false, player, getNativeTown(player), pool, gs->getRandomGenerator(), theOtherHero->type->heroClass); + newHero = gs->hpool.pickHeroFor(false, player, getNativeTown(player), pool, getRandomGenerator(), theOtherHero->type->heroClass); } SetAvailableHeroes sah; @@ -3978,7 +3991,7 @@ bool CGameHandler::makeBattleAction( BattleAction &ba ) { if(currentHP.at(attackedPart) != EWallState::DESTROYED && // this part can be hit currentHP.at(attackedPart) != EWallState::NONE && - gs->getRandomGenerator().nextInt(99) < getCatapultHitChance(attackedPart, sbi))//hit is successful + getRandomGenerator().nextInt(99) < getCatapultHitChance(attackedPart, sbi))//hit is successful { hitSuccessfull = true; } @@ -3993,7 +4006,7 @@ bool CGameHandler::makeBattleAction( BattleAction &ba ) } if (allowedTargets.empty()) break; - attackedPart = *RandomGeneratorUtil::nextItem(allowedTargets, gs->getRandomGenerator()); + attackedPart = *RandomGeneratorUtil::nextItem(allowedTargets, getRandomGenerator()); } } while (!hitSuccessfull); @@ -4009,7 +4022,7 @@ bool CGameHandler::makeBattleAction( BattleAction &ba ) int dmgChance[] = { sbi.noDmg, sbi.oneDmg, sbi.twoDmg }; //dmgChance[i] - chance for doing i dmg when hit is successful - int dmgRand = gs->getRandomGenerator().nextInt(99); + int dmgRand = getRandomGenerator().nextInt(99); //accumulating dmgChance dmgChance[1] += dmgChance[0]; dmgChance[2] += dmgChance[1]; @@ -4164,8 +4177,8 @@ bool CGameHandler::makeBattleAction( BattleAction &ba ) const Bonus * spellcaster = stack->getBonusLocalFirst(Selector::typeSubtype(Bonus::SPELLCASTER, spellID)); //TODO special bonus for genies ability - if(randSpellcaster && battleGetRandomStackSpell(stack, CBattleInfoCallback::RANDOM_AIMED) < 0) - spellID = battleGetRandomStackSpell(stack, CBattleInfoCallback::RANDOM_GENIE); + if(randSpellcaster && battleGetRandomStackSpell(getRandomGenerator(), stack, CBattleInfoCallback::RANDOM_AIMED) < 0) + spellID = battleGetRandomStackSpell(getRandomGenerator(), stack, CBattleInfoCallback::RANDOM_GENIE); if(spellID < 0) complain("That stack can't cast spells!"); @@ -4507,7 +4520,7 @@ void CGameHandler::stackTurnTrigger(const CStack * st) } if (fearsomeCreature) { - if (gs->getRandomGenerator().nextInt(99) < 10) //fixed 10% + if (getRandomGenerator().nextInt(99) < 10) //fixed 10% { bte.effect = Bonus::FEAR; sendAndApply(&bte); @@ -4521,7 +4534,7 @@ void CGameHandler::stackTurnTrigger(const CStack * st) bool cast = false; while (!bl.empty() && !cast) { - auto bonus = *RandomGeneratorUtil::nextItem(bl, gs->getRandomGenerator()); + auto bonus = *RandomGeneratorUtil::nextItem(bl, getRandomGenerator()); auto spellID = SpellID(bonus->subtype); const CSpell * spell = SpellID(spellID).toSpell(); bl.remove_if([&bonus](Bonus * b){return b==bonus;}); @@ -4636,7 +4649,7 @@ void CGameHandler::handleDamageFromObstacle(const CObstacleInstance &obstacle, c bsa.damageAmount = damage; bsa.stackAttacked = curStack->ID; bsa.attackerID = -1; - curStack->prepareAttacked(bsa, gameState()->getRandomGenerator()); + curStack->prepareAttacked(bsa, getRandomGenerator()); StacksInjured si; si.stacks.push_back(bsa); @@ -5233,7 +5246,7 @@ void CGameHandler::attackCasting(const BattleAttack & bat, Bonus::BonusType atta continue; //check if spell should be cast (probability handling) - if(gs->getRandomGenerator().nextInt(99) >= chance) + if(getRandomGenerator().nextInt(99) >= chance) continue; //casting @@ -5317,7 +5330,7 @@ void CGameHandler::handleAfterAttackCasting( const BattleAttack & bat ) TBonusListPtr acidBreath = attacker->getBonuses(Selector::type(Bonus::ACID_BREATH)); for(const Bonus *b : *acidBreath) { - if (b->additionalInfo > gs->getRandomGenerator().nextInt(99)) + if (b->additionalInfo > getRandomGenerator().nextInt(99)) acidDamage += b->val; } if (acidDamage) @@ -5629,7 +5642,7 @@ void CGameHandler::runBattle() || NBonus::hasOfType(gs->curB->battleGetFightingHero(1), Bonus::BLOCK_MORALE)) //checking if gs->curB->heroes have (or don't have) morale blocking bonuses) ) { - if(gs->getRandomGenerator().nextInt(23) < -2 * nextStackMorale) + if(getRandomGenerator().nextInt(23) < -2 * nextStackMorale) { //unit loses its turn - empty freeze action BattleAction ba; @@ -5701,7 +5714,7 @@ void CGameHandler::runBattle() { BattleAction attack; attack.destinationTile = *RandomGeneratorUtil::nextItem(attackableBattleHexes, - gs->getRandomGenerator()); + getRandomGenerator()); attack.actionType = Battle::CATAPULT; attack.additionalInfo = 0; attack.side = !next->attackerOwned; @@ -5727,7 +5740,7 @@ void CGameHandler::runBattle() if(!curOwner || curOwner->getSecSkillLevel(SecondarySkill::FIRST_AID) == 0) //no hero or hero has no first aid { - range::random_shuffle(possibleStacks); + RandomGeneratorUtil::randomShuffle(possibleStacks, getRandomGenerator()); const CStack * toBeHealed = possibleStacks.front(); BattleAction heal; @@ -5806,7 +5819,7 @@ void CGameHandler::runBattle() || NBonus::hasOfType(gs->curB->battleGetFightingHero(1), Bonus::BLOCK_MORALE)) //checking if gs->curB->heroes have (or don't have) morale blocking bonuses ) { - if(gs->getRandomGenerator().nextInt(23) < nextStackMorale) //this stack hasn't got morale this turn + if(getRandomGenerator().nextInt(23) < nextStackMorale) //this stack hasn't got morale this turn { BattleTriggerEffect bte; @@ -5930,7 +5943,8 @@ void CGameHandler::spawnWanderingMonsters(CreatureID creatureID) std::vector tiles; getFreeTiles(tiles); ui32 amount = tiles.size() / 200; //Chance is 0.5% for each tile - std::random_shuffle(tiles.begin(), tiles.end()); + + RandomGeneratorUtil::randomShuffle(tiles, getRandomGenerator()); logGlobal->trace("Spawning wandering monsters. Found %d free tiles. Creature type: %d", tiles.size(), creatureID.num); const CCreature *cre = VLC->creh->creatures.at(creatureID); for (int i = 0; i < amount; ++i) @@ -6234,6 +6248,11 @@ CGameHandler::FinishingBattleHelper::FinishingBattleHelper() winnerHero = loserHero = nullptr; } +CRandomGenerator & CGameHandler::getRandomGenerator() +{ + return CRandomGenerator::getDefault(); +} + ///ServerSpellCastEnvironment ServerSpellCastEnvironment::ServerSpellCastEnvironment(CGameHandler * gh): gh(gh) { @@ -6247,7 +6266,7 @@ void ServerSpellCastEnvironment::sendAndApply(CPackForClient * info) const CRandomGenerator & ServerSpellCastEnvironment::getRandomGenerator() const { - return gh->gameState()->getRandomGenerator(); + return gh->getRandomGenerator(); } void ServerSpellCastEnvironment::complain(const std::string& problem) const diff --git a/server/CGameHandler.h b/server/CGameHandler.h index 3681eb067..36ea8fd7a 100644 --- a/server/CGameHandler.h +++ b/server/CGameHandler.h @@ -247,6 +247,10 @@ public: template void serialize(Handler &h, const int version) { h & QID & states & finishingBattle; + if(version >= 761) + { + h & getRandomGenerator(); + } } void sendMessageToAll(const std::string &message); @@ -290,6 +294,8 @@ public: void spawnWanderingMonsters(CreatureID creatureID); friend class CVCMIServer; + CRandomGenerator & getRandomGenerator(); + private: ServerSpellCastEnvironment * spellEnv;