1
0
Fork 0

FastRandom rewrite (#3754)

This commit is contained in:
peterbell10 2017-06-13 20:35:30 +01:00 committed by Lukas Pioch
parent 9b0eb118b3
commit 360d8eade0
63 changed files with 467 additions and 417 deletions

View File

@ -174,7 +174,7 @@ void cDropSpenserEntity::DropFromSlot(cChunk & a_Chunk, int a_SlotNum)
cItems Pickups;
Pickups.push_back(m_Contents.RemoveOneItem(a_SlotNum));
const int PickupSpeed = m_World->GetTickRandomNumber(4) + 2; // At least 2, at most 6
const int PickupSpeed = GetRandomProvider().RandInt(2, 6); // At least 2, at most 6
int PickupSpeedX = 0, PickupSpeedY = 0, PickupSpeedZ = 0;
switch (Meta & E_META_DROPSPENSER_FACING_MASK)
{

View File

@ -107,7 +107,7 @@ bool cMobSpawnerEntity::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
void cMobSpawnerEntity::ResetTimer(void)
{
m_SpawnDelay = static_cast<short>(200 + m_World->GetTickRandomNumber(600));
m_SpawnDelay = GetRandomProvider().RandInt<short>(200, 800);
m_World->BroadcastBlockEntity(m_PosX, m_PosY, m_PosZ);
}
@ -138,7 +138,7 @@ void cMobSpawnerEntity::SpawnEntity(void)
virtual bool Item(cChunk * a_Chunk)
{
cFastRandom Random;
auto & Random = GetRandomProvider();
bool EntitiesSpawned = false;
for (size_t i = 0; i < 4; i++)
@ -148,9 +148,9 @@ void cMobSpawnerEntity::SpawnEntity(void)
break;
}
int RelX = static_cast<int>(m_RelX + static_cast<double>(Random.NextFloat() - Random.NextFloat()) * 4.0);
int RelY = m_RelY + Random.NextInt(3) - 1;
int RelZ = static_cast<int>(m_RelZ + static_cast<double>(Random.NextFloat() - Random.NextFloat()) * 4.0);
int RelX = m_RelX + static_cast<int>((Random.RandReal<double>() - Random.RandReal<double>()) * 4.0);
int RelY = m_RelY + Random.RandInt(-1, 1);
int RelZ = m_RelZ + static_cast<int>((Random.RandReal<double>() - Random.RandReal<double>()) * 4.0);
cChunk * Chunk = a_Chunk->GetRelNeighborChunkAdjustCoords(RelX, RelZ);
if ((Chunk == nullptr) || !Chunk->IsValid())
@ -171,7 +171,7 @@ void cMobSpawnerEntity::SpawnEntity(void)
}
Monster->SetPosition(PosX, RelY, PosZ);
Monster->SetYaw(Random.NextFloat() * 360.0f);
Monster->SetYaw(Random.RandReal(360.0f));
if (Chunk->GetWorld()->SpawnMobFinalize(Monster) != cEntity::INVALID_ID)
{
EntitiesSpawned = true;

View File

@ -67,8 +67,7 @@ public:
)
)
{
MTRand r1;
if (r1.randInt(10) == 5)
if (GetRandomProvider().RandBool(0.10))
{
cItems Pickups;
if (FlowerMeta == E_META_BIG_FLOWER_DOUBLE_TALL_GRASS)

View File

@ -30,9 +30,7 @@ public:
virtual void OnUpdate(cChunkInterface & cChunkInterface, cWorldInterface & a_WorldInterface, cBlockPluginInterface & a_PluginInterface, cChunk & a_Chunk, int a_RelX, int a_RelY, int a_RelZ) override
{
cFastRandom Random;
if (Random.NextInt(5) == 0)
if (GetRandomProvider().RandBool(0.20))
{
NIBBLETYPE Meta = a_Chunk.GetMeta(a_RelX, a_RelY, a_RelZ);
NIBBLETYPE TypeMeta = Meta & 0x03;

View File

@ -26,7 +26,7 @@ public:
virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_Meta) override
{
cFastRandom rand;
auto & rand = GetRandomProvider();
// If not fully grown, drop the "seed" of whatever is growing:
if (a_Meta < RipeMeta)
@ -51,30 +51,30 @@ public:
{
case E_BLOCK_BEETROOTS:
{
char SeedCount = static_cast<char>(1 + (rand.NextInt(3) + rand.NextInt(3)) / 2); // [1 .. 3] with high preference of 2
a_Pickups.push_back(cItem(E_ITEM_BEETROOT_SEEDS, SeedCount, 0));
char BeetrootCount = static_cast<char>(1 + (rand.NextInt(3) + rand.NextInt(3)) / 2); // [1 .. 3] with high preference of 2
a_Pickups.push_back(cItem(E_ITEM_BEETROOT, BeetrootCount, 0));
char SeedCount = 1 + ((rand.RandInt<char>(2) + rand.RandInt<char>(2)) / 2); // [1 .. 3] with high preference of 2
a_Pickups.emplace_back(E_ITEM_BEETROOT_SEEDS, SeedCount, 0);
char BeetrootCount = 1 + ((rand.RandInt<char>(2) + rand.RandInt<char>(2)) / 2); // [1 .. 3] with high preference of 2
a_Pickups.emplace_back(E_ITEM_BEETROOT, BeetrootCount, 0);
break;
}
case E_BLOCK_CROPS:
{
a_Pickups.push_back(cItem(E_ITEM_WHEAT, 1, 0));
a_Pickups.push_back(cItem(E_ITEM_SEEDS, static_cast<char>(1 + (rand.NextInt(3) + rand.NextInt(3)) / 2), 0)); // [1 .. 3] with high preference of 2
a_Pickups.emplace_back(E_ITEM_WHEAT, 1, 0);
a_Pickups.emplace_back(E_ITEM_SEEDS, 1 + ((rand.RandInt<char>(2) + rand.RandInt<char>(2)) / 2), 0); // [1 .. 3] with high preference of 2
break;
}
case E_BLOCK_CARROTS:
{
a_Pickups.push_back(cItem(E_ITEM_CARROT, static_cast<char>(1 + (rand.NextInt(3) + rand.NextInt(3)) / 2), 0)); // [1 .. 3] with high preference of 2
a_Pickups.emplace_back(E_ITEM_CARROT, 1 + ((rand.RandInt<char>(2) + rand.RandInt<char>(2)) / 2), 0); // [1 .. 3] with high preference of 2
break;
}
case E_BLOCK_POTATOES:
{
a_Pickups.push_back(cItem(E_ITEM_POTATO, static_cast<char>(1 + (rand.NextInt(3) + rand.NextInt(3)) / 2), 0)); // [1 .. 3] with high preference of 2
if (rand.NextInt(21) == 0)
a_Pickups.emplace_back(E_ITEM_POTATO, 1 + ((rand.RandInt<char>(2) + rand.RandInt<char>(2)) / 2), 0); // [1 .. 3] with high preference of 2
if (rand.RandBool(0.05))
{
// With a 5% chance, drop a poisonous potato as well
a_Pickups.push_back(cItem(E_ITEM_POISONOUS_POTATO, 1, 0));
a_Pickups.emplace_back(E_ITEM_POISONOUS_POTATO, 1, 0);
}
break;
}

View File

@ -46,11 +46,10 @@ public:
virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override
{
// Drop 0-3 sticks
cFastRandom random;
int chance = random.NextInt(3);
char chance = GetRandomProvider().RandInt<char>(3);
if (chance != 0)
{
a_Pickups.push_back(cItem(E_ITEM_STICK, static_cast<char>(chance), 0));
a_Pickups.emplace_back(E_ITEM_STICK, chance, 0);
}
}
@ -74,7 +73,7 @@ public:
// Spawn the pickups:
if (!Drops.empty())
{
MTRand r1;
auto & r1 = GetRandomProvider();
// Mid-block position first
double MicroX, MicroY, MicroZ;
@ -83,8 +82,8 @@ public:
MicroZ = a_BlockZ + 0.5;
// Add random offset second
MicroX += r1.rand(1) - 0.5;
MicroZ += r1.rand(1) - 0.5;
MicroX += r1.RandReal<double>(-0.5, 0.5);
MicroZ += r1.RandReal<double>(-0.5, 0.5);
a_WorldInterface.SpawnItemPickups(Drops, MicroX, MicroY, MicroZ);
}

View File

@ -70,12 +70,12 @@ public:
}
// Grass spreads to adjacent dirt blocks:
cFastRandom rand;
auto & rand = GetRandomProvider();
for (int i = 0; i < 2; i++) // Pick two blocks to grow to
{
int OfsX = rand.NextInt(3) - 1; // [-1 .. 1]
int OfsY = rand.NextInt(5) - 3; // [-3 .. 1]
int OfsZ = rand.NextInt(3) - 1; // [-1 .. 1]
int OfsX = rand.RandInt(-1, 1);
int OfsY = rand.RandInt(-3, 1);
int OfsZ = rand.RandInt(-1, 1);
BLOCKTYPE DestBlock;
NIBBLETYPE DestMeta;

View File

@ -18,10 +18,8 @@ public:
virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override
{
cFastRandom Random;
// Add more than one dust
a_Pickups.push_back(cItem(E_ITEM_GLOWSTONE_DUST, static_cast<char>(2 + Random.NextInt(3)), 0));
a_Pickups.emplace_back(E_ITEM_GLOWSTONE_DUST, GetRandomProvider().RandInt<char>(2, 4), 0);
}
virtual ColourID GetMapBaseColourID(NIBBLETYPE a_Meta) override

View File

@ -18,8 +18,7 @@ public:
virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override
{
cFastRandom Random;
if (Random.NextInt(10) == 0)
if (GetRandomProvider().RandBool(0.10))
{
a_Pickups.Add(E_ITEM_FLINT, 1, 0);
}

View File

@ -526,7 +526,7 @@ void cBlockHandler::DropBlock(cChunkInterface & a_ChunkInterface, cWorldInterfac
if (!Pickups.empty())
{
MTRand r1;
auto & r1 = GetRandomProvider();
// Mid-block position first
double MicroX, MicroY, MicroZ;
@ -535,8 +535,8 @@ void cBlockHandler::DropBlock(cChunkInterface & a_ChunkInterface, cWorldInterfac
MicroZ = a_BlockZ + 0.5;
// Add random offset second
MicroX += r1.rand(1) - 0.5;
MicroZ += r1.rand(1) - 0.5;
MicroX += r1.RandReal<double>(-0.5, 0.5);
MicroZ += r1.RandReal<double>(-0.5, 0.5);
a_WorldInterface.SpawnItemPickups(Pickups, MicroX, MicroY, MicroZ);
}

View File

@ -37,22 +37,22 @@ public:
virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override
{
cFastRandom rand;
auto & rand = GetRandomProvider();
// There is a chance to drop a sapling that varies depending on the type of leaf broken.
// TODO: Take into account fortune for sapling drops.
int chance;
double chance = 0.0;
if ((m_BlockType == E_BLOCK_LEAVES) && ((a_BlockMeta & 0x03) == E_META_LEAVES_JUNGLE))
{
// Jungle leaves have a 2.5% chance of dropping a sapling.
chance = rand.NextInt(40);
chance = 0.025;
}
else
{
// Other leaves have a 5% chance of dropping a sapling.
chance = rand.NextInt(20);
chance = 0.05;
}
if (chance == 0)
if (rand.RandBool(chance))
{
a_Pickups.push_back(
cItem(
@ -66,7 +66,7 @@ public:
// 0.5 % chance of dropping an apple, if the leaves' type is Apple Leaves
if ((m_BlockType == E_BLOCK_LEAVES) && ((a_BlockMeta & 0x03) == E_META_LEAVES_APPLE))
{
if (rand.NextInt(200) == 0)
if (rand.RandBool(0.005))
{
a_Pickups.push_back(cItem(E_ITEM_RED_APPLE, 1, 0));
}

View File

@ -18,8 +18,7 @@ public:
virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override
{
cFastRandom Random;
a_Pickups.push_back(cItem(E_ITEM_MELON_SLICE, static_cast<char>(3 + Random.NextInt(5)), 0));
a_Pickups.emplace_back(E_ITEM_MELON_SLICE, GetRandomProvider().RandInt<char>(3, 7), 0);
}
virtual ColourID GetMapBaseColourID(NIBBLETYPE a_Meta) override

View File

@ -42,7 +42,7 @@ public:
cItems Pickups;
Pickups.Add(E_ITEM_HEAD, 1, static_cast<short>(MobHeadEntity->GetType()));
MTRand r1;
auto & r1 = GetRandomProvider();
// Mid-block position first
double MicroX, MicroY, MicroZ;
@ -51,8 +51,8 @@ public:
MicroZ = MobHeadEntity->GetPosZ() + 0.5;
// Add random offset second
MicroX += r1.rand(1) - 0.5;
MicroZ += r1.rand(1) - 0.5;
MicroX += r1.RandReal<double>(-0.5, 0.5);
MicroZ += r1.RandReal<double>(-0.5, 0.5);
MobHeadEntity->GetWorld()->SpawnItemPickups(Pickups, MicroX, MicroY, MicroZ);
return false;

View File

@ -45,8 +45,8 @@ public:
return;
}
cFastRandom Random;
int Reward = 15 + Random.NextInt(15) + Random.NextInt(15);
auto & Random = GetRandomProvider();
int Reward = 15 + Random.RandInt(14) + Random.RandInt(14);
a_WorldInterface.SpawnExperienceOrb(static_cast<double>(a_BlockX), static_cast<double>(a_BlockY + 1), static_cast<double>(a_BlockZ), Reward);
}
} ;

View File

@ -21,12 +21,12 @@ public:
virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_Meta) override
{
cFastRandom rand;
auto & rand = GetRandomProvider();
if (a_Meta == 0x3)
{
// Fully grown, drop the entire produce:
a_Pickups.push_back(cItem(E_ITEM_NETHER_WART, static_cast<char>(1 + (rand.NextInt(3) + rand.NextInt(3))) / 2, 0));
a_Pickups.emplace_back(E_ITEM_NETHER_WART, 1 + (rand.RandInt<char>(2) + rand.RandInt<char>(2)) / 2, 0);
}
else
{

View File

@ -20,19 +20,19 @@ public:
virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override
{
cFastRandom Random;
auto & Random = GetRandomProvider();
switch (m_BlockType)
{
case E_BLOCK_LAPIS_ORE:
{
a_Pickups.push_back(cItem(E_ITEM_DYE, static_cast<char>(4 + Random.NextInt(5)), 4));
a_Pickups.emplace_back(E_ITEM_DYE, Random.RandInt<char>(4, 8), 4);
break;
}
case E_BLOCK_REDSTONE_ORE:
case E_BLOCK_REDSTONE_ORE_GLOWING:
{
a_Pickups.push_back(cItem(E_ITEM_REDSTONE_DUST, static_cast<char>(4 + Random.NextInt(2)), 0));
a_Pickups.emplace_back(E_ITEM_REDSTONE_DUST, Random.RandInt<char>(4, 5), 0);
break;
}
case E_BLOCK_DIAMOND_ORE:
@ -84,7 +84,7 @@ public:
return;
}
cFastRandom Random;
auto & Random = GetRandomProvider();
int Reward = 0;
switch (m_BlockType)
@ -93,27 +93,27 @@ public:
case E_BLOCK_LAPIS_ORE:
{
// Lapis and nether quartz get 2 - 5 experience
Reward = Random.NextInt(4) + 2;
Reward = Random.RandInt(2, 5);
break;
}
case E_BLOCK_REDSTONE_ORE:
case E_BLOCK_REDSTONE_ORE_GLOWING:
{
// Redstone gets 1 - 5 experience
Reward = Random.NextInt(5) + 1;
Reward = Random.RandInt(1, 5);
break;
}
case E_BLOCK_DIAMOND_ORE:
case E_BLOCK_EMERALD_ORE:
{
// Diamond and emerald get 3 - 7 experience
Reward = Random.NextInt(5) + 3;
Reward = Random.RandInt(3, 7);
break;
}
case E_BLOCK_COAL_ORE:
{
// Coal gets 0 - 2 experience
Reward = Random.NextInt(3);
Reward = Random.RandInt(2);
break;
}

View File

@ -75,10 +75,9 @@ protected:
*/
virtual PlantAction CanGrow(cChunk & a_Chunk, int a_RelX, int a_RelY, int a_RelZ)
{
cFastRandom rand;
// Plant can grow if it has the required amount of light, and it passes a random chance based on surrounding blocks
PlantAction Action = HasEnoughLight(a_Chunk, a_RelX, a_RelY, a_RelZ);
if ((Action == paGrowth) && (rand.NextInt(GetGrowthChance(a_Chunk, a_RelX, a_RelY, a_RelZ)) != 0))
if ((Action == paGrowth) && !GetRandomProvider().RandBool(1.0 / GetGrowthChance(a_Chunk, a_RelX, a_RelY, a_RelZ)))
{
Action = paStay;
}

View File

@ -40,8 +40,7 @@ public:
virtual void OnUpdate(cChunkInterface & cChunkInterface, cWorldInterface & a_WorldInterface, cBlockPluginInterface & a_PluginInterface, cChunk & a_Chunk, int a_RelX, int a_RelY, int a_RelZ) override
{
cFastRandom Random;
if (Random.NextInt(2000) != 0)
if (GetRandomProvider().RandBool(0.9995))
{
return;
}

View File

@ -37,16 +37,16 @@ public:
// Only grow if we have the right amount of light
if (Light > 8)
{
cFastRandom random;
auto & random = GetRandomProvider();
// Only grow if we are in the right growth stage and have the right amount of space around them.
if (((Meta & 0x08) != 0) && (random.NextInt(99) < 45) && CanGrowAt(a_Chunk, a_RelX, a_RelY, a_RelZ, Meta))
if (((Meta & 0x08) != 0) && random.RandBool(0.45) && CanGrowAt(a_Chunk, a_RelX, a_RelY, a_RelZ, Meta))
{
int BlockX = a_RelX + a_Chunk.GetPosX() * cChunkDef::Width;
int BlockZ = a_RelZ + a_Chunk.GetPosZ() * cChunkDef::Width;
a_Chunk.GetWorld()->GrowTree(BlockX, a_RelY, BlockZ);
}
// Only move to the next growth stage if we haven't gone there yet
else if (((Meta & 0x08) == 0) && (random.NextInt(99) < 45))
else if (((Meta & 0x08) == 0) && random.RandBool(0.45))
{
a_Chunk.SetMeta(a_RelX, a_RelY, a_RelZ, Meta | 0x08);
}

View File

@ -20,8 +20,7 @@ public:
virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override
{
// Reset meta to 0
cFastRandom Random;
a_Pickups.push_back(cItem(E_ITEM_PRISMARINE_CRYSTALS, static_cast<char>(2 + Random.NextInt(2)), 0));
a_Pickups.emplace_back(E_ITEM_PRISMARINE_CRYSTALS, GetRandomProvider().RandInt<char>(2, 3), 0);
}
} ;

View File

@ -26,8 +26,7 @@ public:
virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override
{
// Drop seeds, sometimes
cFastRandom Random;
if (Random.NextInt(8) == 0)
if (GetRandomProvider().RandBool(0.125))
{
a_Pickups.push_back(cItem(E_ITEM_SEEDS, 1, 0));
}
@ -47,7 +46,7 @@ public:
// Spawn the pickups:
if (!Drops.empty())
{
MTRand r1;
auto & r1 = GetRandomProvider();
// Mid-block position first
double MicroX, MicroY, MicroZ;
@ -56,8 +55,8 @@ public:
MicroZ = a_BlockZ + 0.5;
// Add random offset second
MicroX += r1.rand(1) - 0.5;
MicroZ += r1.rand(1) - 0.5;
MicroX += r1.RandReal<double>(-0.5, 0.5);
MicroZ += r1.RandReal<double>(-0.5, 0.5);
a_WorldInterface.SpawnItemPickups(Drops, MicroX, MicroY, MicroZ);
}

View File

@ -507,14 +507,19 @@ void cChunk::CollectMobCensus(cMobCensus & toFill)
void cChunk::GetThreeRandomNumbers(int & a_X, int & a_Y, int & a_Z, int a_MaxX, int a_MaxY, int a_MaxZ)
{
ASSERT(a_MaxX * a_MaxY * a_MaxZ * 8 < 0x00ffffff);
int Random = m_World->GetTickRandomNumber(0x00ffffff);
a_X = Random % (a_MaxX * 2);
a_Y = (Random / (a_MaxX * 2)) % (a_MaxY * 2);
a_Z = ((Random / (a_MaxX * 2)) / (a_MaxY * 2)) % (a_MaxZ * 2);
a_X /= 2;
a_Y /= 2;
a_Z /= 2;
ASSERT(
(a_MaxX > 0) && (a_MaxY > 0) && (a_MaxZ > 0) &&
(a_MaxX <= std::numeric_limits<int>::max() / a_MaxY) && // a_MaxX * a_MaxY doesn't overflow
(a_MaxX * a_MaxY <= std::numeric_limits<int>::max() / a_MaxZ) // a_MaxX * a_MaxY * a_MaxZ doesn't overflow
);
// MTRand gives an inclusive range [0, Max] but this gives the exclusive range [0, Max)
int OverallMax = (a_MaxX - 1) * (a_MaxY - 1) * (a_MaxZ - 1);
int Random = m_World->GetTickRandomNumber(OverallMax);
a_X = Random % a_MaxX;
a_Y = (Random / a_MaxX) % a_MaxY;
a_Z = ((Random / a_MaxX) / a_MaxY) % a_MaxZ;
}
@ -848,7 +853,7 @@ void cChunk::TickBlocks(void)
void cChunk::ApplyWeatherToTop()
{
if (
(m_World->GetTickRandomNumber(100) != 0) ||
(GetRandomProvider().RandBool(0.99)) ||
(
(m_World->GetWeather() != eWeather_Rain) &&
(m_World->GetWeather() != eWeather_ThunderStorm)
@ -932,8 +937,10 @@ void cChunk::ApplyWeatherToTop()
bool cChunk::GrowMelonPumpkin(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_BlockType, MTRand & a_TickRandom)
bool cChunk::GrowMelonPumpkin(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_BlockType)
{
auto & Random = GetRandomProvider();
// Convert the stem BlockType into produce BlockType
BLOCKTYPE ProduceType;
switch (a_BlockType)
@ -969,7 +976,7 @@ bool cChunk::GrowMelonPumpkin(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_Bl
// Pick a direction in which to place the produce:
int x = 0, z = 0;
int CheckType = a_TickRandom.randInt(3); // The index to the neighbors array which should be checked for emptiness
int CheckType = Random.RandInt(3); // The index to the neighbors array which should be checked for emptiness
switch (CheckType)
{
case 0: x = 1; break;
@ -1001,7 +1008,7 @@ bool cChunk::GrowMelonPumpkin(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_Bl
case E_BLOCK_FARMLAND:
{
// Place a randomly-facing produce:
NIBBLETYPE Meta = (ProduceType == E_BLOCK_MELON) ? 0 : static_cast<NIBBLETYPE>(a_TickRandom.randInt(4) % 4);
NIBBLETYPE Meta = (ProduceType == E_BLOCK_MELON) ? 0 : static_cast<NIBBLETYPE>(Random.RandInt(4) % 4);
LOGD("Growing melon / pumpkin at {%d, %d, %d} (<%d, %d> from stem), overwriting %s, growing on top of %s, meta %d",
a_RelX + x + m_PosX * cChunkDef::Width, a_RelY, a_RelZ + z + m_PosZ * cChunkDef::Width,
x, z,

View File

@ -27,7 +27,6 @@ namespace Json
class cWorld;
class cClientHandle;
class cServer;
class MTRand;
class cPlayer;
class cChunkMap;
class cBeaconEntity;
@ -609,7 +608,7 @@ private:
bool GrowTallGrass (int a_RelX, int a_RelY, int a_RelZ);
/** Grows a melon or a pumpkin next to the block specified (assumed to be the stem); returns true if the pumpkin or melon sucessfully grew */
bool GrowMelonPumpkin(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_BlockType, MTRand & a_Random);
bool GrowMelonPumpkin(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_BlockType);
/** Called by Tick() when an entity moves out of this chunk into a neighbor; moves the entity and sends spawn / despawn packet to clients */
void MoveEntityToNewChunk(cEntity * a_Entity);

View File

@ -1735,7 +1735,7 @@ void cChunkMap::DoExplosionAt(double a_ExplosionSize, double a_BlockX, double a_
case E_BLOCK_TNT:
{
// Activate the TNT, with a random fuse between 10 to 30 game ticks
int FuseTime = 10 + m_World->GetTickRandomNumber(20);
int FuseTime = GetRandomProvider().RandInt(10, 30);
m_World->SpawnPrimedTNT(a_BlockX + x + 0.5, a_BlockY + y + 0.5, a_BlockZ + z + 0.5, FuseTime);
area.SetBlockTypeMeta(bx + x, by + y, bz + z, E_BLOCK_AIR, 0);
a_BlocksAffected.push_back(Vector3i(bx + x, by + y, bz + z));
@ -1775,7 +1775,8 @@ void cChunkMap::DoExplosionAt(double a_ExplosionSize, double a_BlockX, double a_
default:
{
if (m_World->GetTickRandomNumber(100) <= 25) // 25% chance of pickups
auto & Random = GetRandomProvider();
if (Random.RandBool(0.25)) // 25% chance of pickups
{
cItems Drops;
cBlockHandler * Handler = BlockHandler(Block);
@ -1783,7 +1784,7 @@ void cChunkMap::DoExplosionAt(double a_ExplosionSize, double a_BlockX, double a_
Handler->ConvertToPickups(Drops, area.GetBlockMeta(bx + x, by + y, bz + z)); // Stone becomes cobblestone, coal ore becomes coal, etc.
m_World->SpawnItemPickups(Drops, bx + x, by + y, bz + z);
}
else if ((m_World->GetTNTShrapnelLevel() > slNone) && (m_World->GetTickRandomNumber(100) < 20)) // 20% chance of flinging stuff around
else if ((m_World->GetTNTShrapnelLevel() > slNone) && Random.RandBool(0.20)) // 20% chance of flinging stuff around
{
// If the block is shrapnel-able, make a falling block entity out of it:
if (
@ -2516,7 +2517,7 @@ void cChunkMap::GetChunkStats(int & a_NumChunksValid, int & a_NumChunksDirty)
bool cChunkMap::GrowMelonPumpkin(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, MTRand & a_Rand)
bool cChunkMap::GrowMelonPumpkin(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType)
{
int ChunkX, ChunkZ;
cChunkDef::AbsoluteToRelative(a_BlockX, a_BlockY, a_BlockZ, ChunkX, ChunkZ);
@ -2525,7 +2526,7 @@ bool cChunkMap::GrowMelonPumpkin(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCK
cChunkPtr Chunk = GetChunkNoLoad(ChunkX, ChunkZ);
if (Chunk != nullptr)
{
return Chunk->GrowMelonPumpkin(a_BlockX, a_BlockY, a_BlockZ, a_BlockType, a_Rand);
return Chunk->GrowMelonPumpkin(a_BlockX, a_BlockY, a_BlockZ, a_BlockType);
}
return false;
}

View File

@ -15,7 +15,6 @@
class cWorld;
class cWorldInterface;
class cItem;
class MTRand;
class cChunkStay;
class cChunk;
class cPlayer;
@ -362,7 +361,7 @@ public:
void GetChunkStats(int & a_NumChunksValid, int & a_NumChunksDirty);
/** Grows a melon or a pumpkin next to the block specified (assumed to be the stem); returns true if the pumpkin or melon sucessfully grew */
bool GrowMelonPumpkin(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, MTRand & a_Rand);
bool GrowMelonPumpkin(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType);
/** Grows a sugarcane present at the block specified by the amount of blocks specified, up to the max height specified in the config; returns the amount of blocks the sugarcane grew inside this call */
int GrowSugarcane(int a_BlockX, int a_BlockY, int a_BlockZ, int a_NumBlocksToGrow);

View File

@ -1012,14 +1012,12 @@ void cEnchantments::CheckEnchantmentConflictsFromVector(cWeightedEnchantments &
cEnchantments cEnchantments::GetRandomEnchantmentFromVector(cWeightedEnchantments & a_Enchantments)
{
cFastRandom Random;
int AllWeights = 0;
for (cWeightedEnchantments::iterator it = a_Enchantments.begin(); it != a_Enchantments.end(); ++it)
{
AllWeights += (*it).m_Weight;
}
int RandomNumber = Random.GenerateRandomInteger(0, AllWeights - 1);
int RandomNumber = GetRandomProvider().RandInt(AllWeights - 1);
for (cWeightedEnchantments::iterator it = a_Enchantments.begin(); it != a_Enchantments.end(); ++it)
{
RandomNumber -= (*it).m_Weight;

View File

@ -495,12 +495,11 @@ bool cEntity::DoTakeDamage(TakeDamageInfo & a_TDI)
{
int Chance = static_cast<int>(ThornsLevel * 15);
cFastRandom Random;
int RandomValue = Random.GenerateRandomInteger(0, 100);
auto & Random = GetRandomProvider();
if (RandomValue <= Chance)
if (Random.RandBool(Chance / 100.0))
{
a_TDI.Attacker->TakeDamage(dtAttack, this, 0, Random.GenerateRandomInteger(1, 4), 0);
a_TDI.Attacker->TakeDamage(dtAttack, this, 0, Random.RandInt(1, 4), 0);
}
}
@ -574,8 +573,7 @@ bool cEntity::DoTakeDamage(TakeDamageInfo & a_TDI)
TotalEPF = 25;
}
cFastRandom Random;
float RandomValue = Random.GenerateRandomInteger(50, 100) * 0.01f;
float RandomValue = GetRandomProvider().RandReal(0.5f, 1.0f);
TotalEPF = ceil(TotalEPF * RandomValue);

View File

@ -40,6 +40,6 @@ void cExpBottleEntity::Break(const Vector3d &a_HitPos)
{
// Spawn an experience orb with a reward between 3 and 11.
m_World->BroadcastSoundParticleEffect(EffectID::PARTICLE_SPLASH_POTION, POSX_TOINT, POSY_TOINT, POSZ_TOINT, 0);
m_World->SpawnExperienceOrb(GetPosX(), GetPosY(), GetPosZ(), 3 + m_World->GetTickRandomNumber(8));
m_World->SpawnExperienceOrb(GetPosX(), GetPosY(), GetPosZ(), GetRandomProvider().RandInt(3, 11));
Destroy();
}

View File

@ -128,6 +128,8 @@ void cFloater::SpawnOn(cClientHandle & a_Client)
void cFloater::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
{
auto & Random = GetRandomProvider();
HandlePhysics(a_Dt, a_Chunk);
if (IsBlockWater(m_World->GetBlock(POSX_TOINT, POSY_TOINT, POSZ_TOINT))
&& (m_World->GetBlockMeta(POSX_TOINT, POSY_TOINT, POSX_TOINT) == 0))
@ -141,13 +143,13 @@ void cFloater::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
SetPosY(GetPosY() - 1);
m_CanPickupItem = true;
m_PickupCountDown = 20;
m_CountDownTime = 100 + m_World->GetTickRandomNumber(800);
m_CountDownTime = Random.RandInt(100, 900);
LOGD("Floater %i can be picked up", GetUniqueID());
}
else if (m_CountDownTime == 20) // Calculate the position where the particles should spawn and start producing them.
{
LOGD("Started producing particles for floater %i", GetUniqueID());
m_ParticlePos.Set(GetPosX() + (-4 + m_World->GetTickRandomNumber(8)), GetPosY(), GetPosZ() + (-4 + m_World->GetTickRandomNumber(8)));
m_ParticlePos.Set(GetPosX() + Random.RandInt(-4, 4), GetPosY(), GetPosZ() + Random.RandInt(-4, 4));
m_World->GetBroadcaster().BroadcastParticleEffect("splash", static_cast<Vector3f>(m_ParticlePos), Vector3f{}, 0, 15);
}
else if (m_CountDownTime < 20)
@ -159,14 +161,14 @@ void cFloater::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
m_CountDownTime--;
if (m_World->GetHeight(POSX_TOINT, POSZ_TOINT) == POSY_TOINT)
{
if (m_World->IsWeatherWet() && m_World->GetTickRandomNumber(3) == 0) // 25% chance of an extra countdown when being rained on.
if (m_World->IsWeatherWet() && Random.RandBool(0.25)) // 25% chance of an extra countdown when being rained on.
{
m_CountDownTime--;
}
}
else // if the floater is underground it has a 50% chance of not decreasing the countdown.
{
if (m_World->GetTickRandomNumber(1) == 0)
if (Random.RandBool())
{
m_CountDownTime++;
}

View File

@ -1069,9 +1069,9 @@ void cPlayer::KilledBy(TakeDamageInfo & a_TDI)
{
case dtRangedAttack: DamageText = "was shot"; break;
case dtLightning: DamageText = "was plasmified by lightining"; break;
case dtFalling: DamageText = (GetWorld()->GetTickRandomNumber(10) % 2 == 0) ? "fell to death" : "hit the ground too hard"; break;
case dtFalling: DamageText = GetRandomProvider().RandBool() ? "fell to death" : "hit the ground too hard"; break;
case dtDrowning: DamageText = "drowned"; break;
case dtSuffocating: DamageText = (GetWorld()->GetTickRandomNumber(10) % 2 == 0) ? "git merge'd into a block" : "fused with a block"; break;
case dtSuffocating: DamageText = GetRandomProvider().RandBool() ? "git merge'd into a block" : "fused with a block"; break;
case dtStarving: DamageText = "forgot the importance of food"; break;
case dtCactusContact: DamageText = "was impaled on a cactus"; break;
case dtLavaContact: DamageText = "was melted by lava"; break;
@ -2295,18 +2295,17 @@ void cPlayer::UseEquippedItem(int a_Amount)
int UnbreakingLevel = static_cast<int>(Item.m_Enchantments.GetLevel(cEnchantments::enchUnbreaking));
if (UnbreakingLevel > 0)
{
int chance;
double chance = 0.0;
if (ItemCategory::IsArmor(Item.m_ItemType))
{
chance = 60 + (40 / (UnbreakingLevel + 1));
chance = 0.6 + (0.4 / (UnbreakingLevel + 1));
}
else
{
chance = 100 / (UnbreakingLevel + 1);
chance = 1.0 / (UnbreakingLevel + 1);
}
cFastRandom Random;
if (Random.NextInt(101) <= chance)
if (GetRandomProvider().RandBool(chance))
{
return;
}

View File

@ -74,11 +74,12 @@ void cThrownEggEntity::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
void cThrownEggEntity::TrySpawnChicken(const Vector3d & a_HitPos)
{
if (m_World->GetTickRandomNumber(7) == 1)
auto & Random = GetRandomProvider();
if (Random.RandBool(0.125))
{
m_World->SpawnMob(a_HitPos.x, a_HitPos.y, a_HitPos.z, mtChicken, true);
}
else if (m_World->GetTickRandomNumber(32) == 1)
else if (Random.RandBool(1.0 / 33.0))
{
m_World->SpawnMob(a_HitPos.x, a_HitPos.y, a_HitPos.z, mtChicken, true);
m_World->SpawnMob(a_HitPos.x, a_HitPos.y, a_HitPos.z, mtChicken, true);

View File

@ -1,11 +1,11 @@
// FastRandom.cpp
// Implements the cFastRandom class representing a fast random number generator
#include "Globals.h"
#include "FastRandom.h"
#include <mutex>
#include <random>
#if defined (__GNUC__)
@ -13,109 +13,50 @@
#elif defined (_MSC_VER)
#define ATTRIBUTE_TLS static __declspec(thread)
#else
#error "Unknown thread local storage qualifier"
#define ATTRIBUTE_TLS thread_local
#endif
static unsigned int GetRandomSeed()
MTRand & GetRandomProvider()
{
ATTRIBUTE_TLS bool SeedCounterInitialized = 0;
ATTRIBUTE_TLS unsigned int SeedCounter = 0;
// Some compilers don't support thread_local for non-POD types, this is purely a work around for that restriction.
// There should be minimal overhead for the non-initializing case and all thread's instances are deleted properly.
ATTRIBUTE_TLS MTRand * LocalPtr = nullptr;
if (LocalPtr == nullptr)
{
// This list allows deletion of elements as if they had static storage duration
static std::mutex CSDeleteList;
static std::list<std::unique_ptr<MTRand>> DeleteList;
cRandomDeviceSeeder seeder;
auto NewInstance = cpp14::make_unique<MTRand>(seeder);
auto TempPtr = NewInstance.get();
std::lock_guard<std::mutex> Lock(CSDeleteList);
DeleteList.push_front(std::move(NewInstance));
LocalPtr = TempPtr; // Set after push_back so LocalPtr won't dangle if it throws
}
return *LocalPtr;
}
UInt32 Detail::GetRandomSeed()
{
ATTRIBUTE_TLS bool SeedCounterInitialized = false;
ATTRIBUTE_TLS UInt32 SeedCounter = 0;
if (!SeedCounterInitialized)
{
std::random_device rd;
std::uniform_int_distribution<unsigned int> dist;
std::uniform_int_distribution<UInt32> dist;
SeedCounter = dist(rd);
SeedCounterInitialized = true;
}
return ++SeedCounter;
}
////////////////////////////////////////////////////////////////////////////////
// cFastRandom:
cFastRandom::cFastRandom(void) :
m_LinearRand(GetRandomSeed())
{
}
int cFastRandom::NextInt(int a_Range)
{
std::uniform_int_distribution<> distribution(0, a_Range - 1);
return distribution(m_LinearRand);
}
float cFastRandom::NextFloat(float a_Range)
{
std::uniform_real_distribution<float> distribution(0, a_Range);
return distribution(m_LinearRand);
}
int cFastRandom::GenerateRandomInteger(int a_Begin, int a_End)
{
std::uniform_int_distribution<> distribution(a_Begin, a_End);
return distribution(m_LinearRand);
}
////////////////////////////////////////////////////////////////////////////////
// MTRand:
MTRand::MTRand() :
m_MersenneRand(GetRandomSeed())
{
}
int MTRand::randInt(int a_Range)
{
std::uniform_int_distribution<> distribution(0, a_Range);
return distribution(m_MersenneRand);
}
int MTRand::randInt()
{
std::uniform_int_distribution<> distribution(0, std::numeric_limits<int>::max());
return distribution(m_MersenneRand);
}
double MTRand::rand(double a_Range)
{
std::uniform_real_distribution<> distribution(0, a_Range);
return distribution(m_MersenneRand);
}

View File

@ -4,17 +4,10 @@
// Declares the cFastRandom class representing a fast random number generator
/*
The cFastRandom aims to provide a very fast, although not very cryptographically secure, random generator.
It is fast to instantiate, fast to query next, and partially multi-thread-safe.
It is multi-thread-safe in the sense that it can be accessed from multiple threads without crashing, but it may
yield duplicate numbers in that case.
Internally, this works similar to cNoise's integral noise generation, with some predefined inputs: the seed is
taken from a global counter and the random is calculated using a counter that is incremented on each use (hence
the multi-thread duplication). Two alternatives exists for each function, one that takes a range parameter,
and another that takes an additional "salt" parameter; this salt is used as an additional input to the random,
in order to avoid multi-thread duplication. If two threads both use the class at the same time with different
salts, the values they get will be different.
The cFastRandom alias should be avoided in favor of the result of a call to GetRandomProvider().
The MTRand generator used is faster, has a better range and provides higher quality randomness.
Note that MTRand is relatively costly to construct and so instances should be long lived,
prefer calls to GetRandomProvider over creating new instances.
*/
@ -23,58 +16,195 @@ salts, the values they get will be different.
#pragma once
#include <random>
#include <type_traits>
#include <limits>
class cFastRandom
namespace Detail
{
/** Returns a low quality seed. */
UInt32 GetRandomSeed();
/** Aliases true_type if Char is any variant of char ignoring signed-ness. */
template <class Char>
using IsChar = typename std::is_same<typename std::make_signed<Char>::type, signed char>::type;
template <class IntType>
struct cUniformImpl :
public std::conditional<
IsChar<IntType>::value,
typename std::conditional< // Match signed-ness of IntType
std::is_signed<IntType>::value,
std::uniform_int_distribution<short>,
std::uniform_int_distribution<unsigned short>
>::type,
std::uniform_int_distribution<IntType>
>
{
};
/** uniform_int_distribution<char> is undefined so this aliases a valid type. */
template <class IntType>
using cUniform = typename cUniformImpl<IntType>::type;
}
/** Class to wrap any random engine to provide a more convenient interface. */
template <class RandomEngine>
class cRandomWrapper
{
public:
/** Initialize with a low quality seed. */
cRandomWrapper():
m_Engine(Detail::GetRandomSeed())
{
}
cFastRandom(void);
/** Returns a random int in the range [0 .. a_Range - 1]; a_Range must be less than 1M */
int NextInt(int a_Range);
/** Initialize with a SeedSequence. */
template <class SeedSeq>
cRandomWrapper(SeedSeq & a_SeedSeq):
m_Engine(a_SeedSeq)
{
}
/** Returns a random float in the range [0 .. a_Range]; a_Range must be less than 1M */
float NextFloat(float a_Range);
/** Returns a random float between 0 and 1. */
float NextFloat(void) { return NextFloat(1); }
/** Returns a random int in the range [a_Begin .. a_End] */
int GenerateRandomInteger(int a_Begin, int a_End);
/** Return a random IntType in the range [a_Min, a_Max]. */
template <class IntType = int, class ArgType>
IntType RandInt(ArgType a_Min, ArgType a_Max)
{
ASSERT(
(a_Max >= a_Min) &&
(a_Max <= std::numeric_limits<IntType>::max()) &&
(a_Min >= std::numeric_limits<IntType>::min())
);
Detail::cUniform<IntType> dist(
static_cast<IntType>(a_Min),
static_cast<IntType>(a_Max)
);
return static_cast<IntType>(dist(m_Engine));
}
/** Return a random IntType in the range [0, a_Max]. */
template <class IntType = int, class ArgType>
IntType RandInt(ArgType a_Max)
{
ASSERT((a_Max >= 0) && (a_Max <= std::numeric_limits<IntType>::max()));
Detail::cUniform<IntType> dist(IntType(0), static_cast<IntType>(a_Max));
return static_cast<IntType>(dist(m_Engine));
}
/** Return a random IntType in the range [0, std::numeric_limits<IntType>::max()]. */
template <class IntType = int>
IntType RandInt()
{
Detail::cUniform<IntType> dist(IntType(0), std::numeric_limits<IntType>::max());
return static_cast<IntType>(dist(m_Engine));
}
/** Return a random RealType in the range [a_Min, a_Max). */
template <class RealType = float, class ArgType>
RealType RandReal(ArgType a_Min, ArgType a_Max)
{
std::uniform_real_distribution<RealType> dist(a_Min, a_Max);
return dist(m_Engine);
}
/** Return a random RealType in the range [0, a_Max). */
template <class RealType = float, class ArgType>
RealType RandReal(ArgType a_Max)
{
std::uniform_real_distribution<RealType> dist(RealType(0), a_Max);
return dist(m_Engine);
}
/** Return a random RealType in the range [0, 1). */
template <class RealType = float>
RealType RandReal()
{
std::uniform_real_distribution<RealType> dist;
return dist(m_Engine);
}
/** Return a random bool with the given probability of being true. */
bool RandBool(double a_TrueProbability = 0.5)
{
std::bernoulli_distribution dist(a_TrueProbability);
return dist(m_Engine);
}
/** Returns a reference to the underlying random engine. */
RandomEngine & Engine()
{
return m_Engine;
}
private:
std::minstd_rand m_LinearRand;
RandomEngine m_Engine;
};
class MTRand
/** Utility to seed a random engine with maximal entropy from random_device. */
struct cRandomDeviceSeeder
{
public:
using result_type = std::random_device::result_type;
MTRand(void);
/** Returns a random integer in the range [0 .. a_Range]. */
int randInt(int a_Range);
/** Returns a random integer in the range [0 .. MAX_INT]. */
int randInt(void);
/** Returns a random floating point number in the range [0 .. a_Range]. */
double rand(double a_Range);
private:
std::mt19937 m_MersenneRand;
template <class Itr>
void generate(Itr first, Itr last)
{
std::random_device rd;
std::uniform_int_distribution<result_type> dist;
for (; first != last; ++first)
{
*first = dist(rd);
}
}
};
using cFastRandom = cRandomWrapper<std::minstd_rand>;
using MTRand = cRandomWrapper<std::mt19937>;
/** Returns the current thread's random number source. */
MTRand & GetRandomProvider();

View File

@ -59,8 +59,7 @@ bool cChunkGenerator::Start(cPluginInterface & a_PluginInterface, cChunkSink & a
}
else
{
MTRand rnd;
m_Seed = rnd.randInt();
m_Seed = GetRandomProvider().RandInt();
LOGINFO("Chosen a new random seed for world: %d", m_Seed);
a_IniFile.SetValueI("Seed", "Seed", m_Seed);
}

View File

@ -162,7 +162,7 @@ protected:
int RelStartZ = Clamp(a_StartZ - BlockZ, 0, cChunkDef::Width - 1);
int RelEndX = Clamp(a_EndX - BlockX, 0, cChunkDef::Width);
int RelEndZ = Clamp(a_EndZ - BlockZ, 0, cChunkDef::Width);
cFastRandom rnd;
auto & rnd = GetRandomProvider();
for (int y = a_StartY; y < a_EndY; y++)
{
for (int z = RelStartZ; z < RelEndZ; z++)
@ -171,7 +171,7 @@ protected:
{
if (cBlockInfo::CanBeTerraformed(a_ChunkDesc.GetBlockType(x, y, z)))
{
BLOCKTYPE BlockType = (rnd.NextInt(101) < 75) ? a_DstBlockType1 : a_DstBlockType2;
BLOCKTYPE BlockType = rnd.RandBool(0.75) ? a_DstBlockType1 : a_DstBlockType2;
a_ChunkDesc.SetBlockType(x, y, z, BlockType);
}
} // for x

View File

@ -332,9 +332,9 @@ bool cItem::EnchantByXPLevels(int a_NumXPLevels)
return false;
}
cFastRandom Random;
int ModifiedEnchantmentLevel = a_NumXPLevels + static_cast<int>(Random.NextFloat(static_cast<float>(Enchantability / 4))) + static_cast<int>(Random.NextFloat(static_cast<float>(Enchantability / 4))) + 1;
float RandomBonus = 1.0F + (Random.NextFloat(1) + Random.NextFloat(1) - 1.0F) * 0.15F;
auto & Random = GetRandomProvider();
int ModifiedEnchantmentLevel = a_NumXPLevels + Random.RandInt(Enchantability / 4) + Random.RandInt(Enchantability / 4) + 1;
float RandomBonus = 1.0F + (Random.RandReal() + Random.RandReal() - 1.0F) * 0.15F;
int FinalEnchantmentLevel = static_cast<int>(ModifiedEnchantmentLevel * RandomBonus + 0.5F);
cWeightedEnchantments Enchantments;
@ -352,12 +352,10 @@ bool cItem::EnchantByXPLevels(int a_NumXPLevels)
// Checking for conflicting enchantments
cEnchantments::CheckEnchantmentConflictsFromVector(Enchantments, Enchantment1);
float NewEnchantmentLevel = static_cast<float>(a_NumXPLevels);
// Next Enchantment (Second)
NewEnchantmentLevel = NewEnchantmentLevel / 2;
float SecondEnchantmentChance = (NewEnchantmentLevel + 1) / 50 * 100;
if (Enchantments.empty() || (Random.NextFloat(100) > SecondEnchantmentChance))
float NewEnchantmentLevel = a_NumXPLevels / 2.0f;
float SecondEnchantmentChance = (NewEnchantmentLevel + 1) / 50.0f;
if (Enchantments.empty() || !Random.RandBool(SecondEnchantmentChance))
{
return true;
}
@ -370,9 +368,9 @@ bool cItem::EnchantByXPLevels(int a_NumXPLevels)
cEnchantments::CheckEnchantmentConflictsFromVector(Enchantments, Enchantment2);
// Next Enchantment (Third)
NewEnchantmentLevel = NewEnchantmentLevel / 2;
float ThirdEnchantmentChance = (NewEnchantmentLevel + 1) / 50 * 100;
if (Enchantments.empty() || (Random.NextFloat(100) > ThirdEnchantmentChance))
NewEnchantmentLevel = NewEnchantmentLevel / 2.0f;
float ThirdEnchantmentChance = (NewEnchantmentLevel + 1) / 50.0f;
if (Enchantments.empty() || !Random.RandBool(ThirdEnchantmentChance))
{
return true;
}
@ -385,9 +383,9 @@ bool cItem::EnchantByXPLevels(int a_NumXPLevels)
cEnchantments::CheckEnchantmentConflictsFromVector(Enchantments, Enchantment3);
// Next Enchantment (Fourth)
NewEnchantmentLevel = NewEnchantmentLevel / 2;
float FourthEnchantmentChance = (NewEnchantmentLevel + 1) / 50 * 100;
if (Enchantments.empty() || (Random.NextFloat(100) > FourthEnchantmentChance))
NewEnchantmentLevel = NewEnchantmentLevel / 2.0f;
float FourthEnchantmentChance = (NewEnchantmentLevel + 1) / 50.0f;
if (Enchantments.empty() || !Random.RandBool(FourthEnchantmentChance))
{
return true;
}

View File

@ -108,6 +108,8 @@ public:
return false;
}
auto & Random = GetRandomProvider();
if (a_Player->IsFishing())
{
cFloaterCallback FloaterInfo;
@ -122,10 +124,10 @@ public:
else if (FloaterInfo.CanPickup())
{
cItems Drops;
int ItemCategory = a_World->GetTickRandomNumber(99);
int ItemCategory = Random.RandInt(99);
if (ItemCategory <= 4) // Treasures 5%
{
int Treasure = a_World->GetTickRandomNumber(5);
int Treasure = Random.RandInt(5);
switch (Treasure)
{
case 0:
@ -140,7 +142,7 @@ public:
}
case 2:
{
Drops.Add(cItem(E_ITEM_FISHING_ROD, 1, static_cast<short>(a_World->GetTickRandomNumber(50)))); // Fishing rod with durability. TODO: Enchantments on it
Drops.Add(cItem(E_ITEM_FISHING_ROD, 1, Random.RandInt<short>(50))); // Fishing rod with durability. TODO: Enchantments on it
break;
}
case 3:
@ -164,14 +166,14 @@ public:
}
else if (ItemCategory <= 14) // Junk 10%
{
int Junk = a_World->GetTickRandomNumber(70);
int Junk = Random.RandInt(70);
if (Junk <= 1)
{
Drops.Add(cItem(E_ITEM_DYE, 10, 0));
}
else if (Junk <= 4)
{
Drops.Add(cItem(E_ITEM_BOW, 1, static_cast<short>(a_World->GetTickRandomNumber(64))));
Drops.Add(cItem(E_ITEM_BOW, 1, Random.RandInt<short>(64)));
}
else if (Junk <= 9)
{
@ -214,7 +216,7 @@ public:
}
else // Fish
{
int FishType = a_World->GetTickRandomNumber(99);
int FishType = Random.RandInt(99);
if (FishType <= 1) // Clownfish has a 2% chance of spawning
{
Drops.Add(cItem(E_ITEM_RAW_FISH, 1, E_META_RAW_FISH_CLOWNFISH));
@ -250,7 +252,7 @@ public:
}
else
{
cFloater * Floater = new cFloater(a_Player->GetPosX(), a_Player->GetStance(), a_Player->GetPosZ(), a_Player->GetLookVector() * 15, a_Player->GetUniqueID(), static_cast<int>(100 + static_cast<unsigned int>(a_World->GetTickRandomNumber(800)) - (a_Player->GetEquippedItem().m_Enchantments.GetLevel(cEnchantments::enchLure) * 100)));
cFloater * Floater = new cFloater(a_Player->GetPosX(), a_Player->GetStance(), a_Player->GetPosZ(), a_Player->GetLookVector() * 15, a_Player->GetUniqueID(), (Random.RandInt(100, 900) - static_cast<int>(a_Player->GetEquippedItem().m_Enchantments.GetLevel(cEnchantments::enchLure) * 100)));
if (!Floater->Initialize(*a_World))
{
delete Floater;

View File

@ -861,8 +861,7 @@ bool cItemHandler::EatItem(cPlayer * a_Player, cItem * a_Item)
float Chance;
if (Success && GetEatEffect(EffectType, EffectDurationTicks, EffectIntensity, Chance))
{
cFastRandom r1;
if (r1.NextFloat() < Chance)
if (GetRandomProvider().RandBool(Chance))
{
a_Player->AddEntityEffect(EffectType, EffectDurationTicks, EffectIntensity, Chance);
}

View File

@ -36,8 +36,7 @@ public:
Vector3d Speed = a_Player->GetLookVector() * m_SpeedCoeff;
// Play sound
cFastRandom Random;
a_World->BroadcastSoundEffect("entity.arrow.shoot", a_Player->GetPosX(), a_Player->GetPosY() - a_Player->GetHeight(), a_Player->GetPosZ(), 0.5f, 0.4f / (Random.NextFloat(1.0f) * 0.4f + 0.8f));
a_World->BroadcastSoundEffect("entity.arrow.shoot", a_Player->GetPosX(), a_Player->GetPosY() - a_Player->GetHeight(), a_Player->GetPosZ(), 0.5f, 0.4f / GetRandomProvider().RandReal(0.8f, 1.2f));
if (a_World->CreateProjectile(Pos.x, Pos.y, Pos.z, m_ProjectileKind, a_Player, &a_Player->GetEquippedItem(), &Speed) == cEntity::INVALID_ID)
{

View File

@ -306,10 +306,8 @@ const cMapDecorator cMap::CreateDecorator(const cEntity * a_TrackedEntity)
if (GetDimension() == dimNether)
{
cFastRandom Random;
// TODO 2014-02-19 xdot: Refine
Rot = Random.NextInt(16);
Rot = GetRandomProvider().RandInt(15);
}
else
{

View File

@ -111,12 +111,8 @@ eMonsterType cMobSpawner::ChooseMobType(EMCSBiome a_Biome)
if (allowedMobsSize > 0)
{
std::set<eMonsterType>::iterator itr = allowedMobs.begin();
int iRandom = m_Random.NextInt(static_cast<int>(allowedMobsSize));
for (int i = 0; i < iRandom; i++)
{
++itr;
}
std::advance(itr, GetRandomProvider().RandInt<size_t>(allowedMobsSize - 1));
return *itr;
}
@ -139,7 +135,7 @@ bool cMobSpawner::CanSpawnHere(cChunk * a_Chunk, int a_RelX, int a_RelY, int a_R
return false; // Make sure mobs do not spawn on bedrock.
}
cFastRandom Random;
auto & Random = GetRandomProvider();
BLOCKTYPE TargetBlock = a_Chunk->GetBlock(a_RelX, a_RelY, a_RelZ);
cPlayer * a_Closest_Player = a_Chunk->GetWorld()->FindClosestPlayer(a_Chunk->PositionToWorldPosition(a_RelX, a_RelY, a_RelZ), 24);
@ -202,7 +198,7 @@ bool cMobSpawner::CanSpawnHere(cChunk * a_Chunk, int a_RelX, int a_RelY, int a_R
(BlockBelow == E_BLOCK_GRASS) || (BlockBelow == E_BLOCK_LEAVES) || (BlockBelow == E_BLOCK_NEW_LEAVES)
) &&
(a_RelY >= 62) &&
(Random.NextInt(3) != 0)
(Random.RandBool(2.0 / 3.0))
);
}
@ -260,7 +256,7 @@ bool cMobSpawner::CanSpawnHere(cChunk * a_Chunk, int a_RelX, int a_RelY, int a_R
(!cBlockInfo::IsTransparent(BlockBelow)) &&
(SkyLight <= 7) &&
(BlockLight <= 7) &&
(Random.NextInt(2) == 0)
(Random.RandBool())
);
}
@ -274,7 +270,7 @@ bool cMobSpawner::CanSpawnHere(cChunk * a_Chunk, int a_RelX, int a_RelY, int a_R
(!cBlockInfo::IsTransparent(BlockBelow)) &&
(SkyLight <= 7) &&
(BlockLight <= 7) &&
(Random.NextInt(2) == 0)
(Random.RandBool())
);
}
@ -298,7 +294,7 @@ bool cMobSpawner::CanSpawnHere(cChunk * a_Chunk, int a_RelX, int a_RelY, int a_R
(TargetBlock == E_BLOCK_AIR) &&
(BlockAbove == E_BLOCK_AIR) &&
(!cBlockInfo::IsTransparent(BlockBelow)) &&
(Random.NextInt(20) == 0)
(Random.RandBool(0.05))
);
}

View File

@ -5,7 +5,6 @@
#include "BlockID.h"
#include "ChunkDef.h"
#include "Chunk.h"
#include "FastRandom.h"
#include "Mobs/Monster.h" // This is a side-effect of keeping Mobfamily inside Monster class. I'd prefer to keep both (Mobfamily and Monster) inside a "Monster" namespace MG TODO : do it
@ -66,7 +65,6 @@ protected :
bool m_NewPack;
eMonsterType m_MobType;
std::set<cMonster*> m_Spawned;
cFastRandom m_Random;
} ;

View File

@ -34,7 +34,7 @@ void cChicken::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
return; // Babies don't lay eggs
}
if ((m_EggDropTimer == 6000) && (m_World->GetTickRandomNumber(1) == 0))
if ((m_EggDropTimer == 6000) && GetRandomProvider().RandBool())
{
cItems Drops;
m_EggDropTimer = 0;

View File

@ -41,16 +41,18 @@ void cHorse::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
return;
}
auto & Random = GetRandomProvider();
if (!m_bIsMouthOpen)
{
if (m_World->GetTickRandomNumber(50) == 25)
if (Random.RandBool(0.02))
{
m_bIsMouthOpen = true;
}
}
else
{
if (m_World->GetTickRandomNumber(10) == 5)
if (Random.RandBool(0.10))
{
m_bIsMouthOpen = false;
}
@ -60,7 +62,7 @@ void cHorse::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
{
if (m_TameAttemptTimes < m_TimesToTame)
{
if (m_World->GetTickRandomNumber(50) == 25)
if (Random.RandBool(0.02))
{
m_World->BroadcastSoundParticleEffect(EffectID::PARTICLE_SMOKE, FloorC(GetPosX()), FloorC(GetPosY()), FloorC(GetPosZ()), int(SmokeDirection::SOUTH_EAST));
m_World->BroadcastSoundParticleEffect(EffectID::PARTICLE_SMOKE, FloorC(GetPosX()), FloorC(GetPosY()), FloorC(GetPosZ()), int(SmokeDirection::SOUTH_WEST));

View File

@ -512,7 +512,7 @@ void cMonster::KilledBy(TakeDamageInfo & a_TDI)
case mtOcelot:
case mtWolf:
{
Reward = m_World->GetTickRandomNumber(2) + 1;
Reward = GetRandomProvider().RandInt(1, 3);
break;
}
@ -531,7 +531,7 @@ void cMonster::KilledBy(TakeDamageInfo & a_TDI)
case mtSlime:
case mtMagmaCube:
{
Reward = 6 + (m_World->GetTickRandomNumber(2));
Reward = GetRandomProvider().RandInt(6, 8);
break;
}
case mtBlaze:
@ -657,13 +657,15 @@ void cMonster::InStateIdle(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
if (m_IdleInterval > std::chrono::seconds(1))
{
auto & Random = GetRandomProvider();
// At this interval the results are predictable
int rem = m_World->GetTickRandomNumber(6) + 1;
int rem = Random.RandInt(1, 7);
m_IdleInterval -= std::chrono::seconds(1); // So nothing gets dropped when the server hangs for a few seconds
Vector3d Dist;
Dist.x = static_cast<double>(m_World->GetTickRandomNumber(10)) - 5.0;
Dist.z = static_cast<double>(m_World->GetTickRandomNumber(10)) - 5.0;
Dist.x = static_cast<double>(Random.RandInt(-5, 5));
Dist.z = static_cast<double>(Random.RandInt(-5, 5));
if ((Dist.SqrLength() > 2) && (rem >= 3))
{
@ -1005,7 +1007,7 @@ cPawn * cMonster::GetTarget ()
cMonster * cMonster::NewMonsterFromType(eMonsterType a_MobType)
{
cFastRandom Random;
auto & Random = GetRandomProvider();
cMonster * toReturn = nullptr;
// Create the mob entity
@ -1013,23 +1015,23 @@ cMonster * cMonster::NewMonsterFromType(eMonsterType a_MobType)
{
case mtMagmaCube:
{
toReturn = new cMagmaCube(1 << Random.NextInt(3)); // Size 1, 2 or 4
toReturn = new cMagmaCube(1 << Random.RandInt(2)); // Size 1, 2 or 4
break;
}
case mtSlime:
{
toReturn = new cSlime(1 << Random.NextInt(3)); // Size 1, 2 or 4
toReturn = new cSlime(1 << Random.RandInt(2)); // Size 1, 2 or 4
break;
}
case mtSkeleton:
{
// TODO: Actual detection of spawning in Nether
toReturn = new cSkeleton((Random.NextInt(1) == 0) ? false : true);
toReturn = new cSkeleton(false);
break;
}
case mtVillager:
{
int VillagerType = Random.NextInt(6);
int VillagerType = Random.RandInt(6);
if (VillagerType == 6)
{
// Give farmers a better chance of spawning
@ -1042,10 +1044,10 @@ cMonster * cMonster::NewMonsterFromType(eMonsterType a_MobType)
case mtHorse:
{
// Horses take a type (species), a colour, and a style (dots, stripes, etc.)
int HorseType = Random.NextInt(8);
int HorseColor = Random.NextInt(7);
int HorseStyle = Random.NextInt(5);
int HorseTameTimes = Random.NextInt(6) + 1;
int HorseType = Random.RandInt(7);
int HorseColor = Random.RandInt(6);
int HorseStyle = Random.RandInt(4);
int HorseTameTimes = Random.RandInt(1, 6);
if ((HorseType == 5) || (HorseType == 6) || (HorseType == 7))
{
@ -1097,11 +1099,10 @@ cMonster * cMonster::NewMonsterFromType(eMonsterType a_MobType)
void cMonster::AddRandomDropItem(cItems & a_Drops, unsigned int a_Min, unsigned int a_Max, short a_Item, short a_ItemHealth)
{
MTRand r1;
int Count = static_cast<int>(static_cast<unsigned int>(r1.randInt()) % (a_Max + 1 - a_Min) + a_Min);
auto Count = GetRandomProvider().RandInt<char>(static_cast<char>(a_Min), static_cast<char>(a_Max));
if (Count > 0)
{
a_Drops.push_back(cItem(a_Item, static_cast<char>(Count), a_ItemHealth));
a_Drops.emplace_back(a_Item, Count, a_ItemHealth);
}
}
@ -1111,9 +1112,7 @@ void cMonster::AddRandomDropItem(cItems & a_Drops, unsigned int a_Min, unsigned
void cMonster::AddRandomUncommonDropItem(cItems & a_Drops, float a_Chance, short a_Item, short a_ItemHealth)
{
MTRand r1;
int Count = r1.randInt() % 1000;
if (Count < (a_Chance * 10))
if (GetRandomProvider().RandBool(a_Chance / 100.0))
{
a_Drops.push_back(cItem(a_Item, 1, a_ItemHealth));
}
@ -1125,11 +1124,10 @@ void cMonster::AddRandomUncommonDropItem(cItems & a_Drops, float a_Chance, short
void cMonster::AddRandomRareDropItem(cItems & a_Drops, cItems & a_Items, unsigned int a_LootingLevel)
{
MTRand r1;
unsigned int Count = static_cast<unsigned int>(static_cast<unsigned long>(r1.randInt()) % 200);
if (Count < (5 + a_LootingLevel))
auto & r1 = GetRandomProvider();
if (r1.RandBool((5 + a_LootingLevel) / 200.0))
{
size_t Rare = static_cast<size_t>(r1.randInt()) % a_Items.Size();
size_t Rare = r1.RandInt<size_t>(a_Items.Size() - 1);
a_Drops.push_back(a_Items.at(Rare));
}
}
@ -1140,8 +1138,11 @@ void cMonster::AddRandomRareDropItem(cItems & a_Drops, cItems & a_Items, unsigne
void cMonster::AddRandomArmorDropItem(cItems & a_Drops, unsigned int a_LootingLevel)
{
MTRand r1;
if (r1.randInt() % 200 < ((m_DropChanceHelmet * 200) + (a_LootingLevel * 2)))
auto & r1 = GetRandomProvider();
double LootingBonus = a_LootingLevel / 100.0;
if (r1.RandBool(m_DropChanceHelmet + LootingBonus))
{
if (!GetEquippedHelmet().IsEmpty())
{
@ -1149,7 +1150,7 @@ void cMonster::AddRandomArmorDropItem(cItems & a_Drops, unsigned int a_LootingLe
}
}
if (r1.randInt() % 200 < ((m_DropChanceChestplate * 200) + (a_LootingLevel * 2)))
if (r1.RandBool(m_DropChanceChestplate + LootingBonus))
{
if (!GetEquippedChestplate().IsEmpty())
{
@ -1157,7 +1158,7 @@ void cMonster::AddRandomArmorDropItem(cItems & a_Drops, unsigned int a_LootingLe
}
}
if (r1.randInt() % 200 < ((m_DropChanceLeggings * 200) + (a_LootingLevel * 2)))
if (r1.RandBool(m_DropChanceLeggings + LootingBonus))
{
if (!GetEquippedLeggings().IsEmpty())
{
@ -1165,7 +1166,7 @@ void cMonster::AddRandomArmorDropItem(cItems & a_Drops, unsigned int a_LootingLe
}
}
if (r1.randInt() % 200 < ((m_DropChanceBoots * 200) + (a_LootingLevel * 2)))
if (r1.RandBool(m_DropChanceBoots + LootingBonus))
{
if (!GetEquippedBoots().IsEmpty())
{
@ -1180,8 +1181,7 @@ void cMonster::AddRandomArmorDropItem(cItems & a_Drops, unsigned int a_LootingLe
void cMonster::AddRandomWeaponDropItem(cItems & a_Drops, unsigned int a_LootingLevel)
{
MTRand r1;
if (r1.randInt() % 200 < ((m_DropChanceWeapon * 200) + (a_LootingLevel * 2)))
if (GetRandomProvider().RandBool(m_DropChanceWeapon + (a_LootingLevel / 100.0)))
{
if (!GetEquippedWeapon().IsEmpty())
{

View File

@ -127,8 +127,7 @@ void cPassiveMonster::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
Callback.Baby->InheritFromParents(this, m_LovePartner);
}
cFastRandom Random;
m_World->SpawnExperienceOrb(Pos.x, Pos.y, Pos.z, 1 + Random.NextInt(6));
m_World->SpawnExperienceOrb(Pos.x, Pos.y, Pos.z, GetRandomProvider().RandInt(1, 6));
m_LovePartner->ResetLoveMode();
ResetLoveMode();

View File

@ -148,7 +148,7 @@ bool cPath::StepOnce()
// Check if we have a new NearestPoint.
if ((m_Destination - CurrentCell->m_Location).Length() < 5)
{
if (m_Rand.NextInt(4) == 0)
if (GetRandomProvider().RandBool(0.25))
{
m_NearestPointToTarget = CurrentCell;
}

View File

@ -178,7 +178,6 @@ private:
double m_HalfWidth;
int m_StepsLeft;
cPathCell * m_NearestPointToTarget;
cFastRandom m_Rand;
/* Control fields */
ePathFinderStatus m_Status;

View File

@ -10,8 +10,8 @@
cRabbit::cRabbit(void) :
cRabbit(static_cast<eRabbitType>(cFastRandom().NextInt(
static_cast<UInt8>(eRabbitType::SaltAndPepper) + 1 // Max possible Rabbit-Type
cRabbit(static_cast<eRabbitType>(GetRandomProvider().RandInt<UInt8>(
static_cast<UInt8>(eRabbitType::SaltAndPepper) // Max possible Rabbit-Type
)), 0)
{
}

View File

@ -65,8 +65,8 @@ void cSheep::OnRightClicked(cPlayer & a_Player)
a_Player.UseEquippedItem();
cItems Drops;
int NumDrops = m_World->GetTickRandomNumber(2) + 1;
Drops.push_back(cItem(E_BLOCK_WOOL, static_cast<char>(NumDrops), static_cast<short>(m_WoolColor)));
char NumDrops = GetRandomProvider().RandInt<char>(1, 3);
Drops.emplace_back(E_BLOCK_WOOL, NumDrops, static_cast<short>(m_WoolColor));
m_World->SpawnItemPickups(Drops, GetPosX(), GetPosY(), GetPosZ(), 10);
m_World->BroadcastSoundEffect("entity.sheep.shear", GetPosX(), GetPosY(), GetPosZ(), 1.0f, 1.0f);
}
@ -121,7 +121,7 @@ void cSheep::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
}
else
{
if (m_World->GetTickRandomNumber(600) == 1)
if (GetRandomProvider().RandBool(1.0 / 600.0))
{
if (m_World->GetBlock(PosX, PosY, PosZ) == E_BLOCK_GRASS)
{
@ -167,8 +167,7 @@ void cSheep::InheritFromParents(cPassiveMonster * a_Parent1, cPassiveMonster * a
return;
}
}
cFastRandom Random;
SetFurColor((Random.NextInt(100) < 50) ? Parent1->GetFurColor() : Parent2->GetFurColor());
SetFurColor(GetRandomProvider().RandBool() ? Parent1->GetFurColor() : Parent2->GetFurColor());
m_World->BroadcastEntityMetadata(*this);
}
@ -178,8 +177,7 @@ void cSheep::InheritFromParents(cPassiveMonster * a_Parent1, cPassiveMonster * a
NIBBLETYPE cSheep::GenerateNaturalRandomColor(void)
{
cFastRandom Random;
int Chance = Random.NextInt(101);
int Chance = GetRandomProvider().RandInt(100);
if (Chance <= 81)
{

View File

@ -51,12 +51,12 @@ void cSkeleton::GetDrops(cItems & a_Drops, cEntity * a_Killer)
bool cSkeleton::Attack(std::chrono::milliseconds a_Dt)
{
StopMovingToPosition(); // Todo handle this in a better way, the skeleton does some uneeded recalcs due to inStateChasing
cFastRandom Random;
auto & Random = GetRandomProvider();
if ((GetTarget() != nullptr) && (m_AttackCoolDownTicksLeft == 0))
{
Vector3d Inaccuracy = Vector3d(Random.NextFloat(0.5) - 0.25, Random.NextFloat(0.5) - 0.25, Random.NextFloat(0.5) - 0.25);
Vector3d Inaccuracy = Vector3d(Random.RandReal<double>(-0.25, 0.25), Random.RandReal<double>(-0.25, 0.25), Random.RandReal<double>(-0.25, 0.25));
Vector3d Speed = (GetTarget()->GetPosition() + Inaccuracy - GetPosition()) * 5;
Speed.y = Speed.y - 1 + Random.NextInt(3);
Speed.y += Random.RandInt(-1, 1);
cArrowEntity * Arrow = new cArrowEntity(this, GetPosX(), GetPosY() + 1, GetPosZ(), Speed);
if (Arrow == nullptr)
{

View File

@ -70,8 +70,8 @@ void cSlime::KilledBy(TakeDamageInfo & a_TDI)
if (m_Size != 1)
{
cFastRandom Random;
int SpawnAmount = 2 + Random.NextInt(3);
auto & Random = GetRandomProvider();
int SpawnAmount = Random.RandInt(2, 4);
for (int i = 0; i < SpawnAmount; ++i)
{
@ -80,7 +80,7 @@ void cSlime::KilledBy(TakeDamageInfo & a_TDI)
cSlime * NewSlime = new cSlime(m_Size / 2);
NewSlime->SetPosition(GetPosX() + AddX, GetPosY() + 0.5, GetPosZ() + AddZ);
NewSlime->SetYaw(Random.NextFloat(1.0f) * 360.0f);
NewSlime->SetYaw(Random.RandReal(360.0f));
m_World->SpawnMobFinalize(NewSlime);
}
}

View File

@ -32,7 +32,7 @@ bool cVillager::DoTakeDamage(TakeDamageInfo & a_TDI)
if ((a_TDI.Attacker != nullptr) && a_TDI.Attacker->IsPlayer())
{
if (m_World->GetTickRandomNumber(5) == 3)
if (GetRandomProvider().RandBool(1.0 / 6.0))
{
m_World->BroadcastEntityStatus(*this, esVillagerAngry);
}
@ -90,7 +90,7 @@ void cVillager::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
}
// Don't always try to do a special action. Each tick has 1% to do a special action.
if (m_World->GetTickRandomNumber(99) != 0)
if (GetRandomProvider().RandBool(0.99))
{
return;
}
@ -150,7 +150,7 @@ void cVillager::HandleFarmerPrepareFarmCrops()
m_VillagerAction = true;
m_CropsPos = Vector3i(static_cast<int>(GetPosX()) + X - 5, static_cast<int>(GetPosY()) + Y - 3, static_cast<int>(GetPosZ()) + Z - 5);
MoveToPosition(Vector3f(static_cast<float>(m_CropsPos.x + 0.5), static_cast<float>(m_CropsPos.y), static_cast<float>(m_CropsPos.z + 0.5)));
MoveToPosition(Vector3d(m_CropsPos.x + 0.5, m_CropsPos.y + 0.0, m_CropsPos.z + 0.5));
return;
} // for Y loop.
} // Repeat the procces 5 times.

View File

@ -24,11 +24,11 @@ void cWitch::GetDrops(cItems & a_Drops, cEntity * a_Killer)
{
LootingLevel = a_Killer->GetEquippedWeapon().m_Enchantments.GetLevel(cEnchantments::enchLooting);
}
MTRand r1;
int DropTypeCount = (r1.randInt() % 3) + 1;
auto & r1 = GetRandomProvider();
int DropTypeCount = r1.RandInt(1, 3);
for (int i = 0; i < DropTypeCount; i++)
{
int DropType = r1.randInt() % 7;
int DropType = r1.RandInt(6);
switch (DropType)
{
case 0: AddRandomDropItem(a_Drops, 0, 2 + LootingLevel, E_ITEM_GLASS_BOTTLE); break;

View File

@ -138,7 +138,7 @@ void cWolf::ReceiveNearbyFightInfo(AString a_PlayerID, cPawn * a_Opponent, bool
// If a player is asking for help and we already have a target,
// there's a 50% chance of helping and a 50% chance of doing nothing
// This helps spread a wolf pack's targets over several mobs
else if (m_World->GetTickRandomNumber(9)> 4)
else if (GetRandomProvider().RandBool())
{
return;
}
@ -179,7 +179,7 @@ void cWolf::OnRightClicked(cPlayer & a_Player)
a_Player.GetInventory().RemoveOneEquippedItem();
}
if (m_World->GetTickRandomNumber(7) == 0)
if (GetRandomProvider().RandBool(0.125))
{
// Taming succeeded
SetMaxHealth(20);

View File

@ -100,8 +100,7 @@ bool cProbabDistrib::SetDefString(const AString & a_DefString)
int cProbabDistrib::Random(MTRand & a_Rand) const
{
int v = a_Rand.randInt(m_Sum);
return MapValue(v);
return MapValue(a_Rand.RandInt(m_Sum));
}

View File

@ -20,8 +20,7 @@ Usage:
// fwd:
class MTRand;
#include "FastRandom.h"

View File

@ -17,6 +17,7 @@
#include "WebAdmin.h"
#include "Protocol/ProtocolRecognizer.h"
#include "CommandOutput.h"
#include "FastRandom.h"
#include "IniFile.h"
#include "Vector3.h"
@ -218,9 +219,9 @@ bool cServer::InitServer(cSettingsRepositoryInterface & a_Settings, bool a_Shoul
m_ShouldAuthenticate = a_ShouldAuth;
if (m_ShouldAuthenticate)
{
MTRand mtrand1;
unsigned int r1 = (mtrand1.randInt() % 1147483647) + 1000000000;
unsigned int r2 = (mtrand1.randInt() % 1147483647) + 1000000000;
auto & rand = GetRandomProvider();
unsigned int r1 = rand.RandInt<unsigned int>(1000000000U, 0x7fffffffU);
unsigned int r2 = rand.RandInt<unsigned int>(1000000000U, 0x7fffffffU);
std::ostringstream sid;
sid << std::hex << r1;
sid << std::hex << r2;

View File

@ -301,7 +301,7 @@ int cFireSimulator::GetBurnStepTime(cChunk * a_Chunk, int a_RelX, int a_RelY, in
void cFireSimulator::TrySpreadFire(cChunk * a_Chunk, int a_RelX, int a_RelY, int a_RelZ)
{
/*
if (m_World.GetTickRandomNumber(10000) > 100)
if (GetRandomProvider().RandBool(0.99))
{
// Make the chance to spread 100x smaller
return;
@ -317,7 +317,7 @@ void cFireSimulator::TrySpreadFire(cChunk * a_Chunk, int a_RelX, int a_RelY, int
// No need to check the coords for equality with the parent block,
// it cannot catch fire anyway (because it's not an air block)
if (m_World.GetTickRandomNumber(MAX_CHANCE_FLAMMABILITY) > m_Flammability)
if (!GetRandomProvider().RandBool(m_Flammability * (1.0 / MAX_CHANCE_FLAMMABILITY)))
{
continue;
}
@ -381,7 +381,7 @@ void cFireSimulator::RemoveFuelNeighbors(cChunk * a_Chunk, int a_RelX, int a_Rel
return;
}
bool ShouldReplaceFuel = (m_World.GetTickRandomNumber(MAX_CHANCE_REPLACE_FUEL) < m_ReplaceFuelChance);
bool ShouldReplaceFuel = (GetRandomProvider().RandBool(m_ReplaceFuelChance * (1.0 / MAX_CHANCE_REPLACE_FUEL)));
if (ShouldReplaceFuel && !cRoot::Get()->GetPluginManager()->CallHookBlockSpread(m_World, AbsX, Y, AbsZ, ssFireSpread))
{
Neighbour->SetBlock(X, Y, Z, E_BLOCK_FIRE, 0);

View File

@ -1003,8 +1003,7 @@ void cSlotAreaAnvil::OnTakeResult(cPlayer & a_Player)
NIBBLETYPE BlockMeta;
a_Player.GetWorld()->GetBlockTypeMeta(PosX, PosY, PosZ, Block, BlockMeta);
cFastRandom Random;
if (!a_Player.IsGameModeCreative() && (Block == E_BLOCK_ANVIL) && (Random.NextFloat(1.0F) < 0.12F))
if (!a_Player.IsGameModeCreative() && (Block == E_BLOCK_ANVIL) && GetRandomProvider().RandBool(0.12))
{
NIBBLETYPE Orientation = BlockMeta & 0x3;
NIBBLETYPE AnvilDamage = BlockMeta >> 2;
@ -1578,8 +1577,8 @@ void cSlotAreaEnchanting::UpdateResult(cPlayer & a_Player)
{
int Bookshelves = std::min(GetBookshelvesCount(*a_Player.GetWorld()), 15);
cFastRandom Random;
int Base = (Random.GenerateRandomInteger(1, 8) + static_cast<int>(floor(static_cast<float>(Bookshelves / 2)) + Random.GenerateRandomInteger(0, Bookshelves)));
auto & Random = GetRandomProvider();
int Base = (Random.RandInt(1, 8) + (Bookshelves / 2) + Random.RandInt(0, Bookshelves));
int TopSlot = std::max(Base / 3, 1);
int MiddleSlot = (Base * 2) / 3 + 1;
int BottomSlot = std::max(Base, Bookshelves * 2);

View File

@ -245,22 +245,20 @@ void cWorld::CastThunderbolt (int a_BlockX, int a_BlockY, int a_BlockZ)
int cWorld::GetDefaultWeatherInterval(eWeather a_Weather)
{
auto & Random = GetRandomProvider();
switch (a_Weather)
{
case eWeather_Sunny:
{
auto dif = m_MaxSunnyTicks - m_MinSunnyTicks + 1;
return m_MinSunnyTicks + (m_TickRand.randInt() % dif);
return Random.RandInt(m_MinSunnyTicks, m_MaxSunnyTicks);
}
case eWeather_Rain:
{
auto dif = m_MaxRainTicks - m_MinRainTicks + 1;
return m_MinRainTicks + (m_TickRand.randInt() % dif);
return Random.RandInt(m_MinRainTicks, m_MaxRainTicks);
}
case eWeather_ThunderStorm:
{
auto dif = m_MaxThunderStormTicks - m_MinThunderStormTicks + 1;
return m_MinThunderStormTicks + (m_TickRand.randInt() % dif);
return Random.RandInt(m_MinThunderStormTicks, m_MaxThunderStormTicks);
}
}
@ -812,7 +810,7 @@ eWeather cWorld::ChooseNewWeather()
case eWeather_Rain:
{
// 1 / 8 chance of turning into a thunderstorm
return ((m_TickRand.randInt() % 256) < 32) ? eWeather_ThunderStorm : eWeather_Sunny;
return GetRandomProvider().RandBool(0.125) ? eWeather_ThunderStorm : eWeather_Sunny;
}
}
@ -1074,7 +1072,7 @@ void cWorld::TickWeather(float a_Dt)
if (m_Weather == eWeather_ThunderStorm)
{
// 0.5% chance per tick of thunderbolt
if (m_TickRand.randInt() % 199 == 0)
if (GetRandomProvider().RandBool(0.005))
{
CastThunderbolt(0, 0, 0); // TODO: find random positions near players to cast thunderbolts.
}
@ -1697,7 +1695,7 @@ void cWorld::GrowTreeImage(const sSetBlockVector & a_Blocks)
bool cWorld::GrowRipePlant(int a_BlockX, int a_BlockY, int a_BlockZ, bool a_IsByBonemeal)
{
cFastRandom random;
auto & random = GetRandomProvider();
BLOCKTYPE BlockType;
NIBBLETYPE BlockMeta;
GetBlockTypeMeta(a_BlockX, a_BlockY, a_BlockZ, BlockType, BlockMeta);
@ -1735,7 +1733,7 @@ bool cWorld::GrowRipePlant(int a_BlockX, int a_BlockY, int a_BlockZ, bool a_IsBy
}
else
{
BlockMeta += random.NextInt(4) + 2;
BlockMeta += random.RandInt(2, 5);
BlockMeta = std::min(BlockMeta, static_cast<NIBBLETYPE>(7));
}
FastSetBlock(a_BlockX, a_BlockY, a_BlockZ, BlockType, BlockMeta);
@ -1770,7 +1768,7 @@ bool cWorld::GrowRipePlant(int a_BlockX, int a_BlockY, int a_BlockZ, bool a_IsBy
}
else
{
BlockMeta += random.NextInt(4) + 2;
BlockMeta += random.RandInt(2, 5);
BlockMeta = std::min(BlockMeta, static_cast<NIBBLETYPE>(7));
}
FastSetBlock(a_BlockX, a_BlockY, a_BlockZ, BlockType, BlockMeta);
@ -1793,7 +1791,7 @@ bool cWorld::GrowRipePlant(int a_BlockX, int a_BlockY, int a_BlockZ, bool a_IsBy
}
else
{
BlockMeta += random.NextInt(4) + 2;
BlockMeta += random.RandInt(2, 5);
BlockMeta = std::min(BlockMeta, static_cast<NIBBLETYPE>(7));
}
FastSetBlock(a_BlockX, a_BlockY, a_BlockZ, BlockType, BlockMeta);
@ -1825,7 +1823,7 @@ bool cWorld::GrowRipePlant(int a_BlockX, int a_BlockY, int a_BlockZ, bool a_IsBy
}
else
{
BlockMeta += random.NextInt(4) + 2;
BlockMeta += random.RandInt(2, 5);
BlockMeta = std::min(BlockMeta, static_cast<NIBBLETYPE>(7));
}
FastSetBlock(a_BlockX, a_BlockY, a_BlockZ, BlockType, BlockMeta);
@ -1848,7 +1846,7 @@ bool cWorld::GrowRipePlant(int a_BlockX, int a_BlockY, int a_BlockZ, bool a_IsBy
}
else
{
BlockMeta += random.NextInt(4) + 2;
BlockMeta += random.RandInt(2, 5);
BlockMeta = std::min(BlockMeta, static_cast<NIBBLETYPE>(7));
}
FastSetBlock(a_BlockX, a_BlockY, a_BlockZ, BlockType, BlockMeta);
@ -1884,14 +1882,14 @@ bool cWorld::GrowRipePlant(int a_BlockX, int a_BlockY, int a_BlockZ, bool a_IsBy
{
++GrowState;
}
else if (random.NextInt(99) < 45)
else if (random.RandBool(0.45))
{
++GrowState;
}
FastSetBlock(a_BlockX, a_BlockY, a_BlockZ, BlockType, static_cast<NIBBLETYPE>(GrowState << 3 | TypeMeta));
}
else if (random.NextInt(99) < 45)
else if (random.RandBool(0.45))
{
GrowTreeFromSapling(a_BlockX, a_BlockY, a_BlockZ, BlockMeta);
}
@ -1905,12 +1903,12 @@ bool cWorld::GrowRipePlant(int a_BlockX, int a_BlockY, int a_BlockZ, bool a_IsBy
{
return false;
}
MTRand r1;
auto & r1 = GetRandomProvider();
for (int i = 0; i < 60; i++)
{
int OfsX = static_cast<int>(r1.randInt(3) + r1.randInt(3) + r1.randInt(3) + r1.randInt(3)) / 2 - 3;
int OfsY = static_cast<int>(r1.randInt(3) + r1.randInt(3)) - 3;
int OfsZ = static_cast<int>(r1.randInt(3) + r1.randInt(3) + r1.randInt(3) + r1.randInt(3)) / 2 - 3;
int OfsX = (r1.RandInt(3) + r1.RandInt(3) + r1.RandInt(3) + r1.RandInt(3)) / 2 - 3;
int OfsY = r1.RandInt(3) + r1.RandInt(3) - 3;
int OfsZ = (r1.RandInt(3) + r1.RandInt(3) + r1.RandInt(3) + r1.RandInt(3)) / 2 - 3;
BLOCKTYPE Ground = GetBlock(a_BlockX + OfsX, a_BlockY + OfsY, a_BlockZ + OfsZ);
if (Ground != E_BLOCK_GRASS)
{
@ -1923,7 +1921,7 @@ bool cWorld::GrowRipePlant(int a_BlockX, int a_BlockY, int a_BlockZ, bool a_IsBy
}
BLOCKTYPE SpawnType;
NIBBLETYPE SpawnMeta = 0;
switch (r1.randInt(10))
switch (r1.RandInt(10))
{
case 0: SpawnType = E_BLOCK_YELLOW_FLOWER; break;
case 1: SpawnType = E_BLOCK_RED_ROSE; break;
@ -2031,8 +2029,7 @@ int cWorld::GrowCactus(int a_BlockX, int a_BlockY, int a_BlockZ, int a_NumBlocks
bool cWorld::GrowMelonPumpkin(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType)
{
MTRand Rand;
return m_ChunkMap->GrowMelonPumpkin(a_BlockX, a_BlockY, a_BlockZ, a_BlockType, Rand);
return m_ChunkMap->GrowMelonPumpkin(a_BlockX, a_BlockY, a_BlockZ, a_BlockType);
}
@ -2162,6 +2159,7 @@ bool cWorld::WriteBlockArea(cBlockArea & a_Area, int a_MinBlockX, int a_MinBlock
void cWorld::SpawnItemPickups(const cItems & a_Pickups, double a_BlockX, double a_BlockY, double a_BlockZ, double a_FlyAwaySpeed, bool IsPlayerCreated)
{
auto & Random = GetRandomProvider();
a_FlyAwaySpeed /= 100; // Pre-divide, so that we don't have to divide each time inside the loop
for (cItems::const_iterator itr = a_Pickups.begin(); itr != a_Pickups.end(); ++itr)
{
@ -2171,9 +2169,9 @@ void cWorld::SpawnItemPickups(const cItems & a_Pickups, double a_BlockX, double
continue;
}
float SpeedX = static_cast<float>(a_FlyAwaySpeed * (GetTickRandomNumber(10) - 5));
float SpeedY = static_cast<float>(a_FlyAwaySpeed * GetTickRandomNumber(50));
float SpeedZ = static_cast<float>(a_FlyAwaySpeed * (GetTickRandomNumber(10) - 5));
float SpeedX = static_cast<float>(a_FlyAwaySpeed * Random.RandInt(-5, 5));
float SpeedY = static_cast<float>(a_FlyAwaySpeed * Random.RandInt(50));
float SpeedZ = static_cast<float>(a_FlyAwaySpeed * Random.RandInt(-5, 5));
cPickup * Pickup = new cPickup(
a_BlockX, a_BlockY, a_BlockZ,
@ -2310,10 +2308,11 @@ UInt32 cWorld::SpawnPrimedTNT(double a_X, double a_Y, double a_Z, int a_FuseTick
TNT = nullptr;
return cEntity::INVALID_ID;
}
auto & Random = GetRandomProvider();
TNT->SetSpeed(
a_InitialVelocityCoeff * (GetTickRandomNumber(2) - 1), /** -1, 0, 1 */
a_InitialVelocityCoeff * Random.RandInt(-1, 1),
a_InitialVelocityCoeff * 2,
a_InitialVelocityCoeff * (GetTickRandomNumber(2) - 1)
a_InitialVelocityCoeff * Random.RandInt(-1, 1)
);
return TNT->GetUniqueID();
}
@ -3793,6 +3792,15 @@ UInt32 cWorld::CreateProjectile(double a_PosX, double a_PosY, double a_PosZ, cPr
int cWorld::GetTickRandomNumber(int a_Range)
{
return GetRandomProvider().RandInt(a_Range);
}
void cWorld::TabCompleteUserName(const AString & a_Text, AStringVector & a_Results)
{
typedef std::pair<AString::size_type, AString> pair_t;

View File

@ -28,7 +28,6 @@
#include "MapManager.h"
#include "Blocks/WorldInterface.h"
#include "Blocks/BroadcastInterface.h"
#include "FastRandom.h"
#include "EffectID.h"
@ -801,8 +800,8 @@ public:
Item parameter is currently used for Fireworks to correctly set entity metadata based on item metadata. */
UInt32 CreateProjectile(double a_PosX, double a_PosY, double a_PosZ, cProjectileEntity::eKind a_Kind, cEntity * a_Creator, const cItem * a_Item, const Vector3d * a_Speed = nullptr); // tolua_export
/** Returns a random number from the m_TickRand in range [0 .. a_Range]. To be used only in the tick thread! */
int GetTickRandomNumber(int a_Range) { return static_cast<int>(m_TickRand.randInt(a_Range)); }
/** Returns a random number in range [0 .. a_Range]. */
int GetTickRandomNumber(int a_Range);
/** Appends all usernames starting with a_Text (case-insensitive) into Results */
void TabCompleteUserName(const AString & a_Text, AStringVector & a_Results);
@ -880,9 +879,6 @@ private:
/** The dimension of the world, used by the client to provide correct lighting scheme */
eDimension m_Dimension;
/** This random generator is to be used only in the Tick() method, and thus only in the World-Tick-thread (MTRand is not exactly thread-safe) */
MTRand m_TickRand;
bool m_IsSpawnExplicitlySet;
double m_SpawnX;
double m_SpawnY;

View File

@ -15,14 +15,13 @@ static void TestInts(void)
cFastRandom rnd;
int sum = 0;
const int BUCKETS = 8;
int Counts[BUCKETS];
memset(Counts, 0, sizeof(Counts));
int Counts[BUCKETS] = {0};
const int ITER = 10000;
for (int i = 0; i < ITER; i++)
{
int v = rnd.NextInt(1000);
int v = rnd.RandInt(1000);
assert_test(v >= 0);
assert_test(v < 1000);
assert_test(v <= 1000);
Counts[v % BUCKETS]++;
sum += v;
}
@ -43,12 +42,11 @@ static void TestFloats(void)
cFastRandom rnd;
float sum = 0;
const int BUCKETS = 8;
int Counts[BUCKETS];
memset(Counts, 0, sizeof(Counts));
int Counts[BUCKETS] = {0};
const int ITER = 10000;
for (int i = 0; i < ITER; i++)
{
float v = rnd.NextFloat(1000);
float v = rnd.RandReal(1000.0f);
assert_test(v >= 0);
assert_test(v <= 1000);
Counts[static_cast<int>(v) % BUCKETS]++;
@ -76,7 +74,7 @@ static void TestReCreation(void)
for (int i = 0; i < ITER; ++i)
{
cFastRandom rnd;
int val = rnd.NextInt(10);
int val = rnd.RandInt(9);
if (val == lastVal)
{
numSame += 1;