mirror of
https://github.com/OpenMW/openmw.git
synced 2025-06-02 01:11:32 +00:00
Merge branch 'refactor/prng-2' into 'master'
Save random state and refactor usage of generators See merge request OpenMW/openmw!1715
This commit is contained in:
commit
6d55317d57
51 changed files with 329 additions and 154 deletions
|
@ -9,6 +9,7 @@
|
||||||
#include <deque>
|
#include <deque>
|
||||||
|
|
||||||
#include <components/esm3/cellid.hpp>
|
#include <components/esm3/cellid.hpp>
|
||||||
|
#include <components/misc/rng.hpp>
|
||||||
|
|
||||||
#include <osg/Timer>
|
#include <osg/Timer>
|
||||||
|
|
||||||
|
@ -111,6 +112,9 @@ namespace MWBase
|
||||||
|
|
||||||
virtual ~World() {}
|
virtual ~World() {}
|
||||||
|
|
||||||
|
virtual void setRandomSeed(uint32_t seed) = 0;
|
||||||
|
///< \param seed The seed used when starting a new game.
|
||||||
|
|
||||||
virtual void startNewGame (bool bypass) = 0;
|
virtual void startNewGame (bool bypass) = 0;
|
||||||
///< \param bypass Bypass regular game start.
|
///< \param bypass Bypass regular game start.
|
||||||
|
|
||||||
|
@ -658,6 +662,8 @@ namespace MWBase
|
||||||
virtual void reportStats(unsigned int frameNumber, osg::Stats& stats) const = 0;
|
virtual void reportStats(unsigned int frameNumber, osg::Stats& stats) const = 0;
|
||||||
|
|
||||||
virtual std::vector<MWWorld::Ptr> getAll(const std::string& id) = 0;
|
virtual std::vector<MWWorld::Ptr> getAll(const std::string& id) = 0;
|
||||||
|
|
||||||
|
virtual Misc::Rng::Generator& getPrng() = 0;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -120,7 +120,8 @@ namespace MWClass
|
||||||
if(actor.getClass().isNpc() && actor.getClass().getNpcStats(actor).isWerewolf())
|
if(actor.getClass().isNpc() && actor.getClass().getNpcStats(actor).isWerewolf())
|
||||||
{
|
{
|
||||||
const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore();
|
const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore();
|
||||||
const ESM::Sound *sound = store.get<ESM::Sound>().searchRandom("WolfActivator");
|
auto& prng = MWBase::Environment::get().getWorld()->getPrng();
|
||||||
|
const ESM::Sound *sound = store.get<ESM::Sound>().searchRandom("WolfActivator", prng);
|
||||||
|
|
||||||
std::shared_ptr<MWWorld::Action> action(new MWWorld::FailedAction("#{sWerewolfRefusal}"));
|
std::shared_ptr<MWWorld::Action> action(new MWWorld::FailedAction("#{sWerewolfRefusal}"));
|
||||||
if(sound) action->setSound(sound->mId);
|
if(sound) action->setSound(sound->mId);
|
||||||
|
@ -156,6 +157,7 @@ namespace MWClass
|
||||||
int type = getSndGenTypeFromName(name);
|
int type = getSndGenTypeFromName(name);
|
||||||
|
|
||||||
std::vector<const ESM::SoundGenerator*> fallbacksounds;
|
std::vector<const ESM::SoundGenerator*> fallbacksounds;
|
||||||
|
auto& prng = MWBase::Environment::get().getWorld()->getPrng();
|
||||||
if (!creatureId.empty())
|
if (!creatureId.empty())
|
||||||
{
|
{
|
||||||
std::vector<const ESM::SoundGenerator*> sounds;
|
std::vector<const ESM::SoundGenerator*> sounds;
|
||||||
|
@ -168,9 +170,9 @@ namespace MWClass
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!sounds.empty())
|
if (!sounds.empty())
|
||||||
return sounds[Misc::Rng::rollDice(sounds.size())]->mSound;
|
return sounds[Misc::Rng::rollDice(sounds.size(), prng)]->mSound;
|
||||||
if (!fallbacksounds.empty())
|
if (!fallbacksounds.empty())
|
||||||
return fallbacksounds[Misc::Rng::rollDice(fallbacksounds.size())]->mSound;
|
return fallbacksounds[Misc::Rng::rollDice(fallbacksounds.size(), prng)]->mSound;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -180,7 +182,7 @@ namespace MWClass
|
||||||
fallbacksounds.push_back(&*sound);
|
fallbacksounds.push_back(&*sound);
|
||||||
|
|
||||||
if (!fallbacksounds.empty())
|
if (!fallbacksounds.empty())
|
||||||
return fallbacksounds[Misc::Rng::rollDice(fallbacksounds.size())]->mSound;
|
return fallbacksounds[Misc::Rng::rollDice(fallbacksounds.size(), prng)]->mSound;
|
||||||
}
|
}
|
||||||
|
|
||||||
return std::string();
|
return std::string();
|
||||||
|
|
|
@ -56,7 +56,8 @@ namespace MWClass
|
||||||
if(actor.getClass().isNpc() && actor.getClass().getNpcStats(actor).isWerewolf())
|
if(actor.getClass().isNpc() && actor.getClass().getNpcStats(actor).isWerewolf())
|
||||||
{
|
{
|
||||||
const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore();
|
const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore();
|
||||||
const ESM::Sound *sound = store.get<ESM::Sound>().searchRandom("WolfItem");
|
auto& prng = MWBase::Environment::get().getWorld()->getPrng();
|
||||||
|
const ESM::Sound *sound = store.get<ESM::Sound>().searchRandom("WolfItem", prng);
|
||||||
|
|
||||||
std::shared_ptr<MWWorld::Action> action(new MWWorld::FailedAction("#{sWerewolfRefusal}"));
|
std::shared_ptr<MWWorld::Action> action(new MWWorld::FailedAction("#{sWerewolfRefusal}"));
|
||||||
if(sound) action->setSound(sound->mId);
|
if(sound) action->setSound(sound->mId);
|
||||||
|
|
|
@ -32,7 +32,8 @@ namespace MWClass
|
||||||
{
|
{
|
||||||
ContainerCustomData::ContainerCustomData(const ESM::Container& container, MWWorld::CellStore* cell)
|
ContainerCustomData::ContainerCustomData(const ESM::Container& container, MWWorld::CellStore* cell)
|
||||||
{
|
{
|
||||||
unsigned int seed = Misc::Rng::rollDice(std::numeric_limits<int>::max());
|
auto& prng = MWBase::Environment::get().getWorld()->getPrng();
|
||||||
|
unsigned int seed = Misc::Rng::rollDice(std::numeric_limits<int>::max(), prng);
|
||||||
// setting ownership not needed, since taking items from a container inherits the
|
// setting ownership not needed, since taking items from a container inherits the
|
||||||
// container's owner automatically
|
// container's owner automatically
|
||||||
mStore.fillNonRandom(container.mInventory, "", seed);
|
mStore.fillNonRandom(container.mInventory, "", seed);
|
||||||
|
@ -139,7 +140,8 @@ namespace MWClass
|
||||||
if(actor.getClass().isNpc() && actor.getClass().getNpcStats(actor).isWerewolf())
|
if(actor.getClass().isNpc() && actor.getClass().getNpcStats(actor).isWerewolf())
|
||||||
{
|
{
|
||||||
const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore();
|
const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore();
|
||||||
const ESM::Sound *sound = store.get<ESM::Sound>().searchRandom("WolfContainer");
|
auto& prng = MWBase::Environment::get().getWorld()->getPrng();
|
||||||
|
const ESM::Sound *sound = store.get<ESM::Sound>().searchRandom("WolfContainer", prng);
|
||||||
|
|
||||||
std::shared_ptr<MWWorld::Action> action(new MWWorld::FailedAction("#{sWerewolfRefusal}"));
|
std::shared_ptr<MWWorld::Action> action(new MWWorld::FailedAction("#{sWerewolfRefusal}"));
|
||||||
if(sound) action->setSound(sound->mId);
|
if(sound) action->setSound(sound->mId);
|
||||||
|
|
|
@ -159,7 +159,8 @@ namespace MWClass
|
||||||
|
|
||||||
resetter.mPtr = {};
|
resetter.mPtr = {};
|
||||||
|
|
||||||
getContainerStore(ptr).fill(ref->mBase->mInventory, ptr.getCellRef().getRefId());
|
auto& prng = MWBase::Environment::get().getWorld()->getPrng();
|
||||||
|
getContainerStore(ptr).fill(ref->mBase->mInventory, ptr.getCellRef().getRefId(), prng);
|
||||||
|
|
||||||
if (hasInventory)
|
if (hasInventory)
|
||||||
getInventoryStore(ptr).autoEquip(ptr);
|
getInventoryStore(ptr).autoEquip(ptr);
|
||||||
|
@ -264,8 +265,8 @@ namespace MWClass
|
||||||
osg::Vec3f hitPosition (result.second);
|
osg::Vec3f hitPosition (result.second);
|
||||||
|
|
||||||
float hitchance = MWMechanics::getHitChance(ptr, victim, ref->mBase->mData.mCombat);
|
float hitchance = MWMechanics::getHitChance(ptr, victim, ref->mBase->mData.mCombat);
|
||||||
|
auto& prng = MWBase::Environment::get().getWorld()->getPrng();
|
||||||
if(Misc::Rng::roll0to99() >= hitchance)
|
if(Misc::Rng::roll0to99(prng) >= hitchance)
|
||||||
{
|
{
|
||||||
victim.getClass().onHit(victim, 0.0f, false, MWWorld::Ptr(), ptr, osg::Vec3f(), false);
|
victim.getClass().onHit(victim, 0.0f, false, MWWorld::Ptr(), ptr, osg::Vec3f(), false);
|
||||||
MWMechanics::reduceWeaponCondition(0.f, false, weapon, ptr);
|
MWMechanics::reduceWeaponCondition(0.f, false, weapon, ptr);
|
||||||
|
@ -392,7 +393,8 @@ namespace MWClass
|
||||||
float agilityTerm = stats.getAttribute(ESM::Attribute::Agility).getModified() * getGmst().fKnockDownMult->mValue.getFloat();
|
float agilityTerm = stats.getAttribute(ESM::Attribute::Agility).getModified() * getGmst().fKnockDownMult->mValue.getFloat();
|
||||||
float knockdownTerm = stats.getAttribute(ESM::Attribute::Agility).getModified()
|
float knockdownTerm = stats.getAttribute(ESM::Attribute::Agility).getModified()
|
||||||
* getGmst().iKnockDownOddsMult->mValue.getInteger() * 0.01f + getGmst().iKnockDownOddsBase->mValue.getInteger();
|
* getGmst().iKnockDownOddsMult->mValue.getInteger() * 0.01f + getGmst().iKnockDownOddsBase->mValue.getInteger();
|
||||||
if (ishealth && agilityTerm <= damage && knockdownTerm <= Misc::Rng::roll0to99())
|
auto& prng = MWBase::Environment::get().getWorld()->getPrng();
|
||||||
|
if (ishealth && agilityTerm <= damage && knockdownTerm <= Misc::Rng::roll0to99(prng))
|
||||||
stats.setKnockedDown(true);
|
stats.setKnockedDown(true);
|
||||||
else
|
else
|
||||||
stats.setHitRecovery(true); // Is this supposed to always occur?
|
stats.setHitRecovery(true); // Is this supposed to always occur?
|
||||||
|
@ -429,7 +431,8 @@ namespace MWClass
|
||||||
if(actor.getClass().isNpc() && actor.getClass().getNpcStats(actor).isWerewolf())
|
if(actor.getClass().isNpc() && actor.getClass().getNpcStats(actor).isWerewolf())
|
||||||
{
|
{
|
||||||
const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore();
|
const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore();
|
||||||
const ESM::Sound *sound = store.get<ESM::Sound>().searchRandom("WolfCreature");
|
auto& prng = MWBase::Environment::get().getWorld()->getPrng();
|
||||||
|
const ESM::Sound *sound = store.get<ESM::Sound>().searchRandom("WolfCreature", prng);
|
||||||
|
|
||||||
std::shared_ptr<MWWorld::Action> action(new MWWorld::FailedAction("#{sWerewolfRefusal}"));
|
std::shared_ptr<MWWorld::Action> action(new MWWorld::FailedAction("#{sWerewolfRefusal}"));
|
||||||
if(sound) action->setSound(sound->mId);
|
if(sound) action->setSound(sound->mId);
|
||||||
|
@ -642,10 +645,11 @@ namespace MWClass
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto& prng = MWBase::Environment::get().getWorld()->getPrng();
|
||||||
if (!sounds.empty())
|
if (!sounds.empty())
|
||||||
return sounds[Misc::Rng::rollDice(sounds.size())]->mSound;
|
return sounds[Misc::Rng::rollDice(sounds.size(), prng)]->mSound;
|
||||||
if (!fallbacksounds.empty())
|
if (!fallbacksounds.empty())
|
||||||
return fallbacksounds[Misc::Rng::rollDice(fallbacksounds.size())]->mSound;
|
return fallbacksounds[Misc::Rng::rollDice(fallbacksounds.size(), prng)]->mSound;
|
||||||
|
|
||||||
return std::string();
|
return std::string();
|
||||||
}
|
}
|
||||||
|
|
|
@ -127,7 +127,8 @@ namespace MWClass
|
||||||
MWWorld::LiveCellRef<ESM::CreatureLevList> *ref =
|
MWWorld::LiveCellRef<ESM::CreatureLevList> *ref =
|
||||||
ptr.get<ESM::CreatureLevList>();
|
ptr.get<ESM::CreatureLevList>();
|
||||||
|
|
||||||
std::string id = MWMechanics::getLevelledItem(ref->mBase, true);
|
auto& prng = MWBase::Environment::get().getWorld()->getPrng();
|
||||||
|
std::string id = MWMechanics::getLevelledItem(ref->mBase, true, prng);
|
||||||
|
|
||||||
if (!id.empty())
|
if (!id.empty())
|
||||||
{
|
{
|
||||||
|
|
|
@ -394,7 +394,8 @@ namespace MWClass
|
||||||
|
|
||||||
// inventory
|
// inventory
|
||||||
// setting ownership is used to make the NPC auto-equip his initial equipment only, and not bartered items
|
// setting ownership is used to make the NPC auto-equip his initial equipment only, and not bartered items
|
||||||
getInventoryStore(ptr).fill(ref->mBase->mInventory, ptr.getCellRef().getRefId());
|
auto& prng = MWBase::Environment::get().getWorld()->getPrng();
|
||||||
|
getInventoryStore(ptr).fill(ref->mBase->mInventory, ptr.getCellRef().getRefId(), prng);
|
||||||
|
|
||||||
getInventoryStore(ptr).autoEquip(ptr);
|
getInventoryStore(ptr).autoEquip(ptr);
|
||||||
}
|
}
|
||||||
|
@ -584,7 +585,7 @@ namespace MWClass
|
||||||
|
|
||||||
float hitchance = MWMechanics::getHitChance(ptr, victim, getSkill(ptr, weapskill));
|
float hitchance = MWMechanics::getHitChance(ptr, victim, getSkill(ptr, weapskill));
|
||||||
|
|
||||||
if (Misc::Rng::roll0to99() >= hitchance)
|
if (Misc::Rng::roll0to99(world->getPrng()) >= hitchance)
|
||||||
{
|
{
|
||||||
othercls.onHit(victim, 0.0f, false, weapon, ptr, osg::Vec3f(), false);
|
othercls.onHit(victim, 0.0f, false, weapon, ptr, osg::Vec3f(), false);
|
||||||
MWMechanics::reduceWeaponCondition(0.f, false, weapon, ptr);
|
MWMechanics::reduceWeaponCondition(0.f, false, weapon, ptr);
|
||||||
|
@ -726,15 +727,16 @@ namespace MWClass
|
||||||
const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore();
|
const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore();
|
||||||
const GMST& gmst = getGmst();
|
const GMST& gmst = getGmst();
|
||||||
|
|
||||||
int chance = store.get<ESM::GameSetting>().find("iVoiceHitOdds")->mValue.getInteger();
|
int chance = store.get<ESM::GameSetting>().find("iVoiceHitOdds")->mValue.getInteger();
|
||||||
if (Misc::Rng::roll0to99() < chance)
|
auto& prng = MWBase::Environment::get().getWorld()->getPrng();
|
||||||
|
if (Misc::Rng::roll0to99(prng) < chance)
|
||||||
MWBase::Environment::get().getDialogueManager()->say(ptr, "hit");
|
MWBase::Environment::get().getDialogueManager()->say(ptr, "hit");
|
||||||
|
|
||||||
// Check for knockdown
|
// Check for knockdown
|
||||||
float agilityTerm = stats.getAttribute(ESM::Attribute::Agility).getModified() * gmst.fKnockDownMult->mValue.getFloat();
|
float agilityTerm = stats.getAttribute(ESM::Attribute::Agility).getModified() * gmst.fKnockDownMult->mValue.getFloat();
|
||||||
float knockdownTerm = stats.getAttribute(ESM::Attribute::Agility).getModified()
|
float knockdownTerm = stats.getAttribute(ESM::Attribute::Agility).getModified()
|
||||||
* gmst.iKnockDownOddsMult->mValue.getInteger() * 0.01f + gmst.iKnockDownOddsBase->mValue.getInteger();
|
* gmst.iKnockDownOddsMult->mValue.getInteger() * 0.01f + gmst.iKnockDownOddsBase->mValue.getInteger();
|
||||||
if (ishealth && agilityTerm <= damage && knockdownTerm <= Misc::Rng::roll0to99())
|
if (ishealth && agilityTerm <= damage && knockdownTerm <= Misc::Rng::roll0to99(prng))
|
||||||
stats.setKnockedDown(true);
|
stats.setKnockedDown(true);
|
||||||
else
|
else
|
||||||
stats.setHitRecovery(true); // Is this supposed to always occur?
|
stats.setHitRecovery(true); // Is this supposed to always occur?
|
||||||
|
@ -757,7 +759,7 @@ namespace MWClass
|
||||||
MWWorld::InventoryStore::Slot_RightPauldron, MWWorld::InventoryStore::Slot_RightPauldron,
|
MWWorld::InventoryStore::Slot_RightPauldron, MWWorld::InventoryStore::Slot_RightPauldron,
|
||||||
MWWorld::InventoryStore::Slot_LeftGauntlet, MWWorld::InventoryStore::Slot_RightGauntlet
|
MWWorld::InventoryStore::Slot_LeftGauntlet, MWWorld::InventoryStore::Slot_RightGauntlet
|
||||||
};
|
};
|
||||||
int hitslot = hitslots[Misc::Rng::rollDice(20)];
|
int hitslot = hitslots[Misc::Rng::rollDice(20, prng)];
|
||||||
|
|
||||||
float unmitigatedDamage = damage;
|
float unmitigatedDamage = damage;
|
||||||
float x = damage / (damage + getArmorRating(ptr));
|
float x = damage / (damage + getArmorRating(ptr));
|
||||||
|
@ -773,7 +775,7 @@ namespace MWClass
|
||||||
// If there's no item in the carried left slot or if it is not a shield redistribute the hit.
|
// If there's no item in the carried left slot or if it is not a shield redistribute the hit.
|
||||||
if (!hasArmor && hitslot == MWWorld::InventoryStore::Slot_CarriedLeft)
|
if (!hasArmor && hitslot == MWWorld::InventoryStore::Slot_CarriedLeft)
|
||||||
{
|
{
|
||||||
if (Misc::Rng::rollDice(2) == 0)
|
if (Misc::Rng::rollDice(2, prng) == 0)
|
||||||
hitslot = MWWorld::InventoryStore::Slot_Cuirass;
|
hitslot = MWWorld::InventoryStore::Slot_Cuirass;
|
||||||
else
|
else
|
||||||
hitslot = MWWorld::InventoryStore::Slot_LeftPauldron;
|
hitslot = MWWorld::InventoryStore::Slot_LeftPauldron;
|
||||||
|
@ -865,7 +867,8 @@ namespace MWClass
|
||||||
if(actor.getClass().isNpc() && actor.getClass().getNpcStats(actor).isWerewolf())
|
if(actor.getClass().isNpc() && actor.getClass().getNpcStats(actor).isWerewolf())
|
||||||
{
|
{
|
||||||
const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore();
|
const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore();
|
||||||
const ESM::Sound *sound = store.get<ESM::Sound>().searchRandom("WolfNPC");
|
auto& prng = MWBase::Environment::get().getWorld()->getPrng();
|
||||||
|
const ESM::Sound *sound = store.get<ESM::Sound>().searchRandom("WolfNPC", prng);
|
||||||
|
|
||||||
std::shared_ptr<MWWorld::Action> action(new MWWorld::FailedAction("#{sWerewolfRefusal}"));
|
std::shared_ptr<MWWorld::Action> action(new MWWorld::FailedAction("#{sWerewolfRefusal}"));
|
||||||
if(sound) action->setSound(sound->mId);
|
if(sound) action->setSound(sound->mId);
|
||||||
|
|
|
@ -87,7 +87,8 @@ namespace MWGui
|
||||||
std::set<int> skills;
|
std::set<int> skills;
|
||||||
for (int day=0; day<mDays; ++day)
|
for (int day=0; day<mDays; ++day)
|
||||||
{
|
{
|
||||||
int skill = Misc::Rng::rollDice(ESM::Skill::Length);
|
auto& prng = MWBase::Environment::get().getWorld()->getPrng();
|
||||||
|
int skill = Misc::Rng::rollDice(ESM::Skill::Length, prng);
|
||||||
skills.insert(skill);
|
skills.insert(skill);
|
||||||
|
|
||||||
MWMechanics::SkillValue& value = player.getClass().getNpcStats(player).getSkill(skill);
|
MWMechanics::SkillValue& value = player.getClass().getNpcStats(player).getSkill(skill);
|
||||||
|
|
|
@ -24,13 +24,13 @@ namespace MWGui
|
||||||
float chance = player.getClass().getSkill(player, ESM::Skill::Sneak);
|
float chance = player.getClass().getSkill(player, ESM::Skill::Sneak);
|
||||||
|
|
||||||
mSourceModel->update();
|
mSourceModel->update();
|
||||||
|
|
||||||
// build list of items that player is unable to find when attempts to pickpocket.
|
// build list of items that player is unable to find when attempts to pickpocket.
|
||||||
if (hideItems)
|
if (hideItems)
|
||||||
{
|
{
|
||||||
|
auto& prng = MWBase::Environment::get().getWorld()->getPrng();
|
||||||
for (size_t i = 0; i<mSourceModel->getItemCount(); ++i)
|
for (size_t i = 0; i<mSourceModel->getItemCount(); ++i)
|
||||||
{
|
{
|
||||||
if (Misc::Rng::roll0to99() > chance)
|
if (Misc::Rng::roll0to99(prng) > chance)
|
||||||
mHiddenItems.push_back(mSourceModel->getItem(i));
|
mHiddenItems.push_back(mSourceModel->getItem(i));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -191,7 +191,7 @@ namespace MWGui
|
||||||
if (!region->mSleepList.empty())
|
if (!region->mSleepList.empty())
|
||||||
{
|
{
|
||||||
// figure out if player will be woken while sleeping
|
// figure out if player will be woken while sleeping
|
||||||
int x = Misc::Rng::rollDice(hoursToWait);
|
int x = Misc::Rng::rollDice(hoursToWait, world->getPrng());
|
||||||
float fSleepRandMod = world->getStore().get<ESM::GameSetting>().find("fSleepRandMod")->mValue.getFloat();
|
float fSleepRandMod = world->getStore().get<ESM::GameSetting>().find("fSleepRandMod")->mValue.getFloat();
|
||||||
if (x < fSleepRandMod * hoursToWait)
|
if (x < fSleepRandMod * hoursToWait)
|
||||||
{
|
{
|
||||||
|
|
|
@ -60,6 +60,11 @@ namespace MWMechanics
|
||||||
mIsTurningToPlayer = turning;
|
mIsTurningToPlayer = turning;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Misc::TimerStatus Actor::updateEngageCombatTimer(float duration)
|
||||||
|
{
|
||||||
|
return mEngageCombat.update(duration, MWBase::Environment::get().getWorld()->getPrng());
|
||||||
|
}
|
||||||
|
|
||||||
void Actor::setPositionAdjusted(bool adjusted)
|
void Actor::setPositionAdjusted(bool adjusted)
|
||||||
{
|
{
|
||||||
mPositionAdjusted = adjusted;
|
mPositionAdjusted = adjusted;
|
||||||
|
|
|
@ -43,10 +43,7 @@ namespace MWMechanics
|
||||||
bool isTurningToPlayer() const;
|
bool isTurningToPlayer() const;
|
||||||
void setTurningToPlayer(bool turning);
|
void setTurningToPlayer(bool turning);
|
||||||
|
|
||||||
Misc::TimerStatus updateEngageCombatTimer(float duration)
|
Misc::TimerStatus updateEngageCombatTimer(float duration);
|
||||||
{
|
|
||||||
return mEngageCombat.update(duration);
|
|
||||||
}
|
|
||||||
|
|
||||||
void setPositionAdjusted(bool adjusted);
|
void setPositionAdjusted(bool adjusted);
|
||||||
bool getPositionAdjusted() const;
|
bool getPositionAdjusted() const;
|
||||||
|
@ -57,7 +54,7 @@ namespace MWMechanics
|
||||||
float mTargetAngleRadians{0.f};
|
float mTargetAngleRadians{0.f};
|
||||||
GreetingState mGreetingState{Greet_None};
|
GreetingState mGreetingState{Greet_None};
|
||||||
bool mIsTurningToPlayer{false};
|
bool mIsTurningToPlayer{false};
|
||||||
Misc::DeviatingPeriodicTimer mEngageCombat{1.0f, 0.25f, Misc::Rng::deviate(0, 0.25f)};
|
Misc::DeviatingPeriodicTimer mEngageCombat{1.0f, 0.25f, Misc::Rng::deviate(0, 0.25f, MWBase::Environment::get().getWorld()->getPrng())};
|
||||||
bool mPositionAdjusted;
|
bool mPositionAdjusted;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -305,7 +305,7 @@ namespace MWMechanics
|
||||||
// We chose to use the chance MW would have when run at 60 FPS with the default value of the GMST.
|
// We chose to use the chance MW would have when run at 60 FPS with the default value of the GMST.
|
||||||
const float delta = MWBase::Environment::get().getFrameDuration() * 6.f;
|
const float delta = MWBase::Environment::get().getFrameDuration() * 6.f;
|
||||||
static const float fVoiceIdleOdds = world->getStore().get<ESM::GameSetting>().find("fVoiceIdleOdds")->mValue.getFloat();
|
static const float fVoiceIdleOdds = world->getStore().get<ESM::GameSetting>().find("fVoiceIdleOdds")->mValue.getFloat();
|
||||||
if (Misc::Rng::rollProbability() * 10000.f < fVoiceIdleOdds * delta && world->getLOS(getPlayer(), actor))
|
if (Misc::Rng::rollProbability(world->getPrng()) * 10000.f < fVoiceIdleOdds * delta && world->getLOS(getPlayer(), actor))
|
||||||
MWBase::Environment::get().getDialogueManager()->say(actor, "idle");
|
MWBase::Environment::get().getDialogueManager()->say(actor, "idle");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -80,7 +80,8 @@ bool MWMechanics::AiAvoidDoor::isStuck(const osg::Vec3f& actorPos) const
|
||||||
|
|
||||||
void MWMechanics::AiAvoidDoor::adjustDirection()
|
void MWMechanics::AiAvoidDoor::adjustDirection()
|
||||||
{
|
{
|
||||||
mDirection = Misc::Rng::rollDice(MAX_DIRECTIONS);
|
auto& prng = MWBase::Environment::get().getWorld()->getPrng();
|
||||||
|
mDirection = Misc::Rng::rollDice(MAX_DIRECTIONS, prng);
|
||||||
}
|
}
|
||||||
|
|
||||||
float MWMechanics::AiAvoidDoor::getAdjustedAngle() const
|
float MWMechanics::AiAvoidDoor::getAdjustedAngle() const
|
||||||
|
|
|
@ -370,7 +370,8 @@ namespace MWMechanics
|
||||||
|
|
||||||
if (!points.empty())
|
if (!points.empty())
|
||||||
{
|
{
|
||||||
ESM::Pathgrid::Point dest = points[Misc::Rng::rollDice(points.size())];
|
auto& prng = MWBase::Environment::get().getWorld()->getPrng();
|
||||||
|
ESM::Pathgrid::Point dest = points[Misc::Rng::rollDice(points.size(), prng)];
|
||||||
coords.toWorld(dest);
|
coords.toWorld(dest);
|
||||||
|
|
||||||
state = AiCombatStorage::FleeState_RunToDestination;
|
state = AiCombatStorage::FleeState_RunToDestination;
|
||||||
|
@ -464,8 +465,38 @@ namespace MWMechanics
|
||||||
sequence.mPackages.push_back(std::move(package));
|
sequence.mPackages.push_back(std::move(package));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
AiCombatStorage::AiCombatStorage() :
|
||||||
|
mAttackCooldown(0.0f),
|
||||||
|
mReaction(MWBase::Environment::get().getWorld()->getPrng()),
|
||||||
|
mTimerCombatMove(0.0f),
|
||||||
|
mReadyToAttack(false),
|
||||||
|
mAttack(false),
|
||||||
|
mAttackRange(0.0f),
|
||||||
|
mCombatMove(false),
|
||||||
|
mRotateMove(false),
|
||||||
|
mLastTargetPos(0, 0, 0),
|
||||||
|
mCell(nullptr),
|
||||||
|
mCurrentAction(),
|
||||||
|
mActionCooldown(0.0f),
|
||||||
|
mStrength(),
|
||||||
|
mForceNoShortcut(false),
|
||||||
|
mShortcutFailPos(),
|
||||||
|
mMovement(),
|
||||||
|
mFleeState(FleeState_None),
|
||||||
|
mLOS(false),
|
||||||
|
mUpdateLOSTimer(0.0f),
|
||||||
|
mFleeBlindRunTimer(0.0f),
|
||||||
|
mUseCustomDestination(false),
|
||||||
|
mCustomDestination()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
void AiCombatStorage::startCombatMove(bool isDistantCombat, float distToTarget, float rangeAttack, const MWWorld::Ptr& actor, const MWWorld::Ptr& target)
|
void AiCombatStorage::startCombatMove(bool isDistantCombat, float distToTarget, float rangeAttack, const MWWorld::Ptr& actor, const MWWorld::Ptr& target)
|
||||||
{
|
{
|
||||||
|
auto& prng = MWBase::Environment::get().getWorld()->getPrng();
|
||||||
|
|
||||||
// get the range of the target's weapon
|
// get the range of the target's weapon
|
||||||
MWWorld::Ptr targetWeapon = MWWorld::Ptr();
|
MWWorld::Ptr targetWeapon = MWWorld::Ptr();
|
||||||
const MWWorld::Class& targetClass = target.getClass();
|
const MWWorld::Class& targetClass = target.getClass();
|
||||||
|
@ -483,7 +514,7 @@ namespace MWMechanics
|
||||||
|
|
||||||
if (mMovement.mPosition[0] || mMovement.mPosition[1])
|
if (mMovement.mPosition[0] || mMovement.mPosition[1])
|
||||||
{
|
{
|
||||||
mTimerCombatMove = 0.1f + 0.1f * Misc::Rng::rollClosedProbability();
|
mTimerCombatMove = 0.1f + 0.1f * Misc::Rng::rollClosedProbability(prng);
|
||||||
mCombatMove = true;
|
mCombatMove = true;
|
||||||
}
|
}
|
||||||
else if (isDistantCombat)
|
else if (isDistantCombat)
|
||||||
|
@ -537,11 +568,11 @@ namespace MWMechanics
|
||||||
// if actor is within range of target's weapon.
|
// if actor is within range of target's weapon.
|
||||||
if (std::abs(angleToTarget) > osg::PI / 4)
|
if (std::abs(angleToTarget) > osg::PI / 4)
|
||||||
moveDuration = 0.2f;
|
moveDuration = 0.2f;
|
||||||
else if (distToTarget <= rangeAttackOfTarget && Misc::Rng::rollClosedProbability() < 0.25)
|
else if (distToTarget <= rangeAttackOfTarget && Misc::Rng::rollClosedProbability(prng) < 0.25)
|
||||||
moveDuration = 0.1f + 0.1f * Misc::Rng::rollClosedProbability();
|
moveDuration = 0.1f + 0.1f * Misc::Rng::rollClosedProbability(prng);
|
||||||
if (moveDuration > 0)
|
if (moveDuration > 0)
|
||||||
{
|
{
|
||||||
mMovement.mPosition[0] = Misc::Rng::rollProbability() < 0.5 ? 1.0f : -1.0f; // to the left/right
|
mMovement.mPosition[0] = Misc::Rng::rollProbability(prng) < 0.5 ? 1.0f : -1.0f; // to the left/right
|
||||||
mTimerCombatMove = moveDuration;
|
mTimerCombatMove = moveDuration;
|
||||||
mCombatMove = true;
|
mCombatMove = true;
|
||||||
}
|
}
|
||||||
|
@ -580,7 +611,8 @@ namespace MWMechanics
|
||||||
if (!distantCombat)
|
if (!distantCombat)
|
||||||
characterController.setAIAttackType(chooseBestAttack(weapon));
|
characterController.setAIAttackType(chooseBestAttack(weapon));
|
||||||
|
|
||||||
mStrength = Misc::Rng::rollClosedProbability();
|
auto& prng = MWBase::Environment::get().getWorld()->getPrng();
|
||||||
|
mStrength = Misc::Rng::rollClosedProbability(prng);
|
||||||
|
|
||||||
const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore();
|
const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore();
|
||||||
|
|
||||||
|
@ -592,11 +624,11 @@ namespace MWMechanics
|
||||||
|
|
||||||
// Say a provoking combat phrase
|
// Say a provoking combat phrase
|
||||||
const int iVoiceAttackOdds = store.get<ESM::GameSetting>().find("iVoiceAttackOdds")->mValue.getInteger();
|
const int iVoiceAttackOdds = store.get<ESM::GameSetting>().find("iVoiceAttackOdds")->mValue.getInteger();
|
||||||
if (Misc::Rng::roll0to99() < iVoiceAttackOdds)
|
if (Misc::Rng::roll0to99(prng) < iVoiceAttackOdds)
|
||||||
{
|
{
|
||||||
MWBase::Environment::get().getDialogueManager()->say(actor, "attack");
|
MWBase::Environment::get().getDialogueManager()->say(actor, "attack");
|
||||||
}
|
}
|
||||||
mAttackCooldown = std::min(baseDelay + 0.01 * Misc::Rng::roll0to99(), baseDelay + 0.9);
|
mAttackCooldown = std::min(baseDelay + 0.01 * Misc::Rng::roll0to99(prng), baseDelay + 0.9);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
mAttackCooldown -= AI_REACTION_TIME;
|
mAttackCooldown -= AI_REACTION_TIME;
|
||||||
|
@ -657,7 +689,8 @@ std::string chooseBestAttack(const ESM::Weapon* weapon)
|
||||||
int chop = (weapon->mData.mChop[0] + weapon->mData.mChop[1])/2;
|
int chop = (weapon->mData.mChop[0] + weapon->mData.mChop[1])/2;
|
||||||
int thrust = (weapon->mData.mThrust[0] + weapon->mData.mThrust[1])/2;
|
int thrust = (weapon->mData.mThrust[0] + weapon->mData.mThrust[1])/2;
|
||||||
|
|
||||||
float roll = Misc::Rng::rollClosedProbability() * (slash + chop + thrust);
|
auto& prng = MWBase::Environment::get().getWorld()->getPrng();
|
||||||
|
float roll = Misc::Rng::rollClosedProbability(prng) * (slash + chop + thrust);
|
||||||
if(roll <= slash)
|
if(roll <= slash)
|
||||||
attackType = "slash";
|
attackType = "slash";
|
||||||
else if(roll <= (slash + thrust))
|
else if(roll <= (slash + thrust))
|
||||||
|
|
|
@ -59,29 +59,7 @@ namespace MWMechanics
|
||||||
bool mUseCustomDestination;
|
bool mUseCustomDestination;
|
||||||
osg::Vec3f mCustomDestination;
|
osg::Vec3f mCustomDestination;
|
||||||
|
|
||||||
AiCombatStorage():
|
AiCombatStorage();
|
||||||
mAttackCooldown(0.0f),
|
|
||||||
mTimerCombatMove(0.0f),
|
|
||||||
mReadyToAttack(false),
|
|
||||||
mAttack(false),
|
|
||||||
mAttackRange(0.0f),
|
|
||||||
mCombatMove(false),
|
|
||||||
mRotateMove(false),
|
|
||||||
mLastTargetPos(0,0,0),
|
|
||||||
mCell(nullptr),
|
|
||||||
mCurrentAction(),
|
|
||||||
mActionCooldown(0.0f),
|
|
||||||
mStrength(),
|
|
||||||
mForceNoShortcut(false),
|
|
||||||
mShortcutFailPos(),
|
|
||||||
mMovement(),
|
|
||||||
mFleeState(FleeState_None),
|
|
||||||
mLOS(false),
|
|
||||||
mUpdateLOSTimer(0.0f),
|
|
||||||
mFleeBlindRunTimer(0.0f),
|
|
||||||
mUseCustomDestination(false),
|
|
||||||
mCustomDestination()
|
|
||||||
{}
|
|
||||||
|
|
||||||
void startCombatMove(bool isDistantCombat, float distToTarget, float rangeAttack, const MWWorld::Ptr& actor, const MWWorld::Ptr& target);
|
void startCombatMove(bool isDistantCombat, float distToTarget, float rangeAttack, const MWWorld::Ptr& actor, const MWWorld::Ptr& target);
|
||||||
void updateCombatMove(float duration);
|
void updateCombatMove(float duration);
|
||||||
|
|
|
@ -44,6 +44,7 @@ namespace
|
||||||
MWMechanics::AiPackage::AiPackage(AiPackageTypeId typeId, const Options& options) :
|
MWMechanics::AiPackage::AiPackage(AiPackageTypeId typeId, const Options& options) :
|
||||||
mTypeId(typeId),
|
mTypeId(typeId),
|
||||||
mOptions(options),
|
mOptions(options),
|
||||||
|
mReaction(MWBase::Environment::get().getWorld()->getPrng()),
|
||||||
mTargetActorRefId(""),
|
mTargetActorRefId(""),
|
||||||
mTargetActorId(-1),
|
mTargetActorId(-1),
|
||||||
mRotateOnTheRunChecks(0),
|
mRotateOnTheRunChecks(0),
|
||||||
|
|
|
@ -13,13 +13,20 @@ namespace MWMechanics
|
||||||
public:
|
public:
|
||||||
static constexpr float sDeviation = 0.1f;
|
static constexpr float sDeviation = 0.1f;
|
||||||
|
|
||||||
Misc::TimerStatus update(float duration) { return mImpl.update(duration); }
|
AiReactionTimer(Misc::Rng::Generator& prng)
|
||||||
|
: mPrng{ prng }
|
||||||
|
, mImpl{ AI_REACTION_TIME, sDeviation, Misc::Rng::deviate(0, sDeviation, prng) }
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
void reset() { mImpl.reset(Misc::Rng::deviate(0, sDeviation)); }
|
Misc::TimerStatus update(float duration) { return mImpl.update(duration, mPrng); }
|
||||||
|
|
||||||
|
void reset() { mImpl.reset(Misc::Rng::deviate(0, sDeviation, mPrng)); }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Misc::DeviatingPeriodicTimer mImpl {AI_REACTION_TIME, sDeviation,
|
Misc::Rng::Generator& mPrng;
|
||||||
Misc::Rng::deviate(0, sDeviation)};
|
Misc::DeviatingPeriodicTimer mImpl;
|
||||||
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -36,11 +36,13 @@ namespace MWMechanics
|
||||||
{
|
{
|
||||||
AiTravel::AiTravel(float x, float y, float z, bool repeat, AiTravel*)
|
AiTravel::AiTravel(float x, float y, float z, bool repeat, AiTravel*)
|
||||||
: TypedAiPackage<AiTravel>(repeat), mX(x), mY(y), mZ(z), mHidden(false)
|
: TypedAiPackage<AiTravel>(repeat), mX(x), mY(y), mZ(z), mHidden(false)
|
||||||
|
, mDestinationCheck(MWBase::Environment::get().getWorld()->getPrng())
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
AiTravel::AiTravel(float x, float y, float z, AiInternalTravel* derived)
|
AiTravel::AiTravel(float x, float y, float z, AiInternalTravel* derived)
|
||||||
: TypedAiPackage<AiTravel>(derived), mX(x), mY(y), mZ(z), mHidden(true)
|
: TypedAiPackage<AiTravel>(derived), mX(x), mY(y), mZ(z), mHidden(true)
|
||||||
|
, mDestinationCheck(MWBase::Environment::get().getWorld()->getPrng())
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -51,6 +53,7 @@ namespace MWMechanics
|
||||||
|
|
||||||
AiTravel::AiTravel(const ESM::AiSequence::AiTravel *travel)
|
AiTravel::AiTravel(const ESM::AiSequence::AiTravel *travel)
|
||||||
: TypedAiPackage<AiTravel>(travel->mRepeat), mX(travel->mData.mX), mY(travel->mData.mY), mZ(travel->mData.mZ), mHidden(false)
|
: TypedAiPackage<AiTravel>(travel->mRepeat), mX(travel->mData.mX), mY(travel->mData.mY), mZ(travel->mData.mZ), mHidden(false)
|
||||||
|
, mDestinationCheck(MWBase::Environment::get().getWorld()->getPrng())
|
||||||
{
|
{
|
||||||
// Hidden ESM::AiSequence::AiTravel package should be converted into MWMechanics::AiInternalTravel type
|
// Hidden ESM::AiSequence::AiTravel package should be converted into MWMechanics::AiInternalTravel type
|
||||||
assert(!travel->mHidden);
|
assert(!travel->mHidden);
|
||||||
|
|
|
@ -59,7 +59,8 @@ namespace MWMechanics
|
||||||
|
|
||||||
osg::Vec3f getRandomPointAround(const osg::Vec3f& position, const float distance)
|
osg::Vec3f getRandomPointAround(const osg::Vec3f& position, const float distance)
|
||||||
{
|
{
|
||||||
const float randomDirection = Misc::Rng::rollClosedProbability() * 2.0f * osg::PI;
|
auto& prng = MWBase::Environment::get().getWorld()->getPrng();
|
||||||
|
const float randomDirection = Misc::Rng::rollClosedProbability(prng) * 2.0f * osg::PI;
|
||||||
osg::Matrixf rotation;
|
osg::Matrixf rotation;
|
||||||
rotation.makeRotate(randomDirection, osg::Vec3f(0.0, 0.0, 1.0));
|
rotation.makeRotate(randomDirection, osg::Vec3f(0.0, 0.0, 1.0));
|
||||||
return position + osg::Vec3f(distance, 0.0, 0.0) * rotation;
|
return position + osg::Vec3f(distance, 0.0, 0.0) * rotation;
|
||||||
|
@ -102,6 +103,22 @@ namespace MWMechanics
|
||||||
{
|
{
|
||||||
return std::vector<unsigned char>(std::begin(idle), std::end(idle));
|
return std::vector<unsigned char>(std::begin(idle), std::end(idle));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
AiWanderStorage::AiWanderStorage() :
|
||||||
|
mReaction(MWBase::Environment::get().getWorld()->getPrng()),
|
||||||
|
mState(Wander_ChooseAction),
|
||||||
|
mIsWanderingManually(false),
|
||||||
|
mCanWanderAlongPathGrid(true),
|
||||||
|
mIdleAnimation(0),
|
||||||
|
mBadIdles(),
|
||||||
|
mPopulateAvailableNodes(true),
|
||||||
|
mAllowedNodes(),
|
||||||
|
mTrimCurrentNode(false),
|
||||||
|
mCheckIdlePositionTimer(0),
|
||||||
|
mStuckCount(0)
|
||||||
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
AiWander::AiWander(int distance, int duration, int timeOfDay, const std::vector<unsigned char>& idle, bool repeat):
|
AiWander::AiWander(int distance, int duration, int timeOfDay, const std::vector<unsigned char>& idle, bool repeat):
|
||||||
|
@ -250,9 +267,10 @@ namespace MWMechanics
|
||||||
getAllowedNodes(actor, actor.getCell()->getCell(), storage);
|
getAllowedNodes(actor, actor.getCell()->getCell(), storage);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto& prng = MWBase::Environment::get().getWorld()->getPrng();
|
||||||
if (canActorMoveByZAxis(actor) && mDistance > 0) {
|
if (canActorMoveByZAxis(actor) && mDistance > 0) {
|
||||||
// Typically want to idle for a short time before the next wander
|
// Typically want to idle for a short time before the next wander
|
||||||
if (Misc::Rng::rollDice(100) >= 92 && storage.mState != AiWanderStorage::Wander_Walking) {
|
if (Misc::Rng::rollDice(100, prng) >= 92 && storage.mState != AiWanderStorage::Wander_Walking) {
|
||||||
wanderNearStart(actor, storage, mDistance);
|
wanderNearStart(actor, storage, mDistance);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -262,7 +280,7 @@ namespace MWMechanics
|
||||||
// randomly idle or wander near spawn point
|
// randomly idle or wander near spawn point
|
||||||
else if(storage.mAllowedNodes.empty() && mDistance > 0 && !storage.mIsWanderingManually) {
|
else if(storage.mAllowedNodes.empty() && mDistance > 0 && !storage.mIsWanderingManually) {
|
||||||
// Typically want to idle for a short time before the next wander
|
// Typically want to idle for a short time before the next wander
|
||||||
if (Misc::Rng::rollDice(100) >= 96) {
|
if (Misc::Rng::rollDice(100, prng) >= 96) {
|
||||||
wanderNearStart(actor, storage, mDistance);
|
wanderNearStart(actor, storage, mDistance);
|
||||||
} else {
|
} else {
|
||||||
storage.setState(AiWanderStorage::Wander_IdleNow);
|
storage.setState(AiWanderStorage::Wander_IdleNow);
|
||||||
|
@ -330,10 +348,12 @@ namespace MWMechanics
|
||||||
const auto navigator = world->getNavigator();
|
const auto navigator = world->getNavigator();
|
||||||
const auto navigatorFlags = getNavigatorFlags(actor);
|
const auto navigatorFlags = getNavigatorFlags(actor);
|
||||||
const auto areaCosts = getAreaCosts(actor);
|
const auto areaCosts = getAreaCosts(actor);
|
||||||
|
auto& prng = MWBase::Environment::get().getWorld()->getPrng();
|
||||||
|
|
||||||
do {
|
do {
|
||||||
|
|
||||||
// Determine a random location within radius of original position
|
// Determine a random location within radius of original position
|
||||||
const float wanderRadius = (0.2f + Misc::Rng::rollClosedProbability() * 0.8f) * wanderDistance;
|
const float wanderRadius = (0.2f + Misc::Rng::rollClosedProbability(prng) * 0.8f) * wanderDistance;
|
||||||
if (!isWaterCreature && !isFlyingCreature)
|
if (!isWaterCreature && !isFlyingCreature)
|
||||||
{
|
{
|
||||||
// findRandomPointAroundCircle uses wanderDistance as limit for random and not as exact distance
|
// findRandomPointAroundCircle uses wanderDistance as limit for random and not as exact distance
|
||||||
|
@ -544,7 +564,8 @@ namespace MWMechanics
|
||||||
|
|
||||||
void AiWander::setPathToAnAllowedNode(const MWWorld::Ptr& actor, AiWanderStorage& storage, const ESM::Position& actorPos)
|
void AiWander::setPathToAnAllowedNode(const MWWorld::Ptr& actor, AiWanderStorage& storage, const ESM::Position& actorPos)
|
||||||
{
|
{
|
||||||
unsigned int randNode = Misc::Rng::rollDice(storage.mAllowedNodes.size());
|
auto& prng = MWBase::Environment::get().getWorld()->getPrng();
|
||||||
|
unsigned int randNode = Misc::Rng::rollDice(storage.mAllowedNodes.size(), prng);
|
||||||
ESM::Pathgrid::Point dest(storage.mAllowedNodes[randNode]);
|
ESM::Pathgrid::Point dest(storage.mAllowedNodes[randNode]);
|
||||||
|
|
||||||
ToWorldCoordinates(dest, actor.getCell()->getCell());
|
ToWorldCoordinates(dest, actor.getCell()->getCell());
|
||||||
|
@ -650,11 +671,11 @@ namespace MWMechanics
|
||||||
|
|
||||||
for(unsigned int counter = 0; counter < mIdle.size(); counter++)
|
for(unsigned int counter = 0; counter < mIdle.size(); counter++)
|
||||||
{
|
{
|
||||||
static float fIdleChanceMultiplier = MWBase::Environment::get().getWorld()->getStore()
|
MWBase::World* world = MWBase::Environment::get().getWorld();
|
||||||
.get<ESM::GameSetting>().find("fIdleChanceMultiplier")->mValue.getFloat();
|
static float fIdleChanceMultiplier = world->getStore().get<ESM::GameSetting>().find("fIdleChanceMultiplier")->mValue.getFloat();
|
||||||
|
|
||||||
unsigned short idleChance = static_cast<unsigned short>(fIdleChanceMultiplier * mIdle[counter]);
|
unsigned short idleChance = static_cast<unsigned short>(fIdleChanceMultiplier * mIdle[counter]);
|
||||||
unsigned short randSelect = (int)(Misc::Rng::rollProbability() * int(100 / fIdleChanceMultiplier));
|
unsigned short randSelect = (int)(Misc::Rng::rollProbability(world->getPrng()) * int(100 / fIdleChanceMultiplier));
|
||||||
if(randSelect < idleChance && randSelect > idleRoll)
|
if(randSelect < idleChance && randSelect > idleRoll)
|
||||||
{
|
{
|
||||||
selectedAnimation = counter + GroupIndex_MinIdle;
|
selectedAnimation = counter + GroupIndex_MinIdle;
|
||||||
|
@ -678,7 +699,8 @@ namespace MWMechanics
|
||||||
if (storage.mAllowedNodes.empty())
|
if (storage.mAllowedNodes.empty())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
int index = Misc::Rng::rollDice(storage.mAllowedNodes.size());
|
auto& prng = MWBase::Environment::get().getWorld()->getPrng();
|
||||||
|
int index = Misc::Rng::rollDice(storage.mAllowedNodes.size(), prng);
|
||||||
ESM::Pathgrid::Point dest = storage.mAllowedNodes[index];
|
ESM::Pathgrid::Point dest = storage.mAllowedNodes[index];
|
||||||
ESM::Pathgrid::Point worldDest = dest;
|
ESM::Pathgrid::Point worldDest = dest;
|
||||||
ToWorldCoordinates(worldDest, actor.getCell()->getCell());
|
ToWorldCoordinates(worldDest, actor.getCell()->getCell());
|
||||||
|
@ -700,7 +722,7 @@ namespace MWMechanics
|
||||||
// AI will try to move the NPC towards every neighboring node until suitable place will be found
|
// AI will try to move the NPC towards every neighboring node until suitable place will be found
|
||||||
for (int i = 0; i < initialSize; i++)
|
for (int i = 0; i < initialSize; i++)
|
||||||
{
|
{
|
||||||
int randomIndex = Misc::Rng::rollDice(points.size());
|
int randomIndex = Misc::Rng::rollDice(points.size(), prng);
|
||||||
ESM::Pathgrid::Point connDest = points[randomIndex];
|
ESM::Pathgrid::Point connDest = points[randomIndex];
|
||||||
|
|
||||||
// add an offset towards random neighboring node
|
// add an offset towards random neighboring node
|
||||||
|
|
|
@ -55,18 +55,7 @@ namespace MWMechanics
|
||||||
float mCheckIdlePositionTimer;
|
float mCheckIdlePositionTimer;
|
||||||
int mStuckCount;
|
int mStuckCount;
|
||||||
|
|
||||||
AiWanderStorage():
|
AiWanderStorage();
|
||||||
mState(Wander_ChooseAction),
|
|
||||||
mIsWanderingManually(false),
|
|
||||||
mCanWanderAlongPathGrid(true),
|
|
||||||
mIdleAnimation(0),
|
|
||||||
mBadIdles(),
|
|
||||||
mPopulateAvailableNodes(true),
|
|
||||||
mAllowedNodes(),
|
|
||||||
mTrimCurrentNode(false),
|
|
||||||
mCheckIdlePositionTimer(0),
|
|
||||||
mStuckCount(0)
|
|
||||||
{};
|
|
||||||
|
|
||||||
void setState(const WanderState wanderState, const bool isManualWander = false)
|
void setState(const WanderState wanderState, const bool isManualWander = false)
|
||||||
{
|
{
|
||||||
|
|
|
@ -289,7 +289,8 @@ void MWMechanics::Alchemy::addPotion (const std::string& name)
|
||||||
|
|
||||||
newRecord.mName = name;
|
newRecord.mName = name;
|
||||||
|
|
||||||
int index = Misc::Rng::rollDice(6);
|
auto& prng = MWBase::Environment::get().getWorld()->getPrng();
|
||||||
|
int index = Misc::Rng::rollDice(6, prng);
|
||||||
assert (index>=0 && index<6);
|
assert (index>=0 && index<6);
|
||||||
|
|
||||||
static const char *meshes[] = { "standard", "bargain", "cheap", "fresh", "exclusive", "quality" };
|
static const char *meshes[] = { "standard", "bargain", "cheap", "fresh", "exclusive", "quality" };
|
||||||
|
@ -527,8 +528,8 @@ MWMechanics::Alchemy::Result MWMechanics::Alchemy::createSingle ()
|
||||||
removeIngredients();
|
removeIngredients();
|
||||||
return Result_RandomFailure;
|
return Result_RandomFailure;
|
||||||
}
|
}
|
||||||
|
auto& prng = MWBase::Environment::get().getWorld()->getPrng();
|
||||||
if (getAlchemyFactor() < Misc::Rng::roll0to99())
|
if (getAlchemyFactor() < Misc::Rng::roll0to99(prng))
|
||||||
{
|
{
|
||||||
removeIngredients();
|
removeIngredients();
|
||||||
return Result_RandomFailure;
|
return Result_RandomFailure;
|
||||||
|
|
|
@ -193,11 +193,13 @@ public:
|
||||||
|
|
||||||
std::string CharacterController::chooseRandomGroup (const std::string& prefix, int* num) const
|
std::string CharacterController::chooseRandomGroup (const std::string& prefix, int* num) const
|
||||||
{
|
{
|
||||||
|
auto& prng = MWBase::Environment::get().getWorld()->getPrng();
|
||||||
|
|
||||||
int numAnims=0;
|
int numAnims=0;
|
||||||
while (mAnimation->hasAnimation(prefix + std::to_string(numAnims+1)))
|
while (mAnimation->hasAnimation(prefix + std::to_string(numAnims+1)))
|
||||||
++numAnims;
|
++numAnims;
|
||||||
|
|
||||||
int roll = Misc::Rng::rollDice(numAnims) + 1; // [1, numAnims]
|
int roll = Misc::Rng::rollDice(numAnims, prng) + 1; // [1, numAnims]
|
||||||
if (num)
|
if (num)
|
||||||
*num = roll;
|
*num = roll;
|
||||||
return prefix + std::to_string(roll);
|
return prefix + std::to_string(roll);
|
||||||
|
@ -209,12 +211,13 @@ void CharacterController::refreshHitRecoilAnims(CharacterState& idle)
|
||||||
bool knockdown = mPtr.getClass().getCreatureStats(mPtr).getKnockedDown();
|
bool knockdown = mPtr.getClass().getCreatureStats(mPtr).getKnockedDown();
|
||||||
bool block = mPtr.getClass().getCreatureStats(mPtr).getBlock();
|
bool block = mPtr.getClass().getCreatureStats(mPtr).getBlock();
|
||||||
bool isSwimming = MWBase::Environment::get().getWorld()->isSwimming(mPtr);
|
bool isSwimming = MWBase::Environment::get().getWorld()->isSwimming(mPtr);
|
||||||
|
auto& prng = MWBase::Environment::get().getWorld()->getPrng();
|
||||||
if(mHitState == CharState_None)
|
if(mHitState == CharState_None)
|
||||||
{
|
{
|
||||||
if ((mPtr.getClass().getCreatureStats(mPtr).getFatigue().getCurrent() < 0
|
if ((mPtr.getClass().getCreatureStats(mPtr).getFatigue().getCurrent() < 0
|
||||||
|| mPtr.getClass().getCreatureStats(mPtr).getFatigue().getBase() == 0))
|
|| mPtr.getClass().getCreatureStats(mPtr).getFatigue().getBase() == 0))
|
||||||
{
|
{
|
||||||
mTimeUntilWake = Misc::Rng::rollClosedProbability() * 2 + 1; // Wake up after 1 to 3 seconds
|
mTimeUntilWake = Misc::Rng::rollClosedProbability(prng) * 2 + 1; // Wake up after 1 to 3 seconds
|
||||||
if (isSwimming && mAnimation->hasAnimation("swimknockout"))
|
if (isSwimming && mAnimation->hasAnimation("swimknockout"))
|
||||||
{
|
{
|
||||||
mHitState = CharState_SwimKnockOut;
|
mHitState = CharState_SwimKnockOut;
|
||||||
|
@ -678,7 +681,8 @@ void CharacterController::refreshIdleAnims(const std::string& weapShortGroup, Ch
|
||||||
|
|
||||||
// play until the Loop Stop key 2 to 5 times, then play until the Stop key
|
// play until the Loop Stop key 2 to 5 times, then play until the Stop key
|
||||||
// this replicates original engine behavior for the "Idle1h" 1st-person animation
|
// this replicates original engine behavior for the "Idle1h" 1st-person animation
|
||||||
numLoops = 1 + Misc::Rng::rollDice(4);
|
auto& prng = MWBase::Environment::get().getWorld()->getPrng();
|
||||||
|
numLoops = 1 + Misc::Rng::rollDice(4, prng);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1131,6 +1135,8 @@ bool CharacterController::updateCarriedLeftVisible(const int weaptype) const
|
||||||
|
|
||||||
bool CharacterController::updateState(CharacterState idle)
|
bool CharacterController::updateState(CharacterState idle)
|
||||||
{
|
{
|
||||||
|
auto& prng = MWBase::Environment::get().getWorld()->getPrng();
|
||||||
|
|
||||||
const MWWorld::Class &cls = mPtr.getClass();
|
const MWWorld::Class &cls = mPtr.getClass();
|
||||||
CreatureStats &stats = cls.getCreatureStats(mPtr);
|
CreatureStats &stats = cls.getCreatureStats(mPtr);
|
||||||
int weaptype = ESM::Weapon::None;
|
int weaptype = ESM::Weapon::None;
|
||||||
|
@ -1288,7 +1294,7 @@ bool CharacterController::updateState(CharacterState idle)
|
||||||
if(isWerewolf)
|
if(isWerewolf)
|
||||||
{
|
{
|
||||||
const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore();
|
const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore();
|
||||||
const ESM::Sound *sound = store.get<ESM::Sound>().searchRandom("WolfEquip");
|
const ESM::Sound *sound = store.get<ESM::Sound>().searchRandom("WolfEquip", prng);
|
||||||
if(sound)
|
if(sound)
|
||||||
{
|
{
|
||||||
MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager();
|
MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager();
|
||||||
|
@ -1563,7 +1569,7 @@ bool CharacterController::updateState(CharacterState idle)
|
||||||
mUpperBodyState = UpperCharState_StartToMinAttack;
|
mUpperBodyState = UpperCharState_StartToMinAttack;
|
||||||
if (isRandomAttackAnimation(mCurrentWeapon))
|
if (isRandomAttackAnimation(mCurrentWeapon))
|
||||||
{
|
{
|
||||||
mAttackStrength = std::min(1.f, 0.1f + Misc::Rng::rollClosedProbability());
|
mAttackStrength = std::min(1.f, 0.1f + Misc::Rng::rollClosedProbability(prng));
|
||||||
playSwishSound(0.0f);
|
playSwishSound(0.0f);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1596,7 +1602,7 @@ bool CharacterController::updateState(CharacterState idle)
|
||||||
// most creatures don't actually have an attack wind-up animation, so use a uniform random value
|
// most creatures don't actually have an attack wind-up animation, so use a uniform random value
|
||||||
// (even some creatures that can use weapons don't have a wind-up animation either, e.g. Rieklings)
|
// (even some creatures that can use weapons don't have a wind-up animation either, e.g. Rieklings)
|
||||||
// Note: vanilla MW uses a random value for *all* non-player actors, but we probably don't need to go that far.
|
// Note: vanilla MW uses a random value for *all* non-player actors, but we probably don't need to go that far.
|
||||||
attackStrength = std::min(1.f, 0.1f + Misc::Rng::rollClosedProbability());
|
attackStrength = std::min(1.f, 0.1f + Misc::Rng::rollClosedProbability(prng));
|
||||||
}
|
}
|
||||||
|
|
||||||
if(weapclass != ESM::WeaponType::Ranged && weapclass != ESM::WeaponType::Thrown)
|
if(weapclass != ESM::WeaponType::Ranged && weapclass != ESM::WeaponType::Thrown)
|
||||||
|
@ -1606,7 +1612,7 @@ bool CharacterController::updateState(CharacterState idle)
|
||||||
if(isWerewolf)
|
if(isWerewolf)
|
||||||
{
|
{
|
||||||
const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore();
|
const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore();
|
||||||
const ESM::Sound *sound = store.get<ESM::Sound>().searchRandom("WolfSwing");
|
const ESM::Sound *sound = store.get<ESM::Sound>().searchRandom("WolfSwing", prng);
|
||||||
if(sound)
|
if(sound)
|
||||||
sndMgr->playSound3D(mPtr, sound->mId, 1.0f, 1.0f);
|
sndMgr->playSound3D(mPtr, sound->mId, 1.0f, 1.0f);
|
||||||
}
|
}
|
||||||
|
@ -2771,7 +2777,8 @@ void CharacterController::setAIAttackType(const std::string& attackType)
|
||||||
|
|
||||||
void CharacterController::setAttackTypeRandomly(std::string& attackType)
|
void CharacterController::setAttackTypeRandomly(std::string& attackType)
|
||||||
{
|
{
|
||||||
float random = Misc::Rng::rollProbability();
|
MWBase::World* world = MWBase::Environment::get().getWorld();
|
||||||
|
float random = Misc::Rng::rollProbability(world->getPrng());
|
||||||
if (random >= 2/3.f)
|
if (random >= 2/3.f)
|
||||||
attackType = "thrust";
|
attackType = "thrust";
|
||||||
else if (random >= 1/3.f)
|
else if (random >= 1/3.f)
|
||||||
|
|
|
@ -117,7 +117,8 @@ namespace MWMechanics
|
||||||
const int iBlockMinChance = gmst.find("iBlockMinChance")->mValue.getInteger();
|
const int iBlockMinChance = gmst.find("iBlockMinChance")->mValue.getInteger();
|
||||||
int x = std::clamp<int>(blockerTerm - attackerTerm, iBlockMinChance, iBlockMaxChance);
|
int x = std::clamp<int>(blockerTerm - attackerTerm, iBlockMinChance, iBlockMaxChance);
|
||||||
|
|
||||||
if (Misc::Rng::roll0to99() < x)
|
auto& prng = MWBase::Environment::get().getWorld()->getPrng();
|
||||||
|
if (Misc::Rng::roll0to99(prng) < x)
|
||||||
{
|
{
|
||||||
// Reduce shield durability by incoming damage
|
// Reduce shield durability by incoming damage
|
||||||
int shieldhealth = shield->getClass().getItemHealth(*shield);
|
int shieldhealth = shield->getClass().getItemHealth(*shield);
|
||||||
|
@ -212,7 +213,7 @@ namespace MWMechanics
|
||||||
|
|
||||||
int skillValue = attacker.getClass().getSkill(attacker, weaponSkill);
|
int skillValue = attacker.getClass().getSkill(attacker, weaponSkill);
|
||||||
|
|
||||||
if (Misc::Rng::roll0to99() >= getHitChance(attacker, victim, skillValue))
|
if (Misc::Rng::roll0to99(world->getPrng()) >= getHitChance(attacker, victim, skillValue))
|
||||||
{
|
{
|
||||||
victim.getClass().onHit(victim, damage, false, projectile, attacker, osg::Vec3f(), false);
|
victim.getClass().onHit(victim, damage, false, projectile, attacker, osg::Vec3f(), false);
|
||||||
MWMechanics::reduceWeaponCondition(damage, false, weapon, attacker);
|
MWMechanics::reduceWeaponCondition(damage, false, weapon, attacker);
|
||||||
|
@ -262,7 +263,7 @@ namespace MWMechanics
|
||||||
if (victim != getPlayer() && !appliedEnchantment)
|
if (victim != getPlayer() && !appliedEnchantment)
|
||||||
{
|
{
|
||||||
float fProjectileThrownStoreChance = gmst.find("fProjectileThrownStoreChance")->mValue.getFloat();
|
float fProjectileThrownStoreChance = gmst.find("fProjectileThrownStoreChance")->mValue.getFloat();
|
||||||
if (Misc::Rng::rollProbability() < fProjectileThrownStoreChance / 100.f)
|
if (Misc::Rng::rollProbability(world->getPrng()) < fProjectileThrownStoreChance / 100.f)
|
||||||
victim.getClass().getContainerStore(victim).add(projectile, 1, victim);
|
victim.getClass().getContainerStore(victim).add(projectile, 1, victim);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -315,6 +316,7 @@ namespace MWMechanics
|
||||||
bool godmode = attacker == getPlayer() && MWBase::Environment::get().getWorld()->getGodModeState();
|
bool godmode = attacker == getPlayer() && MWBase::Environment::get().getWorld()->getGodModeState();
|
||||||
if (godmode)
|
if (godmode)
|
||||||
return;
|
return;
|
||||||
|
auto& prng = MWBase::Environment::get().getWorld()->getPrng();
|
||||||
for (int i=0; i<3; ++i)
|
for (int i=0; i<3; ++i)
|
||||||
{
|
{
|
||||||
float magnitude = victim.getClass().getCreatureStats(victim).getMagicEffects().get(ESM::MagicEffect::FireShield+i).getMagnitude();
|
float magnitude = victim.getClass().getCreatureStats(victim).getMagicEffects().get(ESM::MagicEffect::FireShield+i).getMagnitude();
|
||||||
|
@ -334,7 +336,7 @@ namespace MWMechanics
|
||||||
|
|
||||||
saveTerm *= 1.25f * normalisedFatigue;
|
saveTerm *= 1.25f * normalisedFatigue;
|
||||||
|
|
||||||
float x = std::max(0.f, saveTerm - Misc::Rng::roll0to99());
|
float x = std::max(0.f, saveTerm - Misc::Rng::roll0to99(prng));
|
||||||
|
|
||||||
int element = ESM::MagicEffect::FireDamage;
|
int element = ESM::MagicEffect::FireDamage;
|
||||||
if (i == 1)
|
if (i == 1)
|
||||||
|
@ -444,7 +446,8 @@ namespace MWMechanics
|
||||||
MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager();
|
MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager();
|
||||||
if(isWerewolf)
|
if(isWerewolf)
|
||||||
{
|
{
|
||||||
const ESM::Sound *sound = store.get<ESM::Sound>().searchRandom("WolfHit");
|
auto& prng = MWBase::Environment::get().getWorld()->getPrng();
|
||||||
|
const ESM::Sound *sound = store.get<ESM::Sound>().searchRandom("WolfHit", prng);
|
||||||
if(sound)
|
if(sound)
|
||||||
sndMgr->playSound3D(victim, sound->mId, 1.0f, 1.0f);
|
sndMgr->playSound3D(victim, sound->mId, 1.0f, 1.0f);
|
||||||
}
|
}
|
||||||
|
|
|
@ -52,7 +52,8 @@ namespace MWMechanics
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
int x = static_cast<int>(fDiseaseXferChance * 100 * resist);
|
int x = static_cast<int>(fDiseaseXferChance * 100 * resist);
|
||||||
if (Misc::Rng::rollDice(10000) < x)
|
auto& prng = MWBase::Environment::get().getWorld()->getPrng();
|
||||||
|
if (Misc::Rng::rollDice(10000, prng) < x)
|
||||||
{
|
{
|
||||||
// Contracted disease!
|
// Contracted disease!
|
||||||
actor.getClass().getCreatureStats(actor).getSpells().add(spell);
|
actor.getClass().getCreatureStats(actor).getSpells().add(spell);
|
||||||
|
|
|
@ -76,7 +76,8 @@ namespace MWMechanics
|
||||||
|
|
||||||
if(mSelfEnchanting)
|
if(mSelfEnchanting)
|
||||||
{
|
{
|
||||||
if(getEnchantChance() <= (Misc::Rng::roll0to99()))
|
auto& prng = MWBase::Environment::get().getWorld()->getPrng();
|
||||||
|
if(getEnchantChance() <= (Misc::Rng::roll0to99(prng)))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
mEnchanter.getClass().skillUsageSucceeded (mEnchanter, ESM::Skill::Enchant, 2);
|
mEnchanter.getClass().skillUsageSucceeded (mEnchanter, ESM::Skill::Enchant, 2);
|
||||||
|
|
|
@ -19,7 +19,7 @@ namespace MWMechanics
|
||||||
{
|
{
|
||||||
|
|
||||||
/// @return ID of resulting item, or empty if none
|
/// @return ID of resulting item, or empty if none
|
||||||
inline std::string getLevelledItem (const ESM::LevelledListBase* levItem, bool creature, Misc::Rng::Generator& prng = Misc::Rng::getGenerator())
|
inline std::string getLevelledItem (const ESM::LevelledListBase* levItem, bool creature, Misc::Rng::Generator& prng)
|
||||||
{
|
{
|
||||||
const std::vector<ESM::LevelledListBase::LevelItem>& items = levItem->mList;
|
const std::vector<ESM::LevelledListBase::LevelItem>& items = levItem->mList;
|
||||||
|
|
||||||
|
|
|
@ -651,7 +651,8 @@ namespace MWMechanics
|
||||||
float x = 0;
|
float x = 0;
|
||||||
float y = 0;
|
float y = 0;
|
||||||
|
|
||||||
int roll = Misc::Rng::roll0to99();
|
auto& prng = MWBase::Environment::get().getWorld()->getPrng();
|
||||||
|
int roll = Misc::Rng::roll0to99(prng);
|
||||||
|
|
||||||
if (type == PT_Admire)
|
if (type == PT_Admire)
|
||||||
{
|
{
|
||||||
|
@ -1570,8 +1571,8 @@ namespace MWMechanics
|
||||||
}
|
}
|
||||||
|
|
||||||
float target = x - y;
|
float target = x - y;
|
||||||
|
auto& prng = MWBase::Environment::get().getWorld()->getPrng();
|
||||||
return (Misc::Rng::roll0to99() >= target);
|
return (Misc::Rng::roll0to99(prng) >= target);
|
||||||
}
|
}
|
||||||
|
|
||||||
void MechanicsManager::startCombat(const MWWorld::Ptr &ptr, const MWWorld::Ptr &target)
|
void MechanicsManager::startCombat(const MWWorld::Ptr &ptr, const MWWorld::Ptr &target)
|
||||||
|
|
|
@ -41,7 +41,8 @@ namespace MWMechanics
|
||||||
int iPickMaxChance = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>()
|
int iPickMaxChance = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>()
|
||||||
.find("iPickMaxChance")->mValue.getInteger();
|
.find("iPickMaxChance")->mValue.getInteger();
|
||||||
|
|
||||||
int roll = Misc::Rng::roll0to99();
|
auto& prng = MWBase::Environment::get().getWorld()->getPrng();
|
||||||
|
int roll = Misc::Rng::roll0to99(prng);
|
||||||
if (t < pcSneak / iPickMinChance)
|
if (t < pcSneak / iPickMinChance)
|
||||||
{
|
{
|
||||||
return (roll > int(pcSneak / iPickMinChance));
|
return (roll > int(pcSneak / iPickMinChance));
|
||||||
|
|
|
@ -49,7 +49,8 @@ bool rechargeItem(const MWWorld::Ptr &item, const MWWorld::Ptr &gem)
|
||||||
intelligenceTerm = 1;
|
intelligenceTerm = 1;
|
||||||
|
|
||||||
float x = (player.getClass().getSkill(player, ESM::Skill::Enchant) + intelligenceTerm + luckTerm) * stats.getFatigueTerm();
|
float x = (player.getClass().getSkill(player, ESM::Skill::Enchant) + intelligenceTerm + luckTerm) * stats.getFatigueTerm();
|
||||||
int roll = Misc::Rng::roll0to99();
|
auto& prng = MWBase::Environment::get().getWorld()->getPrng();
|
||||||
|
int roll = Misc::Rng::roll0to99(prng);
|
||||||
if (roll < x)
|
if (roll < x)
|
||||||
{
|
{
|
||||||
std::string soul = gem.getCellRef().getSoul();
|
std::string soul = gem.getCellRef().getSoul();
|
||||||
|
|
|
@ -44,7 +44,8 @@ void Repair::repair(const MWWorld::Ptr &itemToRepair)
|
||||||
|
|
||||||
float x = (0.1f * pcStrength + 0.1f * pcLuck + armorerSkill) * fatigueTerm;
|
float x = (0.1f * pcStrength + 0.1f * pcLuck + armorerSkill) * fatigueTerm;
|
||||||
|
|
||||||
int roll = Misc::Rng::roll0to99();
|
auto& prng = MWBase::Environment::get().getWorld()->getPrng();
|
||||||
|
int roll = Misc::Rng::roll0to99(prng);
|
||||||
if (roll <= x)
|
if (roll <= x)
|
||||||
{
|
{
|
||||||
int y = static_cast<int>(fRepairAmountMult * toolQuality * roll);
|
int y = static_cast<int>(fRepairAmountMult * toolQuality * roll);
|
||||||
|
|
|
@ -54,7 +54,8 @@ namespace MWMechanics
|
||||||
resultMessage = "#{sLockImpossible}";
|
resultMessage = "#{sLockImpossible}";
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (Misc::Rng::roll0to99() <= x)
|
auto& prng = MWBase::Environment::get().getWorld()->getPrng();
|
||||||
|
if (Misc::Rng::roll0to99(prng) <= x)
|
||||||
{
|
{
|
||||||
lock.getCellRef().unlock();
|
lock.getCellRef().unlock();
|
||||||
resultMessage = "#{sLockSuccess}";
|
resultMessage = "#{sLockSuccess}";
|
||||||
|
@ -98,7 +99,8 @@ namespace MWMechanics
|
||||||
resultMessage = "#{sTrapImpossible}";
|
resultMessage = "#{sTrapImpossible}";
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (Misc::Rng::roll0to99() <= x)
|
auto& prng = MWBase::Environment::get().getWorld()->getPrng();
|
||||||
|
if (Misc::Rng::roll0to99(prng) <= x)
|
||||||
{
|
{
|
||||||
trap.getCellRef().setTrap("");
|
trap.getCellRef().setTrap("");
|
||||||
|
|
||||||
|
|
|
@ -87,7 +87,8 @@ namespace MWMechanics
|
||||||
: ESM::MagicEffect::ResistBlightDisease;
|
: ESM::MagicEffect::ResistBlightDisease;
|
||||||
float x = target.getClass().getCreatureStats(target).getMagicEffects().get(requiredResistance).getMagnitude();
|
float x = target.getClass().getCreatureStats(target).getMagicEffects().get(requiredResistance).getMagnitude();
|
||||||
|
|
||||||
if (Misc::Rng::roll0to99() <= x)
|
auto& prng = MWBase::Environment::get().getWorld()->getPrng();
|
||||||
|
if (Misc::Rng::roll0to99(prng) <= x)
|
||||||
{
|
{
|
||||||
// Fully resisted, show message
|
// Fully resisted, show message
|
||||||
if (target == getPlayer())
|
if (target == getPlayer())
|
||||||
|
@ -339,7 +340,8 @@ namespace MWMechanics
|
||||||
|
|
||||||
// Check success
|
// Check success
|
||||||
float successChance = getSpellSuccessChance(spell, mCaster, nullptr, true, false);
|
float successChance = getSpellSuccessChance(spell, mCaster, nullptr, true, false);
|
||||||
if (Misc::Rng::roll0to99() >= successChance)
|
auto& prng = MWBase::Environment::get().getWorld()->getPrng();
|
||||||
|
if (Misc::Rng::roll0to99(prng) >= successChance)
|
||||||
{
|
{
|
||||||
if (mCaster == getPlayer())
|
if (mCaster == getPlayer())
|
||||||
MWBase::Environment::get().getWindowManager()->messageBox("#{sMagicSkillFail}");
|
MWBase::Environment::get().getWindowManager()->messageBox("#{sMagicSkillFail}");
|
||||||
|
@ -403,7 +405,8 @@ namespace MWMechanics
|
||||||
+ 0.1f * creatureStats.getAttribute (ESM::Attribute::Luck).getModified())
|
+ 0.1f * creatureStats.getAttribute (ESM::Attribute::Luck).getModified())
|
||||||
* creatureStats.getFatigueTerm();
|
* creatureStats.getFatigueTerm();
|
||||||
|
|
||||||
int roll = Misc::Rng::roll0to99();
|
auto& prng = MWBase::Environment::get().getWorld()->getPrng();
|
||||||
|
int roll = Misc::Rng::roll0to99(prng);
|
||||||
if (roll > x)
|
if (roll > x)
|
||||||
{
|
{
|
||||||
// "X has no effect on you"
|
// "X has no effect on you"
|
||||||
|
|
|
@ -35,7 +35,8 @@ namespace
|
||||||
{
|
{
|
||||||
if(effect.mMinMagnitude == effect.mMaxMagnitude)
|
if(effect.mMinMagnitude == effect.mMaxMagnitude)
|
||||||
return effect.mMinMagnitude;
|
return effect.mMinMagnitude;
|
||||||
return effect.mMinMagnitude + Misc::Rng::rollDice(effect.mMaxMagnitude - effect.mMinMagnitude + 1);
|
auto& prng = MWBase::Environment::get().getWorld()->getPrng();
|
||||||
|
return effect.mMinMagnitude + Misc::Rng::rollDice(effect.mMaxMagnitude - effect.mMinMagnitude + 1, prng);
|
||||||
}
|
}
|
||||||
|
|
||||||
void modifyAiSetting(const MWWorld::Ptr& target, const ESM::ActiveEffect& effect, ESM::MagicEffect::Effects creatureEffect, MWMechanics::CreatureStats::AiSetting setting, float magnitude, bool& invalid)
|
void modifyAiSetting(const MWWorld::Ptr& target, const ESM::ActiveEffect& effect, ESM::MagicEffect::Effects creatureEffect, MWMechanics::CreatureStats::AiSetting setting, float magnitude, bool& invalid)
|
||||||
|
@ -305,6 +306,7 @@ namespace
|
||||||
bool canAbsorb = !(effect.mFlags & ESM::ActiveEffect::Flag_Ignore_SpellAbsorption) && magnitudes.get(ESM::MagicEffect::SpellAbsorption).getMagnitude() > 0.f;
|
bool canAbsorb = !(effect.mFlags & ESM::ActiveEffect::Flag_Ignore_SpellAbsorption) && magnitudes.get(ESM::MagicEffect::SpellAbsorption).getMagnitude() > 0.f;
|
||||||
if(canReflect || canAbsorb)
|
if(canReflect || canAbsorb)
|
||||||
{
|
{
|
||||||
|
auto& prng = MWBase::Environment::get().getWorld()->getPrng();
|
||||||
for(const auto& activeParam : stats.getActiveSpells())
|
for(const auto& activeParam : stats.getActiveSpells())
|
||||||
{
|
{
|
||||||
for(const auto& activeEffect : activeParam.getEffects())
|
for(const auto& activeEffect : activeParam.getEffects())
|
||||||
|
@ -313,14 +315,14 @@ namespace
|
||||||
continue;
|
continue;
|
||||||
if(activeEffect.mEffectId == ESM::MagicEffect::Reflect)
|
if(activeEffect.mEffectId == ESM::MagicEffect::Reflect)
|
||||||
{
|
{
|
||||||
if(canReflect && Misc::Rng::roll0to99() < activeEffect.mMagnitude)
|
if(canReflect && Misc::Rng::roll0to99(prng) < activeEffect.mMagnitude)
|
||||||
{
|
{
|
||||||
return MWMechanics::MagicApplicationResult::REFLECTED;
|
return MWMechanics::MagicApplicationResult::REFLECTED;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if(activeEffect.mEffectId == ESM::MagicEffect::SpellAbsorption)
|
else if(activeEffect.mEffectId == ESM::MagicEffect::SpellAbsorption)
|
||||||
{
|
{
|
||||||
if(canAbsorb && Misc::Rng::roll0to99() < activeEffect.mMagnitude)
|
if(canAbsorb && Misc::Rng::roll0to99(prng) < activeEffect.mMagnitude)
|
||||||
{
|
{
|
||||||
absorbSpell(spellParams.getId(), caster, target);
|
absorbSpell(spellParams.getId(), caster, target);
|
||||||
return MWMechanics::MagicApplicationResult::REMOVED;
|
return MWMechanics::MagicApplicationResult::REMOVED;
|
||||||
|
@ -405,8 +407,11 @@ void applyMagicEffect(const MWWorld::Ptr& target, const MWWorld::Ptr& caster, co
|
||||||
if(params.getType() == ESM::ActiveSpells::Type_Temporary)
|
if(params.getType() == ESM::ActiveSpells::Type_Temporary)
|
||||||
{
|
{
|
||||||
const ESM::Spell* spell = MWBase::Environment::get().getWorld()->getStore().get<ESM::Spell>().search(params.getId());
|
const ESM::Spell* spell = MWBase::Environment::get().getWorld()->getStore().get<ESM::Spell>().search(params.getId());
|
||||||
if(spell && spell->mData.mType == ESM::Spell::ST_Spell)
|
if (spell && spell->mData.mType == ESM::Spell::ST_Spell)
|
||||||
return Misc::Rng::roll0to99() < magnitude;
|
{
|
||||||
|
auto& prng = MWBase::Environment::get().getWorld()->getPrng();
|
||||||
|
return Misc::Rng::roll0to99(prng) < magnitude;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}, target);
|
}, target);
|
||||||
|
|
|
@ -51,7 +51,8 @@ namespace MWMechanics
|
||||||
if (castChance > 0)
|
if (castChance > 0)
|
||||||
x *= 50 / castChance;
|
x *= 50 / castChance;
|
||||||
|
|
||||||
float roll = Misc::Rng::rollClosedProbability() * 100;
|
auto& prng = MWBase::Environment::get().getWorld()->getPrng();
|
||||||
|
float roll = Misc::Rng::rollClosedProbability(prng) * 100;
|
||||||
if (magicEffect->mData.mFlags & ESM::MagicEffect::NoMagnitude)
|
if (magicEffect->mData.mFlags & ESM::MagicEffect::NoMagnitude)
|
||||||
roll -= resistance;
|
roll -= resistance;
|
||||||
|
|
||||||
|
|
|
@ -57,7 +57,8 @@ namespace MWMechanics
|
||||||
+ gmst.find("fBargainOfferBase")->mValue.getFloat()
|
+ gmst.find("fBargainOfferBase")->mValue.getFloat()
|
||||||
+ int(pcTerm - npcTerm);
|
+ int(pcTerm - npcTerm);
|
||||||
|
|
||||||
int roll = Misc::Rng::rollDice(100) + 1;
|
auto& prng = MWBase::Environment::get().getWorld()->getPrng();
|
||||||
|
int roll = Misc::Rng::rollDice(100, prng) + 1;
|
||||||
|
|
||||||
// reject if roll fails
|
// reject if roll fails
|
||||||
// (or if player tries to buy things and get money)
|
// (or if player tries to buy things and get money)
|
||||||
|
|
|
@ -61,7 +61,8 @@ namespace
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
std::string itemId = MWMechanics::getLevelledItem(itemPtr.get<ESM::ItemLevList>()->mBase, false);
|
auto& prng = MWBase::Environment::get().getWorld()->getPrng();
|
||||||
|
std::string itemId = MWMechanics::getLevelledItem(itemPtr.get<ESM::ItemLevList>()->mBase, false, prng);
|
||||||
if (itemId.empty())
|
if (itemId.empty())
|
||||||
return;
|
return;
|
||||||
MWWorld::ManualRef manualRef(MWBase::Environment::get().getWorld()->getStore(), itemId, 1);
|
MWWorld::ManualRef manualRef(MWBase::Environment::get().getWorld()->getStore(), itemId, 1);
|
||||||
|
|
|
@ -111,7 +111,8 @@ namespace MWScript
|
||||||
throw std::runtime_error (
|
throw std::runtime_error (
|
||||||
"random: argument out of range (Don't be so negative!)");
|
"random: argument out of range (Don't be so negative!)");
|
||||||
|
|
||||||
runtime.push (static_cast<Interpreter::Type_Float>(::Misc::Rng::rollDice(limit))); // [o, limit)
|
auto& prng = MWBase::Environment::get().getWorld()->getPrng();
|
||||||
|
runtime.push (static_cast<Interpreter::Type_Float>(::Misc::Rng::rollDice(limit, prng))); // [o, limit)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -466,6 +466,7 @@ void MWState::StateManager::loadGame (const Character *character, const std::str
|
||||||
case ESM::REC_LEVI:
|
case ESM::REC_LEVI:
|
||||||
case ESM::REC_CREA:
|
case ESM::REC_CREA:
|
||||||
case ESM::REC_CONT:
|
case ESM::REC_CONT:
|
||||||
|
case ESM::REC_RAND:
|
||||||
MWBase::Environment::get().getWorld()->readRecord(reader, n.toInt(), contentFileMap);
|
MWBase::Environment::get().getWorld()->readRecord(reader, n.toInt(), contentFileMap);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
|
|
@ -333,7 +333,8 @@ namespace MWWorld
|
||||||
if(actor.getClass().isNpc() && actor.getClass().getNpcStats(actor).isWerewolf())
|
if(actor.getClass().isNpc() && actor.getClass().getNpcStats(actor).isWerewolf())
|
||||||
{
|
{
|
||||||
const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore();
|
const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore();
|
||||||
const ESM::Sound *sound = store.get<ESM::Sound>().searchRandom("WolfItem");
|
auto& prng = MWBase::Environment::get().getWorld()->getPrng();
|
||||||
|
const ESM::Sound *sound = store.get<ESM::Sound>().searchRandom("WolfItem", prng);
|
||||||
|
|
||||||
std::shared_ptr<MWWorld::Action> action(new MWWorld::FailedAction("#{sWerewolfRefusal}"));
|
std::shared_ptr<MWWorld::Action> action(new MWWorld::FailedAction("#{sWerewolfRefusal}"));
|
||||||
if(sound) action->setSound(sound->mId);
|
if(sound) action->setSound(sound->mId);
|
||||||
|
|
|
@ -221,7 +221,7 @@ namespace MWWorld
|
||||||
virtual bool stacks (const ConstPtr& ptr1, const ConstPtr& ptr2) const;
|
virtual bool stacks (const ConstPtr& ptr1, const ConstPtr& ptr2) const;
|
||||||
///< @return true if the two specified objects can stack with each other
|
///< @return true if the two specified objects can stack with each other
|
||||||
|
|
||||||
void fill (const ESM::InventoryList& items, const std::string& owner, Misc::Rng::Generator& seed = Misc::Rng::getGenerator());
|
void fill (const ESM::InventoryList& items, const std::string& owner, Misc::Rng::Generator& seed);
|
||||||
///< Insert items into *this.
|
///< Insert items into *this.
|
||||||
|
|
||||||
void fillNonRandom (const ESM::InventoryList& items, const std::string& owner, unsigned int seed);
|
void fillNonRandom (const ESM::InventoryList& items, const std::string& owner, unsigned int seed);
|
||||||
|
|
|
@ -125,7 +125,7 @@ namespace MWWorld
|
||||||
return (dit != mDynamic.end());
|
return (dit != mDynamic.end());
|
||||||
}
|
}
|
||||||
template<typename T>
|
template<typename T>
|
||||||
const T *Store<T>::searchRandom(const std::string &id) const
|
const T *Store<T>::searchRandom(const std::string &id, Misc::Rng::Generator& prng) const
|
||||||
{
|
{
|
||||||
std::vector<const T*> results;
|
std::vector<const T*> results;
|
||||||
std::copy_if(mShared.begin(), mShared.end(), std::back_inserter(results),
|
std::copy_if(mShared.begin(), mShared.end(), std::back_inserter(results),
|
||||||
|
@ -134,7 +134,7 @@ namespace MWWorld
|
||||||
return Misc::StringUtils::ciCompareLen(id, item->mId, id.size()) == 0;
|
return Misc::StringUtils::ciCompareLen(id, item->mId, id.size()) == 0;
|
||||||
});
|
});
|
||||||
if(!results.empty())
|
if(!results.empty())
|
||||||
return results[Misc::Rng::rollDice(results.size())];
|
return results[Misc::Rng::rollDice(results.size(), prng)];
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
template<typename T>
|
template<typename T>
|
||||||
|
|
|
@ -10,6 +10,7 @@
|
||||||
|
|
||||||
#include <components/esm/records.hpp>
|
#include <components/esm/records.hpp>
|
||||||
#include <components/misc/stringops.hpp>
|
#include <components/misc/stringops.hpp>
|
||||||
|
#include <components/misc/rng.hpp>
|
||||||
|
|
||||||
#include "../mwdialogue/keywordsearch.hpp"
|
#include "../mwdialogue/keywordsearch.hpp"
|
||||||
|
|
||||||
|
@ -183,7 +184,7 @@ namespace MWWorld
|
||||||
bool isDynamic(const std::string &id) const;
|
bool isDynamic(const std::string &id) const;
|
||||||
|
|
||||||
/** Returns a random record that starts with the named ID, or nullptr if not found. */
|
/** Returns a random record that starts with the named ID, or nullptr if not found. */
|
||||||
const T *searchRandom(const std::string &id) const;
|
const T *searchRandom(const std::string &id, Misc::Rng::Generator& prng) const;
|
||||||
|
|
||||||
// calls `search` and throws an exception if not found
|
// calls `search` and throws an exception if not found
|
||||||
const T *find(const std::string &id) const;
|
const T *find(const std::string &id) const;
|
||||||
|
|
|
@ -283,6 +283,9 @@ namespace MWWorld
|
||||||
|
|
||||||
MWBase::Environment::get().getWindowManager()->updatePlayer();
|
MWBase::Environment::get().getWindowManager()->updatePlayer();
|
||||||
mCurrentDate->setup(mGlobalVariables);
|
mCurrentDate->setup(mGlobalVariables);
|
||||||
|
|
||||||
|
// Initial seed.
|
||||||
|
mPrng.seed(mRandomSeed);
|
||||||
}
|
}
|
||||||
|
|
||||||
void World::clear()
|
void World::clear()
|
||||||
|
@ -338,6 +341,10 @@ namespace MWWorld
|
||||||
|
|
||||||
void World::write (ESM::ESMWriter& writer, Loading::Listener& progress) const
|
void World::write (ESM::ESMWriter& writer, Loading::Listener& progress) const
|
||||||
{
|
{
|
||||||
|
writer.startRecord(ESM::REC_RAND);
|
||||||
|
writer.writeHNOString("RAND", Misc::Rng::serialize(mPrng));
|
||||||
|
writer.endRecord(ESM::REC_RAND);
|
||||||
|
|
||||||
// Active cells could have a dirty fog of war, sync it to the CellStore first
|
// Active cells could have a dirty fog of war, sync it to the CellStore first
|
||||||
for (CellStore* cellstore : mWorldScene->getActiveCells())
|
for (CellStore* cellstore : mWorldScene->getActiveCells())
|
||||||
{
|
{
|
||||||
|
@ -376,6 +383,12 @@ namespace MWWorld
|
||||||
reader.getHNT(mTeleportEnabled, "TELE");
|
reader.getHNT(mTeleportEnabled, "TELE");
|
||||||
reader.getHNT(mLevitationEnabled, "LEVT");
|
reader.getHNT(mLevitationEnabled, "LEVT");
|
||||||
return;
|
return;
|
||||||
|
case ESM::REC_RAND:
|
||||||
|
{
|
||||||
|
auto data = reader.getHNOString("RAND");
|
||||||
|
Misc::Rng::deserialize(data, mPrng);
|
||||||
|
}
|
||||||
|
break;
|
||||||
case ESM::REC_PLAY:
|
case ESM::REC_PLAY:
|
||||||
mStore.checkPlayer();
|
mStore.checkPlayer();
|
||||||
mPlayer->readRecord(reader, type);
|
mPlayer->readRecord(reader, type);
|
||||||
|
@ -544,7 +557,12 @@ namespace MWWorld
|
||||||
mProjectileManager->clear();
|
mProjectileManager->clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
const ESM::Cell *World::getExterior (const std::string& cellName) const
|
void World::setRandomSeed(uint32_t seed)
|
||||||
|
{
|
||||||
|
mRandomSeed = seed;
|
||||||
|
}
|
||||||
|
|
||||||
|
const ESM::Cell* World::getExterior(const std::string& cellName) const
|
||||||
{
|
{
|
||||||
// first try named cells
|
// first try named cells
|
||||||
const ESM::Cell *cell = mStore.get<ESM::Cell>().searchExtByName (cellName);
|
const ESM::Cell *cell = mStore.get<ESM::Cell>().searchExtByName (cellName);
|
||||||
|
@ -3709,9 +3727,10 @@ namespace MWWorld
|
||||||
static int iNumberCreatures = mStore.get<ESM::GameSetting>().find("iNumberCreatures")->mValue.getInteger();
|
static int iNumberCreatures = mStore.get<ESM::GameSetting>().find("iNumberCreatures")->mValue.getInteger();
|
||||||
int numCreatures = 1 + Misc::Rng::rollDice(iNumberCreatures); // [1, iNumberCreatures]
|
int numCreatures = 1 + Misc::Rng::rollDice(iNumberCreatures); // [1, iNumberCreatures]
|
||||||
|
|
||||||
|
auto& prng = MWBase::Environment::get().getWorld()->getPrng();
|
||||||
for (int i=0; i<numCreatures; ++i)
|
for (int i=0; i<numCreatures; ++i)
|
||||||
{
|
{
|
||||||
std::string selectedCreature = MWMechanics::getLevelledItem(list, true);
|
std::string selectedCreature = MWMechanics::getLevelledItem(list, true, prng);
|
||||||
if (selectedCreature.empty())
|
if (selectedCreature.empty())
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
@ -3999,4 +4018,10 @@ namespace MWWorld
|
||||||
{
|
{
|
||||||
return mCells.getAll(id);
|
return mCells.getAll(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Misc::Rng::Generator& World::getPrng()
|
||||||
|
{
|
||||||
|
return mPrng;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
#include <osg/ref_ptr>
|
#include <osg/ref_ptr>
|
||||||
|
|
||||||
#include <components/settings/settings.hpp>
|
#include <components/settings/settings.hpp>
|
||||||
|
#include <components/misc/rng.hpp>
|
||||||
|
|
||||||
#include "../mwbase/world.hpp"
|
#include "../mwbase/world.hpp"
|
||||||
|
|
||||||
|
@ -84,7 +85,7 @@ namespace MWWorld
|
||||||
GroundcoverStore mGroundcoverStore;
|
GroundcoverStore mGroundcoverStore;
|
||||||
LocalScripts mLocalScripts;
|
LocalScripts mLocalScripts;
|
||||||
MWWorld::Globals mGlobalVariables;
|
MWWorld::Globals mGlobalVariables;
|
||||||
|
Misc::Rng::Generator mPrng;
|
||||||
Cells mCells;
|
Cells mCells;
|
||||||
|
|
||||||
std::string mCurrentWorldSpace;
|
std::string mCurrentWorldSpace;
|
||||||
|
@ -129,6 +130,8 @@ namespace MWWorld
|
||||||
std::map<MWWorld::Ptr, MWWorld::DoorState> mDoorStates;
|
std::map<MWWorld::Ptr, MWWorld::DoorState> mDoorStates;
|
||||||
///< only holds doors that are currently moving. 1 = opening, 2 = closing
|
///< only holds doors that are currently moving. 1 = opening, 2 = closing
|
||||||
|
|
||||||
|
uint32_t mRandomSeed{};
|
||||||
|
|
||||||
// not implemented
|
// not implemented
|
||||||
World (const World&);
|
World (const World&);
|
||||||
World& operator= (const World&);
|
World& operator= (const World&);
|
||||||
|
@ -193,6 +196,8 @@ namespace MWWorld
|
||||||
|
|
||||||
virtual ~World();
|
virtual ~World();
|
||||||
|
|
||||||
|
void setRandomSeed(uint32_t seed) override;
|
||||||
|
|
||||||
void startNewGame (bool bypass) override;
|
void startNewGame (bool bypass) override;
|
||||||
///< \param bypass Bypass regular game start.
|
///< \param bypass Bypass regular game start.
|
||||||
|
|
||||||
|
@ -736,6 +741,8 @@ namespace MWWorld
|
||||||
void reportStats(unsigned int frameNumber, osg::Stats& stats) const override;
|
void reportStats(unsigned int frameNumber, osg::Stats& stats) const override;
|
||||||
|
|
||||||
std::vector<MWWorld::Ptr> getAll(const std::string& id) override;
|
std::vector<MWWorld::Ptr> getAll(const std::string& id) override;
|
||||||
|
|
||||||
|
Misc::Rng::Generator& getPrng() override;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -820,12 +820,12 @@ namespace
|
||||||
|
|
||||||
const auto result = findRandomPointAroundCircle(*mNavigator, mAgentHalfExtents, mStart, 100.0, Flag_walk);
|
const auto result = findRandomPointAroundCircle(*mNavigator, mAgentHalfExtents, mStart, 100.0, Flag_walk);
|
||||||
|
|
||||||
ASSERT_THAT(result, Optional(Vec3fEq(69.6253509521484375, 531.29852294921875, -2.6667339801788330078125)))
|
ASSERT_THAT(result, Optional(Vec3fEq(70.35845947265625, 335.592041015625, -2.6667339801788330078125)))
|
||||||
<< (result ? *result : osg::Vec3f());
|
<< (result ? *result : osg::Vec3f());
|
||||||
|
|
||||||
const auto distance = (*result - mStart).length();
|
const auto distance = (*result - mStart).length();
|
||||||
|
|
||||||
EXPECT_FLOAT_EQ(distance, 73.536231994628906) << distance;
|
EXPECT_FLOAT_EQ(distance, 125.80865478515625) << distance;
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(DetourNavigatorNavigatorTest, multiple_threads_should_lock_tiles)
|
TEST_F(DetourNavigatorNavigatorTest, multiple_threads_should_lock_tiles)
|
||||||
|
|
|
@ -178,6 +178,9 @@ enum RecNameInts : unsigned int
|
||||||
|
|
||||||
// format 16 - Lua scripts in saved games
|
// format 16 - Lua scripts in saved games
|
||||||
REC_LUAM = fourCC("LUAM"), // LuaManager data
|
REC_LUAM = fourCC("LUAM"), // LuaManager data
|
||||||
|
|
||||||
|
// format 21 - Random state in saved games.
|
||||||
|
REC_RAND = fourCC("RAND"), // Random state.
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Common subrecords
|
/// Common subrecords
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
#include "esmwriter.hpp"
|
#include "esmwriter.hpp"
|
||||||
|
|
||||||
unsigned int ESM::SavedGame::sRecordId = ESM::REC_SAVE;
|
unsigned int ESM::SavedGame::sRecordId = ESM::REC_SAVE;
|
||||||
int ESM::SavedGame::sCurrentFormat = 20;
|
int ESM::SavedGame::sCurrentFormat = 21;
|
||||||
|
|
||||||
void ESM::SavedGame::load (ESMReader &esm)
|
void ESM::SavedGame::load (ESMReader &esm)
|
||||||
{
|
{
|
||||||
|
|
|
@ -2,6 +2,9 @@
|
||||||
|
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
#include <random>
|
#include <random>
|
||||||
|
#include <sstream>
|
||||||
|
|
||||||
|
#include <components/debug/debuglog.hpp>
|
||||||
|
|
||||||
namespace Misc::Rng
|
namespace Misc::Rng
|
||||||
{
|
{
|
||||||
|
@ -12,33 +15,72 @@ namespace Misc::Rng
|
||||||
return sGenerator;
|
return sGenerator;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string serialize(const Generator& prng)
|
||||||
|
{
|
||||||
|
std::stringstream ss;
|
||||||
|
ss << prng;
|
||||||
|
|
||||||
|
return ss.str();
|
||||||
|
}
|
||||||
|
|
||||||
|
void deserialize(std::string_view data, Generator& prng)
|
||||||
|
{
|
||||||
|
std::stringstream ss;
|
||||||
|
ss << data;
|
||||||
|
|
||||||
|
ss.seekg(0);
|
||||||
|
ss >> prng;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned int generateDefaultSeed()
|
||||||
|
{
|
||||||
|
auto res = static_cast<unsigned int>(std::chrono::high_resolution_clock::now().time_since_epoch().count());
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
void init(unsigned int seed)
|
void init(unsigned int seed)
|
||||||
{
|
{
|
||||||
sGenerator.seed(seed);
|
sGenerator.seed(seed);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
float rollProbability()
|
||||||
|
{
|
||||||
|
return std::uniform_real_distribution<float>(0, 1 - std::numeric_limits<float>::epsilon())(getGenerator());
|
||||||
|
}
|
||||||
|
|
||||||
float rollProbability(Generator& prng)
|
float rollProbability(Generator& prng)
|
||||||
{
|
{
|
||||||
return std::uniform_real_distribution<float>(0, 1 - std::numeric_limits<float>::epsilon())(prng);
|
return std::uniform_real_distribution<float>(0, 1 - std::numeric_limits<float>::epsilon())(prng);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
float rollClosedProbability()
|
||||||
|
{
|
||||||
|
return std::uniform_real_distribution<float>(0, 1)(getGenerator());
|
||||||
|
}
|
||||||
|
|
||||||
float rollClosedProbability(Generator& prng)
|
float rollClosedProbability(Generator& prng)
|
||||||
{
|
{
|
||||||
return std::uniform_real_distribution<float>(0, 1)(prng);
|
return std::uniform_real_distribution<float>(0, 1)(prng);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int rollDice(int max)
|
||||||
|
{
|
||||||
|
return max > 0 ? std::uniform_int_distribution<int>(0, max - 1)(getGenerator()) : 0;
|
||||||
|
}
|
||||||
|
|
||||||
int rollDice(int max, Generator& prng)
|
int rollDice(int max, Generator& prng)
|
||||||
{
|
{
|
||||||
return max > 0 ? std::uniform_int_distribution<int>(0, max - 1)(prng) : 0;
|
return max > 0 ? std::uniform_int_distribution<int>(0, max - 1)(prng) : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned int generateDefaultSeed()
|
float deviate(float mean, float deviation)
|
||||||
{
|
{
|
||||||
return static_cast<unsigned int>(std::chrono::high_resolution_clock::now().time_since_epoch().count());
|
return std::uniform_real_distribution<float>(mean - deviation, mean + deviation)(getGenerator());
|
||||||
}
|
}
|
||||||
|
|
||||||
float deviate(float mean, float deviation, Generator& prng)
|
float deviate(float mean, float deviation, Generator& prng)
|
||||||
{
|
{
|
||||||
return std::uniform_real_distribution<float>(mean - deviation, mean + deviation)(prng);
|
return std::uniform_real_distribution<float>(mean - deviation, mean + deviation)(prng);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,17 +3,21 @@
|
||||||
|
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
#include <random>
|
#include <random>
|
||||||
|
#include <string_view>
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Provides central implementation of the RNG logic
|
Provides central implementation of the RNG logic
|
||||||
*/
|
*/
|
||||||
namespace Misc::Rng
|
namespace Misc::Rng
|
||||||
{
|
{
|
||||||
|
/// The use of a rather minimalistic prng is preferred to avoid saving a lot of state in the save game.
|
||||||
using Generator = std::mt19937;
|
using Generator = std::minstd_rand;
|
||||||
|
|
||||||
Generator& getGenerator();
|
Generator& getGenerator();
|
||||||
|
|
||||||
|
std::string serialize(const Generator& prng);
|
||||||
|
void deserialize(std::string_view data, Generator& prng);
|
||||||
|
|
||||||
/// returns default seed for RNG
|
/// returns default seed for RNG
|
||||||
unsigned int generateDefaultSeed();
|
unsigned int generateDefaultSeed();
|
||||||
|
|
||||||
|
@ -21,19 +25,23 @@ namespace Misc::Rng
|
||||||
void init(unsigned int seed = generateDefaultSeed());
|
void init(unsigned int seed = generateDefaultSeed());
|
||||||
|
|
||||||
/// return value in range [0.0f, 1.0f) <- note open upper range.
|
/// return value in range [0.0f, 1.0f) <- note open upper range.
|
||||||
float rollProbability(Generator& prng = getGenerator());
|
float rollProbability();
|
||||||
|
float rollProbability(Generator& prng);
|
||||||
|
|
||||||
/// return value in range [0.0f, 1.0f] <- note closed upper range.
|
/// return value in range [0.0f, 1.0f] <- note closed upper range.
|
||||||
float rollClosedProbability(Generator& prng = getGenerator());
|
float rollClosedProbability();
|
||||||
|
float rollClosedProbability(Generator& prng);
|
||||||
|
|
||||||
/// return value in range [0, max) <- note open upper range.
|
/// return value in range [0, max) <- note open upper range.
|
||||||
int rollDice(int max, Generator& prng = getGenerator());
|
int rollDice(int max);
|
||||||
|
int rollDice(int max, Generator& prng);
|
||||||
|
|
||||||
/// return value in range [0, 99]
|
/// return value in range [0, 99]
|
||||||
inline int roll0to99(Generator& prng = getGenerator()) { return rollDice(100, prng); }
|
inline int roll0to99(Generator& prng) { return rollDice(100, prng); }
|
||||||
|
inline int roll0to99() { return rollDice(100); }
|
||||||
float deviate(float mean, float deviation, Generator& prng = getGenerator());
|
|
||||||
|
|
||||||
|
float deviate(float mean, float deviation);
|
||||||
|
float deviate(float mean, float deviation, Generator& prng);
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -18,7 +18,7 @@ namespace Misc
|
||||||
: mPeriod(period), mDeviation(deviation), mTimeLeft(timeLeft)
|
: mPeriod(period), mDeviation(deviation), mTimeLeft(timeLeft)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
TimerStatus update(float duration)
|
TimerStatus update(float duration, Rng::Generator& prng)
|
||||||
{
|
{
|
||||||
if (mTimeLeft > 0)
|
if (mTimeLeft > 0)
|
||||||
{
|
{
|
||||||
|
@ -26,7 +26,7 @@ namespace Misc
|
||||||
return TimerStatus::Waiting;
|
return TimerStatus::Waiting;
|
||||||
}
|
}
|
||||||
|
|
||||||
mTimeLeft = Rng::deviate(mPeriod, mDeviation);
|
mTimeLeft = Rng::deviate(mPeriod, mDeviation, prng);
|
||||||
return TimerStatus::Elapsed;
|
return TimerStatus::Elapsed;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue