summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/Blocks/BlockBigFlower.h12
-rw-r--r--src/Blocks/BlockCrops.h44
-rw-r--r--src/Blocks/BlockGlowstone.h10
-rw-r--r--src/Blocks/BlockGravel.h14
-rw-r--r--src/Blocks/BlockHandler.cpp13
-rw-r--r--src/Blocks/BlockHandler.h9
-rw-r--r--src/Blocks/BlockLeaves.h59
-rw-r--r--src/Blocks/BlockMelon.h3
-rw-r--r--src/Blocks/BlockNetherWart.h10
-rw-r--r--src/Blocks/BlockOre.h22
-rw-r--r--src/Blocks/BlockSeaLantern.h10
-rw-r--r--src/Blocks/BlockTallGrass.h11
-rw-r--r--src/Item.h4
13 files changed, 149 insertions, 72 deletions
diff --git a/src/Blocks/BlockBigFlower.h b/src/Blocks/BlockBigFlower.h
index 848cdce16..5df20dedf 100644
--- a/src/Blocks/BlockBigFlower.h
+++ b/src/Blocks/BlockBigFlower.h
@@ -65,15 +65,23 @@ private:
auto flowerType = a_BlockMeta & 0x07;
if (flowerType == E_META_BIG_FLOWER_DOUBLE_TALL_GRASS)
{
- if (GetRandomProvider().RandBool(1.0 / 24.0))
+
+ // Drop seeds, depending on bernoulli trial result:
+ if (GetRandomProvider().RandBool(0.875))
{
- return cItem(E_ITEM_SEEDS);
+ // 87.5% chance of dropping nothing:
+ return {};
}
+
+ // 12.5% chance of dropping some seeds.
+ const auto DropNum = FortuneDiscreteRandom(1, 1, 2 * ToolFortuneLevel(a_Tool));
+ return cItem(E_ITEM_SEEDS, DropNum);
}
else if (flowerType != E_META_BIG_FLOWER_LARGE_FERN)
{
return cItem(m_BlockType, 1, static_cast<short>(flowerType));
}
+
return {};
}
diff --git a/src/Blocks/BlockCrops.h b/src/Blocks/BlockCrops.h
index 1d612e685..827b26881 100644
--- a/src/Blocks/BlockCrops.h
+++ b/src/Blocks/BlockCrops.h
@@ -21,6 +21,17 @@ public:
private:
+ /** Calculate the number of seeds to drop when the crop is broken. */
+ static char CalculateSeedCount(char a_Min, char a_BaseRolls, char a_FortuneLevel)
+ {
+ std::binomial_distribution Binomial(a_BaseRolls + a_FortuneLevel, 0.57);
+ return a_Min + Binomial(GetRandomProvider().Engine());
+ }
+
+
+
+
+
virtual cItems ConvertToPickups(NIBBLETYPE a_BlockMeta, const cEntity * a_Digger, const cItem * a_Tool) const override
{
auto & rand = GetRandomProvider();
@@ -30,11 +41,12 @@ private:
{
switch (m_BlockType)
{
- case E_BLOCK_BEETROOTS: return cItem(E_ITEM_BEETROOT_SEEDS, 1, 0); break;
- case E_BLOCK_CROPS: return cItem(E_ITEM_SEEDS, 1, 0); break;
- case E_BLOCK_CARROTS: return cItem(E_ITEM_CARROT, 1, 0); break;
- case E_BLOCK_POTATOES: return cItem(E_ITEM_POTATO, 1, 0); break;
+ case E_BLOCK_BEETROOTS: return cItem(E_ITEM_BEETROOT_SEEDS);
+ case E_BLOCK_CROPS: return cItem(E_ITEM_SEEDS);
+ case E_BLOCK_CARROTS: return cItem(E_ITEM_CARROT);
+ case E_BLOCK_POTATOES: return cItem(E_ITEM_POTATO);
}
+
ASSERT(!"Unhandled block type");
return {};
}
@@ -45,30 +57,32 @@ private:
{
case E_BLOCK_BEETROOTS:
{
- char SeedCount = 1 + ((rand.RandInt<char>(2) + rand.RandInt<char>(2)) / 2); // [1 .. 3] with high preference of 2
- res.Add(E_ITEM_BEETROOT_SEEDS, SeedCount, 0);
- char BeetrootCount = 1 + ((rand.RandInt<char>(2) + rand.RandInt<char>(2)) / 2); // [1 .. 3] with high preference of 2
- res.Add(E_ITEM_BEETROOT, BeetrootCount, 0);
+ const auto SeedCount = CalculateSeedCount(0, 3, ToolFortuneLevel(a_Tool));
+ res.Add(E_ITEM_BEETROOT_SEEDS, SeedCount);
+ res.Add(E_ITEM_BEETROOT);
break;
}
case E_BLOCK_CROPS:
{
- res.Add(E_ITEM_WHEAT, 1, 0);
- res.Add(E_ITEM_SEEDS, 1 + ((rand.RandInt<char>(2) + rand.RandInt<char>(2)) / 2), 0); // [1 .. 3] with high preference of 2
+ res.Add(E_ITEM_WHEAT);
+ const auto SeedCount = CalculateSeedCount(1, 3, ToolFortuneLevel(a_Tool));
+ res.Add(E_ITEM_SEEDS, SeedCount);
break;
}
case E_BLOCK_CARROTS:
{
- res.Add(E_ITEM_CARROT, 1 + ((rand.RandInt<char>(2) + rand.RandInt<char>(2)) / 2), 0); // [1 .. 3] with high preference of 2
+ const auto CarrotCount = CalculateSeedCount(1, 4, ToolFortuneLevel(a_Tool));
+ res.Add(E_ITEM_CARROT, CarrotCount);
break;
}
case E_BLOCK_POTATOES:
{
- res.Add(E_ITEM_POTATO, 1 + ((rand.RandInt<char>(2) + rand.RandInt<char>(2)) / 2), 0); // [1 .. 3] with high preference of 2
- if (rand.RandBool(0.05))
+ const auto PotatoCount = CalculateSeedCount(2, 3, ToolFortuneLevel(a_Tool));
+ res.Add(E_ITEM_POTATO, PotatoCount);
+ if (rand.RandBool(0.02))
{
- // With a 5% chance, drop a poisonous potato as well
- res.emplace_back(E_ITEM_POISONOUS_POTATO, 1, 0);
+ // With a 2% chance, drop a poisonous potato as well
+ res.Add(E_ITEM_POISONOUS_POTATO);
}
break;
}
diff --git a/src/Blocks/BlockGlowstone.h b/src/Blocks/BlockGlowstone.h
index 1e187084d..395233b44 100644
--- a/src/Blocks/BlockGlowstone.h
+++ b/src/Blocks/BlockGlowstone.h
@@ -21,16 +21,12 @@ private:
// Drop self only when using silk-touch:
if (ToolHasSilkTouch(a_Tool))
{
- return cItem(E_BLOCK_GLOWSTONE, 1, 0);
+ return cItem(E_BLOCK_GLOWSTONE);
}
// Number of dust to drop, capped at the max amount of 4.
- const auto Drops = std::min(
- static_cast<char>(4),
- GetRandomProvider().RandInt<char>(2, 4 + ToolFortuneLevel(a_Tool))
- );
-
- return cItem(E_ITEM_GLOWSTONE_DUST, Drops);
+ const auto DropNum = FortuneDiscreteRandom(2, 4, ToolFortuneLevel(a_Tool), 4);
+ return cItem(E_ITEM_GLOWSTONE_DUST, DropNum);
}
diff --git a/src/Blocks/BlockGravel.h b/src/Blocks/BlockGravel.h
index 972dea4e4..2e145f5e5 100644
--- a/src/Blocks/BlockGravel.h
+++ b/src/Blocks/BlockGravel.h
@@ -18,15 +18,19 @@ private:
virtual cItems ConvertToPickups(NIBBLETYPE a_BlockMeta, const cEntity * a_Digger, const cItem * a_Tool) const override
{
- // TODO: Handle the Fortune and Silk touch enchantments here
- if (GetRandomProvider().RandBool(0.10))
+ if (ToolHasSilkTouch(a_Tool))
{
- return cItem(E_ITEM_FLINT, 1, 0);
+ return cItem(E_BLOCK_GRAVEL);
}
- else
+
+ // Denominator of probability from wiki, don't let it go below 1.
+ const auto Denominator = std::max(10 - 3 * ToolFortuneLevel(a_Tool), 1);
+ if (GetRandomProvider().RandBool(1.0 / Denominator))
{
- return cItem(E_BLOCK_GRAVEL, 1, 0);
+ return cItem(E_ITEM_FLINT);
}
+
+ return cItem(E_BLOCK_GRAVEL);
}
diff --git a/src/Blocks/BlockHandler.cpp b/src/Blocks/BlockHandler.cpp
index 0066a06a5..a10c5e48d 100644
--- a/src/Blocks/BlockHandler.cpp
+++ b/src/Blocks/BlockHandler.cpp
@@ -658,6 +658,19 @@ unsigned char cBlockHandler::ToolFortuneLevel(const cItem * a_Tool)
+char cBlockHandler::FortuneDiscreteRandom(char a_MinDrop, char a_DefaultMax, unsigned char a_BonusMax, char a_DropCap)
+{
+ // First sample the discrete random distribution.
+ char DropNum = GetRandomProvider().RandInt<char>(a_MinDrop, a_DefaultMax + a_BonusMax);
+
+ // Then clamp to within range (clamp instead of min incase of overflow):
+ return std::clamp<char>(DropNum, a_MinDrop, a_DropCap);
+}
+
+
+
+
+
const cBlockHandler & cBlockHandler::For(BLOCKTYPE a_BlockType)
{
// Switch on the block type, as an enumeration
diff --git a/src/Blocks/BlockHandler.h b/src/Blocks/BlockHandler.h
index 55dbeb5ec..cc701ca8e 100644
--- a/src/Blocks/BlockHandler.h
+++ b/src/Blocks/BlockHandler.h
@@ -227,6 +227,15 @@ public:
Can be used in ConvertToPickups() implementations. */
static unsigned char ToolFortuneLevel(const cItem * a_Tool);
+ /** Returns a random number of drops taking into account fortune.
+ Only applies to drops following clamped discrete random distribution.
+ a_DefaultMax is the maximum items from one block without fortune.
+ a_BonusMax is the amount to increase the max of randInt by, usually the fortune level (but not always)
+ a_DropCap is the maximum items from one block with fortune,
+ if unspecified set to 25 to prevent lag or crash with high level tools.
+ Similar to uniform_bonus_count at https://minecraft.gamepedia.com/Loot_table#Functions */
+ static char FortuneDiscreteRandom(char a_MinDrop, char a_DefaultMax, unsigned char a_BonusMax, char a_DropCap = 25);
+
// Gets the blockhandler for the given block type.
static const cBlockHandler & For(BLOCKTYPE a_BlockType);
diff --git a/src/Blocks/BlockLeaves.h b/src/Blocks/BlockLeaves.h
index c08f1b6bb..1f726f779 100644
--- a/src/Blocks/BlockLeaves.h
+++ b/src/Blocks/BlockLeaves.h
@@ -25,6 +25,23 @@ public:
private:
+ static double FortuneDropProbability(unsigned char a_DefaultDenominator, unsigned char a_FirstDenominatorReduction, unsigned char a_FortuneLevel)
+ {
+ // Fortune 3 behaves like fortune 4 for some reason
+ if (a_FortuneLevel == 3)
+ {
+ a_FortuneLevel++;
+ }
+
+ // Denominator, capped at minimum of 10.
+ const auto Denominator = std::max<unsigned char>(10, a_DefaultDenominator - a_FortuneLevel * a_FirstDenominatorReduction);
+ return 1.0 / Denominator;
+ }
+
+
+
+
+
/** Returns true if the area contains a continous path from the specified block to a log block entirely made out of leaves blocks. */
static bool HasNearLog(cBlockArea & a_Area, const Vector3i a_BlockPos)
{
@@ -98,44 +115,56 @@ private:
// If breaking with shears, drop self:
if ((a_Tool != nullptr) && (a_Tool->m_ItemType == E_ITEM_SHEARS))
{
- return cItem(m_BlockType, a_BlockMeta & 0x03);
+ return cItem(m_BlockType, 1, a_BlockMeta & 0x03);
}
// There is a chance to drop a sapling that varies depending on the type of leaf broken.
// Note: It is possible (though very rare) for a single leaves block to drop both a sapling and an apple
- // TODO: Take into account fortune for sapling drops.
- double chance = 0.0;
- auto & rand = GetRandomProvider();
- cItems res;
+ double DropProbability;
+ const auto FortuneLevel = ToolFortuneLevel(a_Tool);
+ auto & Random = GetRandomProvider();
+ cItems Res;
+
if ((m_BlockType == E_BLOCK_LEAVES) && ((a_BlockMeta & 0x03) == E_META_LEAVES_JUNGLE))
{
- // Jungle leaves have a 2.5% chance of dropping a sapling.
- chance = 0.025;
+ // Jungle leaves have a 2.5% default chance of dropping a sapling.
+ DropProbability = FortuneDropProbability(40, 4, FortuneLevel);
}
else
{
- // Other leaves have a 5% chance of dropping a sapling.
- chance = 0.05;
+ // Other leaves have a 5% default chance of dropping a sapling.
+ DropProbability = FortuneDropProbability(20, 4, FortuneLevel);
}
- if (rand.RandBool(chance))
+
+ if (Random.RandBool(DropProbability))
{
- res.Add(
+ Res.Add(
E_BLOCK_SAPLING,
1,
(m_BlockType == E_BLOCK_LEAVES) ? (a_BlockMeta & 0x03) : static_cast<short>(4 + (a_BlockMeta & 0x01))
);
}
- // 0.5 % chance of dropping an apple, if the leaves' type is Apple Leaves
+ // 0.5 % chance of dropping an apple, increased by fortune, if the leaves' type is Apple Leaves
if ((m_BlockType == E_BLOCK_LEAVES) && ((a_BlockMeta & 0x03) == E_META_LEAVES_APPLE))
{
- if (rand.RandBool(0.005))
+ DropProbability = FortuneDropProbability(200, 20, FortuneLevel);
+ if (Random.RandBool(DropProbability))
{
- res.Add(E_ITEM_RED_APPLE, 1, 0);
+ Res.Add(E_ITEM_RED_APPLE);
}
}
- return res;
+
+ // 2% chance of dropping sticks (yuck) in 1.14
+ DropProbability = FortuneDropProbability(50, 5, FortuneLevel);
+ if (Random.RandBool(DropProbability))
+ {
+ // 1 or 2 sticks are dropped on success:
+ Res.Add(E_ITEM_STICK, Random.RandInt<char>(1, 2));
+ }
+
+ return Res;
}
diff --git a/src/Blocks/BlockMelon.h b/src/Blocks/BlockMelon.h
index 09841e642..da021830c 100644
--- a/src/Blocks/BlockMelon.h
+++ b/src/Blocks/BlockMelon.h
@@ -20,7 +20,8 @@ private:
virtual cItems ConvertToPickups(NIBBLETYPE a_BlockMeta, const cEntity * a_Digger, const cItem * a_Tool) const override
{
- return cItem(E_ITEM_MELON_SLICE, GetRandomProvider().RandInt<char>(3, 7), 0);
+ const auto DropNum = FortuneDiscreteRandom(3, 7, ToolFortuneLevel(a_Tool), 9);
+ return cItem(E_ITEM_MELON_SLICE, DropNum);
}
diff --git a/src/Blocks/BlockNetherWart.h b/src/Blocks/BlockNetherWart.h
index 590a55772..dde6b17d4 100644
--- a/src/Blocks/BlockNetherWart.h
+++ b/src/Blocks/BlockNetherWart.h
@@ -24,13 +24,11 @@ private:
if (a_BlockMeta == 0x03)
{
// Fully grown, drop the entire produce:
- auto & rand = GetRandomProvider();
- return cItem(E_ITEM_NETHER_WART, 1 + (rand.RandInt<char>(2) + rand.RandInt<char>(2)) / 2, 0);
- }
- else
- {
- return cItem(E_ITEM_NETHER_WART);
+ const auto DropNum = FortuneDiscreteRandom(2, 4, ToolFortuneLevel(a_Tool));
+ return cItem(E_ITEM_NETHER_WART, DropNum);
}
+
+ return cItem(E_ITEM_NETHER_WART);
}
diff --git a/src/Blocks/BlockOre.h b/src/Blocks/BlockOre.h
index 98db5198d..95bffc75e 100644
--- a/src/Blocks/BlockOre.h
+++ b/src/Blocks/BlockOre.h
@@ -31,19 +31,23 @@ private:
}
}
- auto & Random = GetRandomProvider();
const auto FortuneLevel = ToolFortuneLevel(a_Tool);
- const auto Drops = std::max(static_cast<char>(1), FloorC<char>(Random.RandReal(FortuneLevel + 2.0)));
+ if ((m_BlockType == E_BLOCK_REDSTONE_ORE) || (m_BlockType == E_BLOCK_REDSTONE_ORE_GLOWING))
+ { // Redstone follows the discrete random distribution, unlike other ores
+ const auto DropNum = FortuneDiscreteRandom(4, 5, FortuneLevel);
+ return cItem(E_ITEM_REDSTONE_DUST, DropNum);
+ }
+
+ auto & Random = GetRandomProvider();
+ const auto DropMult = std::max(static_cast<char>(1), FloorC<char>(Random.RandReal(FortuneLevel + 2.0)));
switch (m_BlockType)
{
- case E_BLOCK_LAPIS_ORE: return cItem(E_ITEM_DYE, Drops * Random.RandInt<char>(4, 9), 4);
- case E_BLOCK_REDSTONE_ORE: // Handled by next case (glowing redstone)
- case E_BLOCK_REDSTONE_ORE_GLOWING: return cItem(E_ITEM_REDSTONE_DUST, Random.RandInt<char>(4, 5 + FortuneLevel));
- case E_BLOCK_DIAMOND_ORE: return cItem(E_ITEM_DIAMOND, Drops);
- case E_BLOCK_EMERALD_ORE: return cItem(E_ITEM_EMERALD, Drops);
- case E_BLOCK_COAL_ORE: return cItem(E_ITEM_COAL, Drops);
- case E_BLOCK_NETHER_QUARTZ_ORE: return cItem(E_ITEM_NETHER_QUARTZ, Drops);
+ case E_BLOCK_LAPIS_ORE: return cItem(E_ITEM_DYE, DropMult * Random.RandInt<char>(4, 9), 4);
+ case E_BLOCK_DIAMOND_ORE: return cItem(E_ITEM_DIAMOND, DropMult);
+ case E_BLOCK_EMERALD_ORE: return cItem(E_ITEM_EMERALD, DropMult);
+ case E_BLOCK_COAL_ORE: return cItem(E_ITEM_COAL, DropMult);
+ case E_BLOCK_NETHER_QUARTZ_ORE: return cItem(E_ITEM_NETHER_QUARTZ, DropMult);
case E_BLOCK_CLAY: return cItem(E_ITEM_CLAY, 4);
default:
{
diff --git a/src/Blocks/BlockSeaLantern.h b/src/Blocks/BlockSeaLantern.h
index 9804642ca..d3adc95f4 100644
--- a/src/Blocks/BlockSeaLantern.h
+++ b/src/Blocks/BlockSeaLantern.h
@@ -23,15 +23,11 @@ private:
// Drop self only when using silk-touch:
if (ToolHasSilkTouch(a_Tool))
{
- return cItem(E_BLOCK_SEA_LANTERN, 1, 0);
+ return cItem(E_BLOCK_SEA_LANTERN);
}
// Number of crystals to drop, capped at the max amount of 5.
- const auto Drops = std::min(
- static_cast<char>(5),
- GetRandomProvider().RandInt<char>(2, 3 + ToolFortuneLevel(a_Tool))
- );
-
- return cItem(E_ITEM_PRISMARINE_CRYSTALS, Drops);
+ const auto DropNum = FortuneDiscreteRandom(2, 3, ToolFortuneLevel(a_Tool), 5);
+ return cItem(E_ITEM_PRISMARINE_CRYSTALS, DropNum);
}
} ;
diff --git a/src/Blocks/BlockTallGrass.h b/src/Blocks/BlockTallGrass.h
index f0da32cf5..d43d3ad04 100644
--- a/src/Blocks/BlockTallGrass.h
+++ b/src/Blocks/BlockTallGrass.h
@@ -37,12 +37,15 @@ private:
return cItem(m_BlockType, 1, a_BlockMeta);
}
- // Drop seeds, sometimes:
- if (GetRandomProvider().RandBool(0.125))
+ // Drop seeds, depending on bernoulli trial result:
+ if (GetRandomProvider().RandBool(0.875)) // 87.5% chance of dropping nothing
{
- return cItem(E_ITEM_SEEDS);
+ return {};
}
- return {};
+
+ // 12.5% chance of dropping 0 or more seeds.
+ const auto DropNum = FortuneDiscreteRandom(1, 1, 2 * ToolFortuneLevel(a_Tool));
+ return cItem(E_ITEM_SEEDS, DropNum);
}
diff --git a/src/Item.h b/src/Item.h
index 2f7b1a238..d3f853170 100644
--- a/src/Item.h
+++ b/src/Item.h
@@ -230,6 +230,8 @@ public:
cItem * Get (int a_Idx);
void Set (int a_Idx, const cItem & a_Item);
void Add (const cItem & a_Item) {push_back(a_Item); }
+ void Add (short a_ItemType) { emplace_back(a_ItemType); }
+ void Add (short a_ItemType, char a_ItemCount) { emplace_back(a_ItemType, a_ItemCount); }
void Delete(int a_Idx);
void Clear (void) {clear(); }
size_t Size (void) const { return size(); }
@@ -239,7 +241,7 @@ public:
void Add (short a_ItemType, char a_ItemCount, short a_ItemDamage)
{
- push_back(cItem(a_ItemType, a_ItemCount, a_ItemDamage));
+ emplace_back(a_ItemType, a_ItemCount, a_ItemDamage);
}
/** Adds a copy of all items in a_ItemGrid. */