test: add more cases for testLootTables

This commit is contained in:
dualfroz committed 2026-09-12 11:29:24 +02:00
1 parent 2d5ad51975
commit d87424e3c4
1 file changed
+165
+165
View File
@@ -478,3 +478,168 @@ VF_TEST(ironGolemsIgnoreLootingWhileZombiesDoNot) {
LootContext looted = plain;
addEnchant(looted.toolEnchants, EnchantKind::Looting, 3);
int golem = registry.rollMob(Species::IronGolem, plain).countOf(ItemId::IronIngot);
golemLowest = std::min(golemLowest, golem);
golemHighest = std::max(golemHighest, golem);
golemLootedHighest = std::max(golemLootedHighest,
registry.rollMob(Species::IronGolem, looted)
.countOf(ItemId::IronIngot));
zombieHighest = std::max(zombieHighest,
registry.rollMob(Species::Zombie, plain)
.countOf(ItemId::RottenFlesh));
zombieLootedHighest = std::max(zombieLootedHighest,
registry.rollMob(Species::Zombie, looted)
.countOf(ItemId::RottenFlesh));
}
CHECK_EQ(golemLowest, 3);
CHECK_EQ(golemHighest, 5);
CHECK_EQ(golemLootedHighest, 5);
CHECK_EQ(zombieHighest, 2);
CHECK_EQ(zombieLootedHighest, 5);
}
VF_TEST(villagersLeaveNothingBehindAndSayItInTheirRule) {
const LootRegistry& registry = lootRegistry();
const MobDropRule& villager = registry.mobRule(Species::Villager);
CHECK_EQ(villager.drop, ItemId::None);
CHECK_EQ(villager.rareDrop, ItemId::None);
CHECK(!villager.hostile);
CHECK(mobRuleTable(villager).empty());
for (int i = 0; i < 200; ++i) {
LootContext context = enchantedWith(ItemId::DiamondSword, EnchantKind::Looting, 3,
seedFor(i));
context.playerKill = true;
CHECK(registry.rollMob(Species::Villager, context).empty());
}
}
VF_TEST(everyStructureChestFillsInsideItsDeclaredBoundsAndOnlyFromItsOwnPool) {
const LootRegistry& registry = lootRegistry();
const struct { Structure structure; int lowest; int highest; } bounds[] = {
{Structure::Village, 3, 20}, {Structure::Dungeon, 2, 16},
{Structure::Mineshaft, 3, 40}, {Structure::Temple, 2, 32},
{Structure::NetherFortress, 2, 20}, {Structure::EndShip, 2, 18},
};
for (const auto& entry : bounds) {
const ChestFill& fill = registry.chestFill(entry.structure);
CHECK_EQ(fill.structure, entry.structure);
CHECK(!fill.pool.entries.empty());
int seenLowest = 1000;
int seenHighest = 0;
int strayItems = 0;
for (int i = 0; i < 700; ++i) {
LootRoll roll = registry.rollChest(entry.structure, contextWith(ItemId::None,
seedFor(i)));
seenLowest = std::min(seenLowest, roll.totalCount());
seenHighest = std::max(seenHighest, roll.totalCount());
for (const ItemSlot& stack : roll.items) {
if (!poolCanYield(fill.pool, stack.item)) ++strayItems;
}
}
CHECK_EQ(strayItems, 0);
CHECK(seenLowest >= entry.lowest);
CHECK(seenHighest <= entry.highest);
CHECK(seenHighest > seenLowest);
}
CHECK(registry.rollChest(Structure::Count, contextWith(ItemId::None, 1u)).empty());
}
VF_TEST(structureChestCountsStayInsideTheRangeTheStructureDeclares) {
const LootRegistry& registry = lootRegistry();
for (int index = 0; index < static_cast<int>(Structure::Count); ++index) {
Structure structure = static_cast<Structure>(index);
const ChestFill& fill = registry.chestFill(structure);
CHECK(fill.chestsMin >= 1);
CHECK(fill.chestsMax >= fill.chestsMin);
int seenLowest = 1000;
int seenHighest = 0;
for (int i = 0; i < 400; ++i) {
int count = registry.chestCountFor(structure, seedFor(i));
seenLowest = std::min(seenLowest, count);
seenHighest = std::max(seenHighest, count);
}
CHECK_EQ(seenLowest, fill.chestsMin);
CHECK_EQ(seenHighest, fill.chestsMax);
CHECK_EQ(registry.chestCountFor(structure, 77u), registry.chestCountFor(structure, 77u));
}
CHECK_EQ(registry.chestCountFor(Structure::Count, 5u), 0);
}
VF_TEST(diamondGearOnlyTurnsUpInTheEndShipAndPickaxesOnlyInMineshafts) {
const LootRegistry& registry = lootRegistry();
int endShipSwords = 0;
int mineshaftPickaxes = 0;
int elsewhereGear = 0;
for (int index = 0; index < static_cast<int>(Structure::Count); ++index) {
Structure structure = static_cast<Structure>(index);
for (int i = 0; i < 600; ++i) {
LootRoll roll = registry.rollChest(structure, contextWith(ItemId::None, seedFor(i)));
int swords = roll.countOf(ItemId::DiamondSword);
int pickaxes = roll.countOf(ItemId::IronPickaxe);
if (structure == Structure::EndShip) {
endShipSwords += swords;
} else {
elsewhereGear += swords;
}
if (structure == Structure::Mineshaft) {
mineshaftPickaxes += pickaxes;
} else {
elsewhereGear += pickaxes;
}
}
}
CHECK(endShipSwords > 0);
CHECK(mineshaftPickaxes > 0);
CHECK_EQ(elsewhereGear, 0);
}
VF_TEST(fishingSplitsIntoThreePoolsAndLuckShiftsTheWeightsAwayFromJunk) {
const LootRegistry& registry = lootRegistry();
int plainFish = 0;
int plainTreasure = 0;
int plainJunk = 0;
int luckyTreasure = 0;
int luckyJunk = 0;
for (int i = 0; i < 4000; ++i) {
FishingLoot plain = registry.rollFishing(contextWith(ItemId::WoodHoe, seedFor(i)));
CHECK_EQ(plain.fishWeight, fishingFishWeight);
CHECK_EQ(plain.treasureWeight, fishingTreasureWeight);
CHECK_EQ(plain.junkWeight, fishingJunkWeight);
if (plain.pool == FishingLoot::Pool::Fish) ++plainFish;
if (plain.pool == FishingLoot::Pool::Treasure) ++plainTreasure;
if (plain.pool == FishingLoot::Pool::Junk) ++plainJunk;
FishingLoot lucky = registry.rollFishing(
enchantedWith(ItemId::WoodHoe, EnchantKind::Fortune, 3, seedFor(i)));
CHECK_EQ(lucky.treasureWeight, fishingTreasureWeight + 3 * fishingLuckWeightShift);
CHECK_EQ(lucky.junkWeight, fishingJunkWeight - 3 * fishingLuckWeightShift);
if (lucky.pool == FishingLoot::Pool::Treasure) ++luckyTreasure;
if (lucky.pool == FishingLoot::Pool::Junk) ++luckyJunk;
}
CHECK(plainFish > plainTreasure + plainJunk);
CHECK(plainTreasure > 0);
CHECK(plainJunk > 0);
CHECK(luckyTreasure > plainTreasure);
CHECK(luckyJunk < plainJunk);
// An over-cap Fortune cannot come from addEnchant, but it can be placed on
// a set by hand, and it must clamp the junk weight at nothing rather than
// going negative and eating the treasure band.
int junkAtHighLuck = 0;
for (int i = 0; i < 500; ++i) {
LootContext overCharged = contextWith(ItemId::WoodHoe, seedFor(i));
overCharged.toolEnchants.entries.push_back({EnchantKind::Fortune, 9});
FishingLoot outcome = registry.rollFishing(overCharged);
CHECK_EQ(outcome.junkWeight, 0);
CHECK_EQ(outcome.treasureWeight, fishingTreasureWeight + 9 * fishingLuckWeightShift);
if (outcome.pool == FishingLoot::Pool::Junk) ++junkAtHighLuck;
}
CHECK_EQ(junkAtHighLuck, 0);
}