test: cover testLootTables

This commit is contained in:
dualfroz committed 2026-09-11 23:39:08 +02:00
1 parent ad73588190
commit b4aae0528a
1 file changed
+157
+157
View File
@@ -160,3 +160,160 @@ VF_TEST(theRegistryIsNeverMorePermissiveThanTheSalvagedToolCheck) {
CHECK(gatedCases > 100);
}
VF_TEST(harvestLevelsGateTheOresExactlyWhereTheTiersSay) {
CHECK_EQ(toolHarvestLevel(ToolTier::None), 0);
CHECK_EQ(toolHarvestLevel(ToolTier::Wood), 1);
CHECK_EQ(toolHarvestLevel(ToolTier::Gold), 1);
CHECK_EQ(toolHarvestLevel(ToolTier::Stone), 2);
CHECK_EQ(toolHarvestLevel(ToolTier::Iron), 3);
CHECK_EQ(toolHarvestLevel(ToolTier::Diamond), 4);
const LootRegistry& registry = lootRegistry();
const struct { Block block; ItemId tool; bool allowed; } cases[] = {
{Block::Stone, ItemId::None, false},
{Block::Stone, ItemId::DiamondShovel, false},
{Block::Stone, ItemId::WoodPickaxe, true},
{Block::IronOre, ItemId::WoodPickaxe, false},
{Block::IronOre, ItemId::GoldPickaxe, false},
{Block::IronOre, ItemId::StonePickaxe, true},
{Block::DiamondOre, ItemId::StonePickaxe, false},
{Block::DiamondOre, ItemId::IronPickaxe, true},
{Block::Obsidian, ItemId::IronPickaxe, false},
{Block::Obsidian, ItemId::DiamondPickaxe, true},
{Block::CryingObsidian, ItemId::IronPickaxe, false},
{Block::CopperOre, ItemId::WoodPickaxe, false},
{Block::CopperOre, ItemId::StonePickaxe, true},
{Block::Dirt, ItemId::None, true},
{Block::Wood, ItemId::None, true},
};
for (const auto& item : cases) {
CHECK_EQ(toolAllows(registry.blockRule(item.block), item.tool), item.allowed);
}
CHECK(registry.rollBlock(Block::Obsidian, contextWith(ItemId::IronPickaxe, 9u)).empty());
CHECK_EQ(registry.rollBlock(Block::Obsidian, contextWith(ItemId::DiamondPickaxe, 9u))
.countOf(ItemId::Obsidian),
1);
}
VF_TEST(stoneBecomesCobblestoneUnlessItIsTakenWholeOrBrokenInsideFire) {
const LootRegistry& registry = lootRegistry();
LootContext plain = contextWith(ItemId::IronPickaxe, 17u);
CHECK_EQ(registry.rollBlock(Block::Stone, plain).countOf(ItemId::Cobblestone), 1);
LootContext silk = enchantedWith(ItemId::IronPickaxe, EnchantKind::SilkTouch, 1, 17u);
LootRoll harvested = registry.rollBlock(Block::Stone, silk);
CHECK_EQ(harvested.countOf(ItemId::Stone), 1);
CHECK_EQ(harvested.totalCount(), 1);
LootContext burning = contextWith(ItemId::IronPickaxe, 17u);
burning.fiery = true;
LootRoll melted = registry.rollBlock(Block::Stone, burning);
CHECK_EQ(melted.countOf(ItemId::Stone), 1);
CHECK_EQ(melted.countOf(ItemId::Cobblestone), 0);
// Cobblestone bakes back into stone the same way, and iron and gold ore
// come out as ingots.
LootRoll cobble = registry.rollBlock(Block::Cobblestone, burning);
CHECK_EQ(cobble.countOf(ItemId::Stone), 1);
CHECK_EQ(registry.rollBlock(Block::IronOre, burning).countOf(ItemId::IronIngot), 1);
CHECK_EQ(registry.rollBlock(Block::GoldOre, burning).countOf(ItemId::GoldIngot), 1);
CHECK_EQ(registry.rollBlock(Block::IronOre, plain).countOf(ItemId::IronOre), 1);
}
VF_TEST(oreRulesMultiplyWithFortuneAndHandBackTheOreBlockUnderSilkTouch) {
const LootRegistry& registry = lootRegistry();
const struct { Block block; ItemId raw; int lowest; int highest; } ores[] = {
{Block::CoalOre, ItemId::CoalLump, 1, 1},
{Block::DiamondOre, ItemId::DiamondGem, 1, 1},
{Block::RedstoneOre, ItemId::RedstoneDust, 4, 5},
{Block::LapisOre, ItemId::LapisLazuli, 4, 9},
{Block::Glowstone, ItemId::Glowstone, 2, 4},
};
for (const auto& ore : ores) {
int seenLowest = 1000;
int seenHighest = 0;
int fortuneHighest = 0;
for (int i = 0; i < 500; ++i) {
LootContext plain = contextWith(ItemId::DiamondPickaxe, seedFor(i));
int count = registry.rollBlock(ore.block, plain).countOf(ore.raw);
seenLowest = std::min(seenLowest, count);
seenHighest = std::max(seenHighest, count);
LootContext lucky = enchantedWith(ItemId::DiamondPickaxe, EnchantKind::Fortune, 3,
seedFor(i));
fortuneHighest = std::max(fortuneHighest,
registry.rollBlock(ore.block, lucky).countOf(ore.raw));
}
CHECK_EQ(seenLowest, ore.lowest);
CHECK_EQ(seenHighest, ore.highest);
CHECK_EQ(fortuneHighest, ore.highest * 4);
LootContext silk = enchantedWith(ItemId::DiamondPickaxe, EnchantKind::SilkTouch, 1, 5u);
LootRoll whole = registry.rollBlock(ore.block, silk);
CHECK_EQ(whole.countOf(itemForBlock(ore.block)), 1);
CHECK_EQ(whole.totalCount(), 1);
}
}
VF_TEST(shearClassToolsTakeTheBlockItselfWithoutNeedingSilkTouch) {
const LootRegistry& registry = lootRegistry();
for (ItemId hoe : {ItemId::WoodHoe, ItemId::DiamondHoe}) {
LootRoll sheared = registry.rollBlock(Block::Leaves, contextWith(hoe, 42u));
CHECK_EQ(sheared.countOf(ItemId::Leaves), 1);
CHECK_EQ(sheared.totalCount(), 1);
}
// A pickaxe falls through to the chance table instead.
int leafBlocks = 0;
int cuttings = 0;
for (int i = 0; i < 800; ++i) {
LootRoll roll = registry.rollBlock(Block::Leaves,
contextWith(ItemId::IronPickaxe, seedFor(i)));
leafBlocks += roll.countOf(ItemId::Leaves);
cuttings += roll.countOf(ItemId::Wood);
}
CHECK_EQ(leafBlocks, 0);
CHECK(cuttings > 0);
// Shearing a block the palette has no item for still yields nothing.
for (Block block : {Block::Wool, Block::Cobweb, Block::Vine, Block::Kelp, Block::Moss}) {
CHECK(registry.blockRule(block).shearHarvest);
CHECK(registry.rollBlock(block, contextWith(ItemId::DiamondHoe, 8u)).empty());
}
}
VF_TEST(everyBlockRuleOnlyEverProducesItemsItDeclaredUpFront) {
const LootRegistry& registry = lootRegistry();
int strayItems = 0;
int rollsMade = 0;
for (int index = 0; index < static_cast<int>(Block::Count); ++index) {
Block block = static_cast<Block>(index);
const BlockDropRule& rule = registry.blockRule(block);
for (int sample = 0; sample < 12; ++sample) {
LootContext context = contextWith(bestToolFor(rule.harvestTool), seedFor(sample));
if (sample % 3 == 1) addEnchant(context.toolEnchants, EnchantKind::SilkTouch, 1);
if (sample % 3 == 2) addEnchant(context.toolEnchants, EnchantKind::Fortune, 3);
if (sample % 4 == 3) context.fiery = true;
LootRoll roll = registry.rollBlock(block, context);
++rollsMade;
for (const ItemSlot& stack : roll.items) {
if (!ruleCanYield(rule, stack.item)) ++strayItems;
if (stack.count <= 0) ++strayItems;
}
}
}
CHECK_EQ(strayItems, 0);
CHECK_EQ(rollsMade, static_cast<int>(Block::Count) * 12);
}
VF_TEST(everyBlockThatDeclaresACertainDropActuallyHandsItOver) {
const LootRegistry& registry = lootRegistry();
int certainDrops = 0;
int missed = 0;