1
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; cItems Pickups;
Pickups.push_back(m_Contents.RemoveOneItem(a_SlotNum)); 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; int PickupSpeedX = 0, PickupSpeedY = 0, PickupSpeedZ = 0;
switch (Meta & E_META_DROPSPENSER_FACING_MASK) 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) 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); m_World->BroadcastBlockEntity(m_PosX, m_PosY, m_PosZ);
} }
@ -138,7 +138,7 @@ void cMobSpawnerEntity::SpawnEntity(void)
virtual bool Item(cChunk * a_Chunk) virtual bool Item(cChunk * a_Chunk)
{ {
cFastRandom Random; auto & Random = GetRandomProvider();
bool EntitiesSpawned = false; bool EntitiesSpawned = false;
for (size_t i = 0; i < 4; i++) for (size_t i = 0; i < 4; i++)
@ -148,9 +148,9 @@ void cMobSpawnerEntity::SpawnEntity(void)
break; break;
} }
int RelX = static_cast<int>(m_RelX + 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.NextInt(3) - 1; int RelY = m_RelY + Random.RandInt(-1, 1);
int RelZ = static_cast<int>(m_RelZ + static_cast<double>(Random.NextFloat() - Random.NextFloat()) * 4.0); int RelZ = m_RelZ + static_cast<int>((Random.RandReal<double>() - Random.RandReal<double>()) * 4.0);
cChunk * Chunk = a_Chunk->GetRelNeighborChunkAdjustCoords(RelX, RelZ); cChunk * Chunk = a_Chunk->GetRelNeighborChunkAdjustCoords(RelX, RelZ);
if ((Chunk == nullptr) || !Chunk->IsValid()) if ((Chunk == nullptr) || !Chunk->IsValid())
@ -171,7 +171,7 @@ void cMobSpawnerEntity::SpawnEntity(void)
} }
Monster->SetPosition(PosX, RelY, PosZ); 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) if (Chunk->GetWorld()->SpawnMobFinalize(Monster) != cEntity::INVALID_ID)
{ {
EntitiesSpawned = true; EntitiesSpawned = true;

View File

@ -67,8 +67,7 @@ public:
) )
) )
{ {
MTRand r1; if (GetRandomProvider().RandBool(0.10))
if (r1.randInt(10) == 5)
{ {
cItems Pickups; cItems Pickups;
if (FlowerMeta == E_META_BIG_FLOWER_DOUBLE_TALL_GRASS) 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 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 (GetRandomProvider().RandBool(0.20))
if (Random.NextInt(5) == 0)
{ {
NIBBLETYPE Meta = a_Chunk.GetMeta(a_RelX, a_RelY, a_RelZ); NIBBLETYPE Meta = a_Chunk.GetMeta(a_RelX, a_RelY, a_RelZ);
NIBBLETYPE TypeMeta = Meta & 0x03; NIBBLETYPE TypeMeta = Meta & 0x03;

View File

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

View File

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

View File

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

View File

@ -18,10 +18,8 @@ public:
virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override
{ {
cFastRandom Random;
// Add more than one dust // 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 virtual ColourID GetMapBaseColourID(NIBBLETYPE a_Meta) override

View File

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

View File

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

View File

@ -37,22 +37,22 @@ public:
virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override 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. // 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. // 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)) if ((m_BlockType == E_BLOCK_LEAVES) && ((a_BlockMeta & 0x03) == E_META_LEAVES_JUNGLE))
{ {
// Jungle leaves have a 2.5% chance of dropping a sapling. // Jungle leaves have a 2.5% chance of dropping a sapling.
chance = rand.NextInt(40); chance = 0.025;
} }
else else
{ {
// Other leaves have a 5% chance of dropping a sapling. // 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( a_Pickups.push_back(
cItem( cItem(
@ -66,7 +66,7 @@ public:
// 0.5 % chance of dropping an apple, if the leaves' type is Apple Leaves // 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 ((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)); 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 virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override
{ {
cFastRandom Random; a_Pickups.emplace_back(E_ITEM_MELON_SLICE, GetRandomProvider().RandInt<char>(3, 7), 0);
a_Pickups.push_back(cItem(E_ITEM_MELON_SLICE, static_cast<char>(3 + Random.NextInt(5)), 0));
} }
virtual ColourID GetMapBaseColourID(NIBBLETYPE a_Meta) override virtual ColourID GetMapBaseColourID(NIBBLETYPE a_Meta) override

View File

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

View File

@ -45,8 +45,8 @@ public:
return; return;
} }
cFastRandom Random; auto & Random = GetRandomProvider();
int Reward = 15 + Random.NextInt(15) + Random.NextInt(15); 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); 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 virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_Meta) override
{ {
cFastRandom rand; auto & rand = GetRandomProvider();
if (a_Meta == 0x3) if (a_Meta == 0x3)
{ {
// Fully grown, drop the entire produce: // 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 else
{ {

View File

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

View File

@ -75,10 +75,9 @@ protected:
*/ */
virtual PlantAction CanGrow(cChunk & a_Chunk, int a_RelX, int a_RelY, int a_RelZ) 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 // 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); 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; 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 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 (GetRandomProvider().RandBool(0.9995))
if (Random.NextInt(2000) != 0)
{ {
return; return;
} }

View File

@ -37,16 +37,16 @@ public:
// Only grow if we have the right amount of light // Only grow if we have the right amount of light
if (Light > 8) 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. // 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 BlockX = a_RelX + a_Chunk.GetPosX() * cChunkDef::Width;
int BlockZ = a_RelZ + a_Chunk.GetPosZ() * cChunkDef::Width; int BlockZ = a_RelZ + a_Chunk.GetPosZ() * cChunkDef::Width;
a_Chunk.GetWorld()->GrowTree(BlockX, a_RelY, BlockZ); a_Chunk.GetWorld()->GrowTree(BlockX, a_RelY, BlockZ);
} }
// Only move to the next growth stage if we haven't gone there yet // 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); 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 virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override
{ {
// Reset meta to 0 // Reset meta to 0
cFastRandom Random; a_Pickups.emplace_back(E_ITEM_PRISMARINE_CRYSTALS, GetRandomProvider().RandInt<char>(2, 3), 0);
a_Pickups.push_back(cItem(E_ITEM_PRISMARINE_CRYSTALS, static_cast<char>(2 + Random.NextInt(2)), 0));
} }
} ; } ;

View File

@ -26,8 +26,7 @@ public:
virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override
{ {
// Drop seeds, sometimes // Drop seeds, sometimes
cFastRandom Random; if (GetRandomProvider().RandBool(0.125))
if (Random.NextInt(8) == 0)
{ {
a_Pickups.push_back(cItem(E_ITEM_SEEDS, 1, 0)); a_Pickups.push_back(cItem(E_ITEM_SEEDS, 1, 0));
} }
@ -47,7 +46,7 @@ public:
// Spawn the pickups: // Spawn the pickups:
if (!Drops.empty()) if (!Drops.empty())
{ {
MTRand r1; auto & r1 = GetRandomProvider();
// Mid-block position first // Mid-block position first
double MicroX, MicroY, MicroZ; double MicroX, MicroY, MicroZ;
@ -56,8 +55,8 @@ public:
MicroZ = a_BlockZ + 0.5; MicroZ = a_BlockZ + 0.5;
// Add random offset second // Add random offset second
MicroX += r1.rand(1) - 0.5; MicroX += r1.RandReal<double>(-0.5, 0.5);
MicroZ += r1.rand(1) - 0.5; MicroZ += r1.RandReal<double>(-0.5, 0.5);
a_WorldInterface.SpawnItemPickups(Drops, MicroX, MicroY, MicroZ); 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) 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); ASSERT(
int Random = m_World->GetTickRandomNumber(0x00ffffff); (a_MaxX > 0) && (a_MaxY > 0) && (a_MaxZ > 0) &&
a_X = Random % (a_MaxX * 2); (a_MaxX <= std::numeric_limits<int>::max() / a_MaxY) && // a_MaxX * a_MaxY doesn't overflow
a_Y = (Random / (a_MaxX * 2)) % (a_MaxY * 2); (a_MaxX * a_MaxY <= std::numeric_limits<int>::max() / a_MaxZ) // a_MaxX * a_MaxY * a_MaxZ doesn't overflow
a_Z = ((Random / (a_MaxX * 2)) / (a_MaxY * 2)) % (a_MaxZ * 2); );
a_X /= 2;
a_Y /= 2; // MTRand gives an inclusive range [0, Max] but this gives the exclusive range [0, Max)
a_Z /= 2; 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() void cChunk::ApplyWeatherToTop()
{ {
if ( if (
(m_World->GetTickRandomNumber(100) != 0) || (GetRandomProvider().RandBool(0.99)) ||
( (
(m_World->GetWeather() != eWeather_Rain) && (m_World->GetWeather() != eWeather_Rain) &&
(m_World->GetWeather() != eWeather_ThunderStorm) (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 // Convert the stem BlockType into produce BlockType
BLOCKTYPE ProduceType; BLOCKTYPE ProduceType;
switch (a_BlockType) 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: // Pick a direction in which to place the produce:
int x = 0, z = 0; 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) switch (CheckType)
{ {
case 0: x = 1; break; 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: case E_BLOCK_FARMLAND:
{ {
// Place a randomly-facing produce: // 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", 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, a_RelX + x + m_PosX * cChunkDef::Width, a_RelY, a_RelZ + z + m_PosZ * cChunkDef::Width,
x, z, x, z,

View File

@ -27,7 +27,6 @@ namespace Json
class cWorld; class cWorld;
class cClientHandle; class cClientHandle;
class cServer; class cServer;
class MTRand;
class cPlayer; class cPlayer;
class cChunkMap; class cChunkMap;
class cBeaconEntity; class cBeaconEntity;
@ -609,7 +608,7 @@ private:
bool GrowTallGrass (int a_RelX, int a_RelY, int a_RelZ); 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 */ /** 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 */ /** 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); 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: case E_BLOCK_TNT:
{ {
// Activate the TNT, with a random fuse between 10 to 30 game ticks // 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); 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); area.SetBlockTypeMeta(bx + x, by + y, bz + z, E_BLOCK_AIR, 0);
a_BlocksAffected.push_back(Vector3i(bx + x, by + y, bz + z)); 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: 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; cItems Drops;
cBlockHandler * Handler = BlockHandler(Block); 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. 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); 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 the block is shrapnel-able, make a falling block entity out of it:
if ( 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; int ChunkX, ChunkZ;
cChunkDef::AbsoluteToRelative(a_BlockX, a_BlockY, a_BlockZ, 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); cChunkPtr Chunk = GetChunkNoLoad(ChunkX, ChunkZ);
if (Chunk != nullptr) 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; return false;
} }

View File

@ -15,7 +15,6 @@
class cWorld; class cWorld;
class cWorldInterface; class cWorldInterface;
class cItem; class cItem;
class MTRand;
class cChunkStay; class cChunkStay;
class cChunk; class cChunk;
class cPlayer; class cPlayer;
@ -362,7 +361,7 @@ public:
void GetChunkStats(int & a_NumChunksValid, int & a_NumChunksDirty); 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 */ /** 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 */ /** 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); 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) cEnchantments cEnchantments::GetRandomEnchantmentFromVector(cWeightedEnchantments & a_Enchantments)
{ {
cFastRandom Random;
int AllWeights = 0; int AllWeights = 0;
for (cWeightedEnchantments::iterator it = a_Enchantments.begin(); it != a_Enchantments.end(); ++it) for (cWeightedEnchantments::iterator it = a_Enchantments.begin(); it != a_Enchantments.end(); ++it)
{ {
AllWeights += (*it).m_Weight; 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) for (cWeightedEnchantments::iterator it = a_Enchantments.begin(); it != a_Enchantments.end(); ++it)
{ {
RandomNumber -= (*it).m_Weight; RandomNumber -= (*it).m_Weight;

View File

@ -495,12 +495,11 @@ bool cEntity::DoTakeDamage(TakeDamageInfo & a_TDI)
{ {
int Chance = static_cast<int>(ThornsLevel * 15); int Chance = static_cast<int>(ThornsLevel * 15);
cFastRandom Random; auto & Random = GetRandomProvider();
int RandomValue = Random.GenerateRandomInteger(0, 100);
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; TotalEPF = 25;
} }
cFastRandom Random; float RandomValue = GetRandomProvider().RandReal(0.5f, 1.0f);
float RandomValue = Random.GenerateRandomInteger(50, 100) * 0.01f;
TotalEPF = ceil(TotalEPF * RandomValue); 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. // 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->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(); Destroy();
} }

View File

@ -128,6 +128,8 @@ void cFloater::SpawnOn(cClientHandle & a_Client)
void cFloater::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) void cFloater::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
{ {
auto & Random = GetRandomProvider();
HandlePhysics(a_Dt, a_Chunk); HandlePhysics(a_Dt, a_Chunk);
if (IsBlockWater(m_World->GetBlock(POSX_TOINT, POSY_TOINT, POSZ_TOINT)) if (IsBlockWater(m_World->GetBlock(POSX_TOINT, POSY_TOINT, POSZ_TOINT))
&& (m_World->GetBlockMeta(POSX_TOINT, POSY_TOINT, POSX_TOINT) == 0)) && (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); SetPosY(GetPosY() - 1);
m_CanPickupItem = true; m_CanPickupItem = true;
m_PickupCountDown = 20; m_PickupCountDown = 20;
m_CountDownTime = 100 + m_World->GetTickRandomNumber(800); m_CountDownTime = Random.RandInt(100, 900);
LOGD("Floater %i can be picked up", GetUniqueID()); 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. 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()); 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); m_World->GetBroadcaster().BroadcastParticleEffect("splash", static_cast<Vector3f>(m_ParticlePos), Vector3f{}, 0, 15);
} }
else if (m_CountDownTime < 20) else if (m_CountDownTime < 20)
@ -159,14 +161,14 @@ void cFloater::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
m_CountDownTime--; m_CountDownTime--;
if (m_World->GetHeight(POSX_TOINT, POSZ_TOINT) == POSY_TOINT) 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--; m_CountDownTime--;
} }
} }
else // if the floater is underground it has a 50% chance of not decreasing the countdown. 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++; m_CountDownTime++;
} }

View File

@ -1069,9 +1069,9 @@ void cPlayer::KilledBy(TakeDamageInfo & a_TDI)
{ {
case dtRangedAttack: DamageText = "was shot"; break; case dtRangedAttack: DamageText = "was shot"; break;
case dtLightning: DamageText = "was plasmified by lightining"; 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 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 dtStarving: DamageText = "forgot the importance of food"; break;
case dtCactusContact: DamageText = "was impaled on a cactus"; break; case dtCactusContact: DamageText = "was impaled on a cactus"; break;
case dtLavaContact: DamageText = "was melted by lava"; 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)); int UnbreakingLevel = static_cast<int>(Item.m_Enchantments.GetLevel(cEnchantments::enchUnbreaking));
if (UnbreakingLevel > 0) if (UnbreakingLevel > 0)
{ {
int chance; double chance = 0.0;
if (ItemCategory::IsArmor(Item.m_ItemType)) if (ItemCategory::IsArmor(Item.m_ItemType))
{ {
chance = 60 + (40 / (UnbreakingLevel + 1)); chance = 0.6 + (0.4 / (UnbreakingLevel + 1));
} }
else else
{ {
chance = 100 / (UnbreakingLevel + 1); chance = 1.0 / (UnbreakingLevel + 1);
} }
cFastRandom Random; if (GetRandomProvider().RandBool(chance))
if (Random.NextInt(101) <= chance)
{ {
return; 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) 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); 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);
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 // FastRandom.cpp
// Implements the cFastRandom class representing a fast random number generator
#include "Globals.h" #include "Globals.h"
#include "FastRandom.h" #include "FastRandom.h"
#include <mutex>
#include <random> #include <random>
#if defined (__GNUC__) #if defined (__GNUC__)
@ -13,109 +13,50 @@
#elif defined (_MSC_VER) #elif defined (_MSC_VER)
#define ATTRIBUTE_TLS static __declspec(thread) #define ATTRIBUTE_TLS static __declspec(thread)
#else #else
#error "Unknown thread local storage qualifier" #define ATTRIBUTE_TLS thread_local
#endif #endif
static unsigned int GetRandomSeed()
MTRand & GetRandomProvider()
{ {
ATTRIBUTE_TLS bool SeedCounterInitialized = 0; // Some compilers don't support thread_local for non-POD types, this is purely a work around for that restriction.
ATTRIBUTE_TLS unsigned int SeedCounter = 0; // 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) if (!SeedCounterInitialized)
{ {
std::random_device rd; std::random_device rd;
std::uniform_int_distribution<unsigned int> dist; std::uniform_int_distribution<UInt32> dist;
SeedCounter = dist(rd); SeedCounter = dist(rd);
SeedCounterInitialized = true; SeedCounterInitialized = true;
} }
return ++SeedCounter; 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 // 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. The cFastRandom alias should be avoided in favor of the result of a call to GetRandomProvider().
It is fast to instantiate, fast to query next, and partially multi-thread-safe. The MTRand generator used is faster, has a better range and provides higher quality randomness.
It is multi-thread-safe in the sense that it can be accessed from multiple threads without crashing, but it may Note that MTRand is relatively costly to construct and so instances should be long lived,
yield duplicate numbers in that case. prefer calls to GetRandomProvider over creating new instances.
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.
*/ */
@ -23,58 +16,195 @@ salts, the values they get will be different.
#pragma once #pragma once
#include <random> #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: 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 */ /** Initialize with a SeedSequence. */
int NextInt(int a_Range); 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] */ /** Return a random IntType in the range [a_Min, a_Max]. */
int GenerateRandomInteger(int a_Begin, int a_End); 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: 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); template <class Itr>
void generate(Itr first, Itr last)
/** Returns a random integer in the range [0 .. a_Range]. */ {
int randInt(int a_Range); std::random_device rd;
std::uniform_int_distribution<result_type> dist;
/** Returns a random integer in the range [0 .. MAX_INT]. */ for (; first != last; ++first)
int randInt(void); {
*first = dist(rd);
/** Returns a random floating point number in the range [0 .. a_Range]. */ }
double rand(double a_Range); }
private:
std::mt19937 m_MersenneRand;
}; };
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 else
{ {
MTRand rnd; m_Seed = GetRandomProvider().RandInt();
m_Seed = rnd.randInt();
LOGINFO("Chosen a new random seed for world: %d", m_Seed); LOGINFO("Chosen a new random seed for world: %d", m_Seed);
a_IniFile.SetValueI("Seed", "Seed", 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 RelStartZ = Clamp(a_StartZ - BlockZ, 0, cChunkDef::Width - 1);
int RelEndX = Clamp(a_EndX - BlockX, 0, cChunkDef::Width); int RelEndX = Clamp(a_EndX - BlockX, 0, cChunkDef::Width);
int RelEndZ = Clamp(a_EndZ - BlockZ, 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 y = a_StartY; y < a_EndY; y++)
{ {
for (int z = RelStartZ; z < RelEndZ; z++) for (int z = RelStartZ; z < RelEndZ; z++)
@ -171,7 +171,7 @@ protected:
{ {
if (cBlockInfo::CanBeTerraformed(a_ChunkDesc.GetBlockType(x, y, z))) 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); a_ChunkDesc.SetBlockType(x, y, z, BlockType);
} }
} // for x } // for x

View File

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

View File

@ -108,6 +108,8 @@ public:
return false; return false;
} }
auto & Random = GetRandomProvider();
if (a_Player->IsFishing()) if (a_Player->IsFishing())
{ {
cFloaterCallback FloaterInfo; cFloaterCallback FloaterInfo;
@ -122,10 +124,10 @@ public:
else if (FloaterInfo.CanPickup()) else if (FloaterInfo.CanPickup())
{ {
cItems Drops; cItems Drops;
int ItemCategory = a_World->GetTickRandomNumber(99); int ItemCategory = Random.RandInt(99);
if (ItemCategory <= 4) // Treasures 5% if (ItemCategory <= 4) // Treasures 5%
{ {
int Treasure = a_World->GetTickRandomNumber(5); int Treasure = Random.RandInt(5);
switch (Treasure) switch (Treasure)
{ {
case 0: case 0:
@ -140,7 +142,7 @@ public:
} }
case 2: 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; break;
} }
case 3: case 3:
@ -164,14 +166,14 @@ public:
} }
else if (ItemCategory <= 14) // Junk 10% else if (ItemCategory <= 14) // Junk 10%
{ {
int Junk = a_World->GetTickRandomNumber(70); int Junk = Random.RandInt(70);
if (Junk <= 1) if (Junk <= 1)
{ {
Drops.Add(cItem(E_ITEM_DYE, 10, 0)); Drops.Add(cItem(E_ITEM_DYE, 10, 0));
} }
else if (Junk <= 4) 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) else if (Junk <= 9)
{ {
@ -214,7 +216,7 @@ public:
} }
else // Fish else // Fish
{ {
int FishType = a_World->GetTickRandomNumber(99); int FishType = Random.RandInt(99);
if (FishType <= 1) // Clownfish has a 2% chance of spawning if (FishType <= 1) // Clownfish has a 2% chance of spawning
{ {
Drops.Add(cItem(E_ITEM_RAW_FISH, 1, E_META_RAW_FISH_CLOWNFISH)); Drops.Add(cItem(E_ITEM_RAW_FISH, 1, E_META_RAW_FISH_CLOWNFISH));
@ -250,7 +252,7 @@ public:
} }
else 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)) if (!Floater->Initialize(*a_World))
{ {
delete Floater; delete Floater;

View File

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

View File

@ -36,8 +36,7 @@ public:
Vector3d Speed = a_Player->GetLookVector() * m_SpeedCoeff; Vector3d Speed = a_Player->GetLookVector() * m_SpeedCoeff;
// Play sound // 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 / GetRandomProvider().RandReal(0.8f, 1.2f));
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));
if (a_World->CreateProjectile(Pos.x, Pos.y, Pos.z, m_ProjectileKind, a_Player, &a_Player->GetEquippedItem(), &Speed) == cEntity::INVALID_ID) 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) if (GetDimension() == dimNether)
{ {
cFastRandom Random;
// TODO 2014-02-19 xdot: Refine // TODO 2014-02-19 xdot: Refine
Rot = Random.NextInt(16); Rot = GetRandomProvider().RandInt(15);
} }
else else
{ {

View File

@ -111,12 +111,8 @@ eMonsterType cMobSpawner::ChooseMobType(EMCSBiome a_Biome)
if (allowedMobsSize > 0) if (allowedMobsSize > 0)
{ {
std::set<eMonsterType>::iterator itr = allowedMobs.begin(); std::set<eMonsterType>::iterator itr = allowedMobs.begin();
int iRandom = m_Random.NextInt(static_cast<int>(allowedMobsSize));
for (int i = 0; i < iRandom; i++) std::advance(itr, GetRandomProvider().RandInt<size_t>(allowedMobsSize - 1));
{
++itr;
}
return *itr; 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. 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); 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); 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) (BlockBelow == E_BLOCK_GRASS) || (BlockBelow == E_BLOCK_LEAVES) || (BlockBelow == E_BLOCK_NEW_LEAVES)
) && ) &&
(a_RelY >= 62) && (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)) && (!cBlockInfo::IsTransparent(BlockBelow)) &&
(SkyLight <= 7) && (SkyLight <= 7) &&
(BlockLight <= 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)) && (!cBlockInfo::IsTransparent(BlockBelow)) &&
(SkyLight <= 7) && (SkyLight <= 7) &&
(BlockLight <= 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) && (TargetBlock == E_BLOCK_AIR) &&
(BlockAbove == E_BLOCK_AIR) && (BlockAbove == E_BLOCK_AIR) &&
(!cBlockInfo::IsTransparent(BlockBelow)) && (!cBlockInfo::IsTransparent(BlockBelow)) &&
(Random.NextInt(20) == 0) (Random.RandBool(0.05))
); );
} }

View File

@ -5,7 +5,6 @@
#include "BlockID.h" #include "BlockID.h"
#include "ChunkDef.h" #include "ChunkDef.h"
#include "Chunk.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 #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; bool m_NewPack;
eMonsterType m_MobType; eMonsterType m_MobType;
std::set<cMonster*> m_Spawned; 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 return; // Babies don't lay eggs
} }
if ((m_EggDropTimer == 6000) && (m_World->GetTickRandomNumber(1) == 0)) if ((m_EggDropTimer == 6000) && GetRandomProvider().RandBool())
{ {
cItems Drops; cItems Drops;
m_EggDropTimer = 0; m_EggDropTimer = 0;

View File

@ -41,16 +41,18 @@ void cHorse::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
return; return;
} }
auto & Random = GetRandomProvider();
if (!m_bIsMouthOpen) if (!m_bIsMouthOpen)
{ {
if (m_World->GetTickRandomNumber(50) == 25) if (Random.RandBool(0.02))
{ {
m_bIsMouthOpen = true; m_bIsMouthOpen = true;
} }
} }
else else
{ {
if (m_World->GetTickRandomNumber(10) == 5) if (Random.RandBool(0.10))
{ {
m_bIsMouthOpen = false; 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_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_EAST));
m_World->BroadcastSoundParticleEffect(EffectID::PARTICLE_SMOKE, FloorC(GetPosX()), FloorC(GetPosY()), FloorC(GetPosZ()), int(SmokeDirection::SOUTH_WEST)); 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 mtOcelot:
case mtWolf: case mtWolf:
{ {
Reward = m_World->GetTickRandomNumber(2) + 1; Reward = GetRandomProvider().RandInt(1, 3);
break; break;
} }
@ -531,7 +531,7 @@ void cMonster::KilledBy(TakeDamageInfo & a_TDI)
case mtSlime: case mtSlime:
case mtMagmaCube: case mtMagmaCube:
{ {
Reward = 6 + (m_World->GetTickRandomNumber(2)); Reward = GetRandomProvider().RandInt(6, 8);
break; break;
} }
case mtBlaze: case mtBlaze:
@ -657,13 +657,15 @@ void cMonster::InStateIdle(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
if (m_IdleInterval > std::chrono::seconds(1)) if (m_IdleInterval > std::chrono::seconds(1))
{ {
auto & Random = GetRandomProvider();
// At this interval the results are predictable // 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 m_IdleInterval -= std::chrono::seconds(1); // So nothing gets dropped when the server hangs for a few seconds
Vector3d Dist; Vector3d Dist;
Dist.x = static_cast<double>(m_World->GetTickRandomNumber(10)) - 5.0; Dist.x = static_cast<double>(Random.RandInt(-5, 5));
Dist.z = static_cast<double>(m_World->GetTickRandomNumber(10)) - 5.0; Dist.z = static_cast<double>(Random.RandInt(-5, 5));
if ((Dist.SqrLength() > 2) && (rem >= 3)) if ((Dist.SqrLength() > 2) && (rem >= 3))
{ {
@ -1005,7 +1007,7 @@ cPawn * cMonster::GetTarget ()
cMonster * cMonster::NewMonsterFromType(eMonsterType a_MobType) cMonster * cMonster::NewMonsterFromType(eMonsterType a_MobType)
{ {
cFastRandom Random; auto & Random = GetRandomProvider();
cMonster * toReturn = nullptr; cMonster * toReturn = nullptr;
// Create the mob entity // Create the mob entity
@ -1013,23 +1015,23 @@ cMonster * cMonster::NewMonsterFromType(eMonsterType a_MobType)
{ {
case mtMagmaCube: 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; break;
} }
case mtSlime: 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; break;
} }
case mtSkeleton: case mtSkeleton:
{ {
// TODO: Actual detection of spawning in Nether // TODO: Actual detection of spawning in Nether
toReturn = new cSkeleton((Random.NextInt(1) == 0) ? false : true); toReturn = new cSkeleton(false);
break; break;
} }
case mtVillager: case mtVillager:
{ {
int VillagerType = Random.NextInt(6); int VillagerType = Random.RandInt(6);
if (VillagerType == 6) if (VillagerType == 6)
{ {
// Give farmers a better chance of spawning // Give farmers a better chance of spawning
@ -1042,10 +1044,10 @@ cMonster * cMonster::NewMonsterFromType(eMonsterType a_MobType)
case mtHorse: case mtHorse:
{ {
// Horses take a type (species), a colour, and a style (dots, stripes, etc.) // Horses take a type (species), a colour, and a style (dots, stripes, etc.)
int HorseType = Random.NextInt(8); int HorseType = Random.RandInt(7);
int HorseColor = Random.NextInt(7); int HorseColor = Random.RandInt(6);
int HorseStyle = Random.NextInt(5); int HorseStyle = Random.RandInt(4);
int HorseTameTimes = Random.NextInt(6) + 1; int HorseTameTimes = Random.RandInt(1, 6);
if ((HorseType == 5) || (HorseType == 6) || (HorseType == 7)) 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) void cMonster::AddRandomDropItem(cItems & a_Drops, unsigned int a_Min, unsigned int a_Max, short a_Item, short a_ItemHealth)
{ {
MTRand r1; auto Count = GetRandomProvider().RandInt<char>(static_cast<char>(a_Min), static_cast<char>(a_Max));
int Count = static_cast<int>(static_cast<unsigned int>(r1.randInt()) % (a_Max + 1 - a_Min) + a_Min);
if (Count > 0) 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) void cMonster::AddRandomUncommonDropItem(cItems & a_Drops, float a_Chance, short a_Item, short a_ItemHealth)
{ {
MTRand r1; if (GetRandomProvider().RandBool(a_Chance / 100.0))
int Count = r1.randInt() % 1000;
if (Count < (a_Chance * 10))
{ {
a_Drops.push_back(cItem(a_Item, 1, a_ItemHealth)); 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) void cMonster::AddRandomRareDropItem(cItems & a_Drops, cItems & a_Items, unsigned int a_LootingLevel)
{ {
MTRand r1; auto & r1 = GetRandomProvider();
unsigned int Count = static_cast<unsigned int>(static_cast<unsigned long>(r1.randInt()) % 200); if (r1.RandBool((5 + a_LootingLevel) / 200.0))
if (Count < (5 + a_LootingLevel))
{ {
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)); 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) void cMonster::AddRandomArmorDropItem(cItems & a_Drops, unsigned int a_LootingLevel)
{ {
MTRand r1; auto & r1 = GetRandomProvider();
if (r1.randInt() % 200 < ((m_DropChanceHelmet * 200) + (a_LootingLevel * 2)))
double LootingBonus = a_LootingLevel / 100.0;
if (r1.RandBool(m_DropChanceHelmet + LootingBonus))
{ {
if (!GetEquippedHelmet().IsEmpty()) 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()) 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()) 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()) 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) void cMonster::AddRandomWeaponDropItem(cItems & a_Drops, unsigned int a_LootingLevel)
{ {
MTRand r1; if (GetRandomProvider().RandBool(m_DropChanceWeapon + (a_LootingLevel / 100.0)))
if (r1.randInt() % 200 < ((m_DropChanceWeapon * 200) + (a_LootingLevel * 2)))
{ {
if (!GetEquippedWeapon().IsEmpty()) 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); Callback.Baby->InheritFromParents(this, m_LovePartner);
} }
cFastRandom Random; m_World->SpawnExperienceOrb(Pos.x, Pos.y, Pos.z, GetRandomProvider().RandInt(1, 6));
m_World->SpawnExperienceOrb(Pos.x, Pos.y, Pos.z, 1 + Random.NextInt(6));
m_LovePartner->ResetLoveMode(); m_LovePartner->ResetLoveMode();
ResetLoveMode(); ResetLoveMode();

View File

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

View File

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

View File

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

View File

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

View File

@ -70,8 +70,8 @@ void cSlime::KilledBy(TakeDamageInfo & a_TDI)
if (m_Size != 1) if (m_Size != 1)
{ {
cFastRandom Random; auto & Random = GetRandomProvider();
int SpawnAmount = 2 + Random.NextInt(3); int SpawnAmount = Random.RandInt(2, 4);
for (int i = 0; i < SpawnAmount; ++i) for (int i = 0; i < SpawnAmount; ++i)
{ {
@ -80,7 +80,7 @@ void cSlime::KilledBy(TakeDamageInfo & a_TDI)
cSlime * NewSlime = new cSlime(m_Size / 2); cSlime * NewSlime = new cSlime(m_Size / 2);
NewSlime->SetPosition(GetPosX() + AddX, GetPosY() + 0.5, GetPosZ() + AddZ); 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); 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 ((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); 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. // 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; return;
} }
@ -150,7 +150,7 @@ void cVillager::HandleFarmerPrepareFarmCrops()
m_VillagerAction = true; m_VillagerAction = true;
m_CropsPos = Vector3i(static_cast<int>(GetPosX()) + X - 5, static_cast<int>(GetPosY()) + Y - 3, static_cast<int>(GetPosZ()) + Z - 5); 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; return;
} // for Y loop. } // for Y loop.
} // Repeat the procces 5 times. } // 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); LootingLevel = a_Killer->GetEquippedWeapon().m_Enchantments.GetLevel(cEnchantments::enchLooting);
} }
MTRand r1; auto & r1 = GetRandomProvider();
int DropTypeCount = (r1.randInt() % 3) + 1; int DropTypeCount = r1.RandInt(1, 3);
for (int i = 0; i < DropTypeCount; i++) for (int i = 0; i < DropTypeCount; i++)
{ {
int DropType = r1.randInt() % 7; int DropType = r1.RandInt(6);
switch (DropType) switch (DropType)
{ {
case 0: AddRandomDropItem(a_Drops, 0, 2 + LootingLevel, E_ITEM_GLASS_BOTTLE); break; 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, // 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 // 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 // This helps spread a wolf pack's targets over several mobs
else if (m_World->GetTickRandomNumber(9)> 4) else if (GetRandomProvider().RandBool())
{ {
return; return;
} }
@ -179,7 +179,7 @@ void cWolf::OnRightClicked(cPlayer & a_Player)
a_Player.GetInventory().RemoveOneEquippedItem(); a_Player.GetInventory().RemoveOneEquippedItem();
} }
if (m_World->GetTickRandomNumber(7) == 0) if (GetRandomProvider().RandBool(0.125))
{ {
// Taming succeeded // Taming succeeded
SetMaxHealth(20); SetMaxHealth(20);

View File

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

View File

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

View File

@ -17,6 +17,7 @@
#include "WebAdmin.h" #include "WebAdmin.h"
#include "Protocol/ProtocolRecognizer.h" #include "Protocol/ProtocolRecognizer.h"
#include "CommandOutput.h" #include "CommandOutput.h"
#include "FastRandom.h"
#include "IniFile.h" #include "IniFile.h"
#include "Vector3.h" #include "Vector3.h"
@ -218,9 +219,9 @@ bool cServer::InitServer(cSettingsRepositoryInterface & a_Settings, bool a_Shoul
m_ShouldAuthenticate = a_ShouldAuth; m_ShouldAuthenticate = a_ShouldAuth;
if (m_ShouldAuthenticate) if (m_ShouldAuthenticate)
{ {
MTRand mtrand1; auto & rand = GetRandomProvider();
unsigned int r1 = (mtrand1.randInt() % 1147483647) + 1000000000; unsigned int r1 = rand.RandInt<unsigned int>(1000000000U, 0x7fffffffU);
unsigned int r2 = (mtrand1.randInt() % 1147483647) + 1000000000; unsigned int r2 = rand.RandInt<unsigned int>(1000000000U, 0x7fffffffU);
std::ostringstream sid; std::ostringstream sid;
sid << std::hex << r1; sid << std::hex << r1;
sid << std::hex << r2; 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) 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 // Make the chance to spread 100x smaller
return; 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, // No need to check the coords for equality with the parent block,
// it cannot catch fire anyway (because it's not an air 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; continue;
} }
@ -381,7 +381,7 @@ void cFireSimulator::RemoveFuelNeighbors(cChunk * a_Chunk, int a_RelX, int a_Rel
return; 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)) if (ShouldReplaceFuel && !cRoot::Get()->GetPluginManager()->CallHookBlockSpread(m_World, AbsX, Y, AbsZ, ssFireSpread))
{ {
Neighbour->SetBlock(X, Y, Z, E_BLOCK_FIRE, 0); Neighbour->SetBlock(X, Y, Z, E_BLOCK_FIRE, 0);

View File

@ -1003,8 +1003,7 @@ void cSlotAreaAnvil::OnTakeResult(cPlayer & a_Player)
NIBBLETYPE BlockMeta; NIBBLETYPE BlockMeta;
a_Player.GetWorld()->GetBlockTypeMeta(PosX, PosY, PosZ, Block, BlockMeta); a_Player.GetWorld()->GetBlockTypeMeta(PosX, PosY, PosZ, Block, BlockMeta);
cFastRandom Random; if (!a_Player.IsGameModeCreative() && (Block == E_BLOCK_ANVIL) && GetRandomProvider().RandBool(0.12))
if (!a_Player.IsGameModeCreative() && (Block == E_BLOCK_ANVIL) && (Random.NextFloat(1.0F) < 0.12F))
{ {
NIBBLETYPE Orientation = BlockMeta & 0x3; NIBBLETYPE Orientation = BlockMeta & 0x3;
NIBBLETYPE AnvilDamage = BlockMeta >> 2; NIBBLETYPE AnvilDamage = BlockMeta >> 2;
@ -1578,8 +1577,8 @@ void cSlotAreaEnchanting::UpdateResult(cPlayer & a_Player)
{ {
int Bookshelves = std::min(GetBookshelvesCount(*a_Player.GetWorld()), 15); int Bookshelves = std::min(GetBookshelvesCount(*a_Player.GetWorld()), 15);
cFastRandom Random; auto & Random = GetRandomProvider();
int Base = (Random.GenerateRandomInteger(1, 8) + static_cast<int>(floor(static_cast<float>(Bookshelves / 2)) + Random.GenerateRandomInteger(0, Bookshelves))); int Base = (Random.RandInt(1, 8) + (Bookshelves / 2) + Random.RandInt(0, Bookshelves));
int TopSlot = std::max(Base / 3, 1); int TopSlot = std::max(Base / 3, 1);
int MiddleSlot = (Base * 2) / 3 + 1; int MiddleSlot = (Base * 2) / 3 + 1;
int BottomSlot = std::max(Base, Bookshelves * 2); 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) int cWorld::GetDefaultWeatherInterval(eWeather a_Weather)
{ {
auto & Random = GetRandomProvider();
switch (a_Weather) switch (a_Weather)
{ {
case eWeather_Sunny: case eWeather_Sunny:
{ {
auto dif = m_MaxSunnyTicks - m_MinSunnyTicks + 1; return Random.RandInt(m_MinSunnyTicks, m_MaxSunnyTicks);
return m_MinSunnyTicks + (m_TickRand.randInt() % dif);
} }
case eWeather_Rain: case eWeather_Rain:
{ {
auto dif = m_MaxRainTicks - m_MinRainTicks + 1; return Random.RandInt(m_MinRainTicks, m_MaxRainTicks);
return m_MinRainTicks + (m_TickRand.randInt() % dif);
} }
case eWeather_ThunderStorm: case eWeather_ThunderStorm:
{ {
auto dif = m_MaxThunderStormTicks - m_MinThunderStormTicks + 1; return Random.RandInt(m_MinThunderStormTicks, m_MaxThunderStormTicks);
return m_MinThunderStormTicks + (m_TickRand.randInt() % dif);
} }
} }
@ -812,7 +810,7 @@ eWeather cWorld::ChooseNewWeather()
case eWeather_Rain: case eWeather_Rain:
{ {
// 1 / 8 chance of turning into a thunderstorm // 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) if (m_Weather == eWeather_ThunderStorm)
{ {
// 0.5% chance per tick of thunderbolt // 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. 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) bool cWorld::GrowRipePlant(int a_BlockX, int a_BlockY, int a_BlockZ, bool a_IsByBonemeal)
{ {
cFastRandom random; auto & random = GetRandomProvider();
BLOCKTYPE BlockType; BLOCKTYPE BlockType;
NIBBLETYPE BlockMeta; NIBBLETYPE BlockMeta;
GetBlockTypeMeta(a_BlockX, a_BlockY, a_BlockZ, BlockType, 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 else
{ {
BlockMeta += random.NextInt(4) + 2; BlockMeta += random.RandInt(2, 5);
BlockMeta = std::min(BlockMeta, static_cast<NIBBLETYPE>(7)); BlockMeta = std::min(BlockMeta, static_cast<NIBBLETYPE>(7));
} }
FastSetBlock(a_BlockX, a_BlockY, a_BlockZ, BlockType, BlockMeta); 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 else
{ {
BlockMeta += random.NextInt(4) + 2; BlockMeta += random.RandInt(2, 5);
BlockMeta = std::min(BlockMeta, static_cast<NIBBLETYPE>(7)); BlockMeta = std::min(BlockMeta, static_cast<NIBBLETYPE>(7));
} }
FastSetBlock(a_BlockX, a_BlockY, a_BlockZ, BlockType, BlockMeta); 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 else
{ {
BlockMeta += random.NextInt(4) + 2; BlockMeta += random.RandInt(2, 5);
BlockMeta = std::min(BlockMeta, static_cast<NIBBLETYPE>(7)); BlockMeta = std::min(BlockMeta, static_cast<NIBBLETYPE>(7));
} }
FastSetBlock(a_BlockX, a_BlockY, a_BlockZ, BlockType, BlockMeta); 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 else
{ {
BlockMeta += random.NextInt(4) + 2; BlockMeta += random.RandInt(2, 5);
BlockMeta = std::min(BlockMeta, static_cast<NIBBLETYPE>(7)); BlockMeta = std::min(BlockMeta, static_cast<NIBBLETYPE>(7));
} }
FastSetBlock(a_BlockX, a_BlockY, a_BlockZ, BlockType, BlockMeta); 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 else
{ {
BlockMeta += random.NextInt(4) + 2; BlockMeta += random.RandInt(2, 5);
BlockMeta = std::min(BlockMeta, static_cast<NIBBLETYPE>(7)); BlockMeta = std::min(BlockMeta, static_cast<NIBBLETYPE>(7));
} }
FastSetBlock(a_BlockX, a_BlockY, a_BlockZ, BlockType, BlockMeta); 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; ++GrowState;
} }
else if (random.NextInt(99) < 45) else if (random.RandBool(0.45))
{ {
++GrowState; ++GrowState;
} }
FastSetBlock(a_BlockX, a_BlockY, a_BlockZ, BlockType, static_cast<NIBBLETYPE>(GrowState << 3 | TypeMeta)); 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); 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; return false;
} }
MTRand r1; auto & r1 = GetRandomProvider();
for (int i = 0; i < 60; i++) 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 OfsX = (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 OfsY = 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 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); BLOCKTYPE Ground = GetBlock(a_BlockX + OfsX, a_BlockY + OfsY, a_BlockZ + OfsZ);
if (Ground != E_BLOCK_GRASS) 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; BLOCKTYPE SpawnType;
NIBBLETYPE SpawnMeta = 0; NIBBLETYPE SpawnMeta = 0;
switch (r1.randInt(10)) switch (r1.RandInt(10))
{ {
case 0: SpawnType = E_BLOCK_YELLOW_FLOWER; break; case 0: SpawnType = E_BLOCK_YELLOW_FLOWER; break;
case 1: SpawnType = E_BLOCK_RED_ROSE; 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) 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);
return m_ChunkMap->GrowMelonPumpkin(a_BlockX, a_BlockY, a_BlockZ, a_BlockType, Rand);
} }
@ -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) 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 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) 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; continue;
} }
float SpeedX = 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 * GetTickRandomNumber(50)); float SpeedY = static_cast<float>(a_FlyAwaySpeed * Random.RandInt(50));
float SpeedZ = static_cast<float>(a_FlyAwaySpeed * (GetTickRandomNumber(10) - 5)); float SpeedZ = static_cast<float>(a_FlyAwaySpeed * Random.RandInt(-5, 5));
cPickup * Pickup = new cPickup( cPickup * Pickup = new cPickup(
a_BlockX, a_BlockY, a_BlockZ, 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; TNT = nullptr;
return cEntity::INVALID_ID; return cEntity::INVALID_ID;
} }
auto & Random = GetRandomProvider();
TNT->SetSpeed( TNT->SetSpeed(
a_InitialVelocityCoeff * (GetTickRandomNumber(2) - 1), /** -1, 0, 1 */ a_InitialVelocityCoeff * Random.RandInt(-1, 1),
a_InitialVelocityCoeff * 2, a_InitialVelocityCoeff * 2,
a_InitialVelocityCoeff * (GetTickRandomNumber(2) - 1) a_InitialVelocityCoeff * Random.RandInt(-1, 1)
); );
return TNT->GetUniqueID(); 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) void cWorld::TabCompleteUserName(const AString & a_Text, AStringVector & a_Results)
{ {
typedef std::pair<AString::size_type, AString> pair_t; typedef std::pair<AString::size_type, AString> pair_t;

View File

@ -28,7 +28,6 @@
#include "MapManager.h" #include "MapManager.h"
#include "Blocks/WorldInterface.h" #include "Blocks/WorldInterface.h"
#include "Blocks/BroadcastInterface.h" #include "Blocks/BroadcastInterface.h"
#include "FastRandom.h"
#include "EffectID.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. */ 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 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! */ /** Returns a random number in range [0 .. a_Range]. */
int GetTickRandomNumber(int a_Range) { return static_cast<int>(m_TickRand.randInt(a_Range)); } int GetTickRandomNumber(int a_Range);
/** Appends all usernames starting with a_Text (case-insensitive) into Results */ /** Appends all usernames starting with a_Text (case-insensitive) into Results */
void TabCompleteUserName(const AString & a_Text, AStringVector & a_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 */ /** The dimension of the world, used by the client to provide correct lighting scheme */
eDimension m_Dimension; 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; bool m_IsSpawnExplicitlySet;
double m_SpawnX; double m_SpawnX;
double m_SpawnY; double m_SpawnY;

View File

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