test: cover testLoot

This commit is contained in:
dualfroz committed 2026-09-11 22:15:43 +02:00
1 parent ea3d54436c
commit 80ef7386dc
1 file changed
+157
+157
View File
@@ -156,3 +156,160 @@ VF_TEST(fortuneMultipliesOreDropsAndNeverDropsBelowTheBaseYield) {
CHECK_EQ(worstPlain, 1); CHECK_EQ(worstPlain, 1);
CHECK_EQ(worstFortune, 1); CHECK_EQ(worstFortune, 1);
CHECK_EQ(maxOverSeeds(Block::DiamondOre, bare, ItemId::DiamondPickaxe, ItemId::DiamondGem, 400),
1);
CHECK_EQ(maxOverSeeds(Block::DiamondOre, fortune, ItemId::DiamondPickaxe, ItemId::DiamondGem,
400),
4);
// Silk Touch wins over Fortune: the ore block itself, never a multiplied gem.
LootContext both = enchanted(ItemId::DiamondPickaxe, EnchantKind::SilkTouch, 1, 99u);
addEnchant(both.toolEnchants, EnchantKind::Fortune, 3);
LootRoll roll = rollBlockDrops(Block::DiamondOre, both);
CHECK_EQ(roll.countOf(ItemId::DiamondOre), 1);
CHECK_EQ(roll.countOf(ItemId::DiamondGem), 0);
}
VF_TEST(grassYieldsDirtAndBedrockYieldsNothingAtAll) {
LootContext shovel = contextWith(ItemId::IronShovel, 5u);
LootRoll grass = rollBlockDrops(Block::Grass, shovel);
CHECK_EQ(grass.countOf(ItemId::Dirt), 1);
CHECK_EQ(grass.countOf(ItemId::Grass), 0);
LootContext pickaxe = contextWith(ItemId::DiamondPickaxe, 5u);
CHECK(rollBlockDrops(Block::Bedrock, pickaxe).empty());
CHECK(blockLootTable(Block::Bedrock).empty());
LootContext silk = enchanted(ItemId::DiamondPickaxe, EnchantKind::SilkTouch, 1, 5u);
CHECK(rollBlockDrops(Block::Bedrock, silk).empty());
}
VF_TEST(leavesOnlyDropTheirSubstituteCuttingWhenSilkTouchIsAbsent) {
LootContext silk = enchanted(ItemId::IronHoe, EnchantKind::SilkTouch, 1, 3u);
LootRoll harvested = rollBlockDrops(Block::Leaves, silk);
CHECK_EQ(harvested.countOf(ItemId::Leaves), 1);
CHECK_EQ(harvested.totalCount(), 1);
int cuttings = 0;
int cuttingsWithFortune = 0;
int leafBlocks = 0;
EnchantSet fortune;
addEnchant(fortune, EnchantKind::Fortune, 3);
for (int i = 0; i < 1500; ++i) {
LootContext plain = contextWith(ItemId::IronHoe, seedFor(i));
LootRoll roll = rollBlockDrops(Block::Leaves, plain);
cuttings += roll.countOf(ItemId::Wood);
leafBlocks += roll.countOf(ItemId::Leaves);
LootContext lucky = contextWith(ItemId::IronHoe, seedFor(i));
lucky.toolEnchants = fortune;
cuttingsWithFortune += rollBlockDrops(Block::Leaves, lucky).countOf(ItemId::Wood);
}
CHECK_EQ(leafBlocks, 0);
CHECK(cuttings > 0);
CHECK(cuttingsWithFortune > cuttings * 2);
}
VF_TEST(toolTierGatesTheOresTheWayTheHarvestLevelsSay) {
CHECK(!toolQualifiesFor(Block::Stone, ItemId::None));
CHECK(!toolQualifiesFor(Block::Stone, ItemId::IronAxe));
CHECK(toolQualifiesFor(Block::Stone, ItemId::WoodPickaxe));
CHECK(!toolQualifiesFor(Block::IronOre, ItemId::WoodPickaxe));
CHECK(!toolQualifiesFor(Block::IronOre, ItemId::GoldPickaxe));
CHECK(toolQualifiesFor(Block::IronOre, ItemId::StonePickaxe));
CHECK(!toolQualifiesFor(Block::DiamondOre, ItemId::StonePickaxe));
CHECK(!toolQualifiesFor(Block::DiamondOre, ItemId::GoldPickaxe));
CHECK(toolQualifiesFor(Block::DiamondOre, ItemId::IronPickaxe));
CHECK(toolQualifiesFor(Block::EmeraldOre, ItemId::DiamondPickaxe));
// Nothing outside the pickaxe class is gated at all.
CHECK(toolQualifiesFor(Block::Dirt, ItemId::None));
CHECK(toolQualifiesFor(Block::Leaves, ItemId::None));
CHECK(toolQualifiesFor(Block::Grass, ItemId::WoodShovel));
}
VF_TEST(aBlockBrokenWithTooWeakAToolProducesNoDropsAtAll) {
LootContext weak = contextWith(ItemId::StonePickaxe, 11u);
CHECK(rollBlockDrops(Block::DiamondOre, weak).empty());
LootContext bare = contextWith(ItemId::None, 11u);
CHECK(rollBlockDrops(Block::Stone, bare).empty());
// The gate runs before the table, so even Silk Touch cannot rescue it.
LootContext silkOnAStonePick = enchanted(ItemId::StonePickaxe, EnchantKind::SilkTouch, 1, 11u);
CHECK(rollBlockDrops(Block::DiamondOre, silkOnAStonePick).empty());
}
VF_TEST(zombiesDropFleshAlwaysAndIronOnlyWhenAPlayerLandedTheKill) {
int ingotsFromPlayers = 0;
int ingotsFromTheWorld = 0;
int fleshTotal = 0;
for (int i = 0; i < 3000; ++i) {
LootRoll byPlayer = rollMobDrops(LootTable::MobKind::Zombie, killContext(true, seedFor(i)));
LootRoll byWorld = rollMobDrops(LootTable::MobKind::Zombie, killContext(false, seedFor(i)));
ingotsFromPlayers += byPlayer.countOf(ItemId::IronIngot);
ingotsFromTheWorld += byWorld.countOf(ItemId::IronIngot);
fleshTotal += byPlayer.countOf(ItemId::RottenFlesh);
}
CHECK_EQ(ingotsFromTheWorld, 0);
CHECK(ingotsFromPlayers > 0);
CHECK(fleshTotal > 2000);
CHECK(fleshTotal < 4500);
}
VF_TEST(lootingRaisesBothTheDropCeilingAndTheRareDropRate) {
int plainCeiling = 0;
int lootedCeiling = 0;
int plainIngots = 0;
int lootedIngots = 0;
for (int i = 0; i < 3000; ++i) {
LootContext plain = killContext(true, seedFor(i));
LootContext looted = killContext(true, seedFor(i));
addEnchant(looted.toolEnchants, EnchantKind::Looting, 3);
LootRoll plainRoll = rollMobDrops(LootTable::MobKind::Zombie, plain);
LootRoll lootedRoll = rollMobDrops(LootTable::MobKind::Zombie, looted);
plainCeiling = std::max(plainCeiling, plainRoll.countOf(ItemId::RottenFlesh));
lootedCeiling = std::max(lootedCeiling, lootedRoll.countOf(ItemId::RottenFlesh));
plainIngots += plainRoll.countOf(ItemId::IronIngot);
lootedIngots += lootedRoll.countOf(ItemId::IronIngot);
}
CHECK_EQ(plainCeiling, 2);
CHECK_EQ(lootedCeiling, 5);
CHECK(lootedIngots > plainIngots);
}
VF_TEST(livestockDropRawMeatThatComesOutCookedWhenTheKillWasFiery) {
const struct { LootTable::MobKind kind; ItemId raw; ItemId cooked; int ceiling; } herd[] = {
{LootTable::MobKind::Sheep, ItemId::RawMutton, ItemId::CookedMutton, 2},
{LootTable::MobKind::Cow, ItemId::RawBeef, ItemId::CookedBeef, 3},
{LootTable::MobKind::Pig, ItemId::RawPorkchop, ItemId::CookedPorkchop, 3},
};
for (const auto& animal : herd) {
int rawSeen = 0;
int cookedSeen = 0;
int highest = 0;
for (int i = 0; i < 300; ++i) {
LootContext plain = killContext(true, seedFor(i));
LootRoll rawRoll = rollMobDrops(animal.kind, plain);
rawSeen += rawRoll.countOf(animal.raw);
highest = std::max(highest, rawRoll.countOf(animal.raw));
CHECK_EQ(rawRoll.countOf(animal.cooked), 0);
LootContext burning = killContext(true, seedFor(i));
burning.fiery = true;
LootRoll cookedRoll = rollMobDrops(animal.kind, burning);
cookedSeen += cookedRoll.countOf(animal.cooked);
CHECK_EQ(cookedRoll.countOf(animal.raw), 0);
}
CHECK_EQ(rawSeen, cookedSeen);
CHECK_EQ(highest, animal.ceiling);
}
}
VF_TEST(lootContextForCarriesTheKillOverAndRefusesADeadSeed) {
EnchantSet enchants;
addEnchant(enchants, EnchantKind::Looting, 2);