test: extend coverage of testLootTables
This commit is contained in:
1 parent
b4aae0528a
commit
2d5ad51975
1 file changed
+161
@@ -317,3 +317,164 @@ VF_TEST(everyBlockThatDeclaresACertainDropActuallyHandsItOver) {
|
|||||||
int certainDrops = 0;
|
int certainDrops = 0;
|
||||||
int missed = 0;
|
int missed = 0;
|
||||||
|
|
||||||
|
for (int index = 0; index < static_cast<int>(Block::Count); ++index) {
|
||||||
|
Block block = static_cast<Block>(index);
|
||||||
|
const BlockDropRule& rule = registry.blockRule(block);
|
||||||
|
if (rule.drop == ItemId::None || rule.dropChance < 1.0f) continue;
|
||||||
|
++certainDrops;
|
||||||
|
|
||||||
|
LootContext context = contextWith(bestToolFor(rule.harvestTool), seedFor(index));
|
||||||
|
LootRoll roll = registry.rollBlock(block, context);
|
||||||
|
if (roll.countOf(rule.drop) < rule.minCount) ++missed;
|
||||||
|
}
|
||||||
|
CHECK_EQ(missed, 0);
|
||||||
|
CHECK_EQ(certainDrops, 62);
|
||||||
|
}
|
||||||
|
|
||||||
|
VF_TEST(blocksTheItemPaletteCannotExpressStillCarryARuleThatYieldsNothing) {
|
||||||
|
const LootRegistry& registry = lootRegistry();
|
||||||
|
int barren = 0;
|
||||||
|
for (int index = 0; index < static_cast<int>(Block::Count); ++index) {
|
||||||
|
Block block = static_cast<Block>(index);
|
||||||
|
const BlockDropRule& rule = registry.blockRule(block);
|
||||||
|
if (!rule.yieldsNothing()) continue;
|
||||||
|
++barren;
|
||||||
|
CHECK_EQ(rule.block, block);
|
||||||
|
CHECK(blockRuleTable(rule).empty());
|
||||||
|
|
||||||
|
LootContext generous = enchantedWith(bestToolFor(rule.harvestTool), EnchantKind::Fortune, 3,
|
||||||
|
seedFor(index));
|
||||||
|
addEnchant(generous.toolEnchants, EnchantKind::SilkTouch, 1);
|
||||||
|
CHECK(registry.rollBlock(block, generous).empty());
|
||||||
|
}
|
||||||
|
CHECK_EQ(barren, static_cast<int>(Block::Count) - 64);
|
||||||
|
}
|
||||||
|
|
||||||
|
VF_TEST(theSameSeedReproducesTheSameDropForEveryBlockInThePalette) {
|
||||||
|
const LootRegistry& registry = lootRegistry();
|
||||||
|
int divergences = 0;
|
||||||
|
for (int index = 0; index < static_cast<int>(Block::Count); ++index) {
|
||||||
|
Block block = static_cast<Block>(index);
|
||||||
|
LootContext context = enchantedWith(ItemId::DiamondPickaxe, EnchantKind::Fortune, 2,
|
||||||
|
seedFor(index));
|
||||||
|
LootRoll first = registry.rollBlock(block, context);
|
||||||
|
LootRoll second = registry.rollBlock(block, context);
|
||||||
|
if (!sameRoll(first, second)) ++divergences;
|
||||||
|
}
|
||||||
|
CHECK_EQ(divergences, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
VF_TEST(everyMobRuleOnlyEverProducesItemsItDeclaredUpFront) {
|
||||||
|
const LootRegistry& registry = lootRegistry();
|
||||||
|
int strayItems = 0;
|
||||||
|
for (int index = 0; index < static_cast<int>(Species::Count); ++index) {
|
||||||
|
Species species = static_cast<Species>(index);
|
||||||
|
const MobDropRule& rule = registry.mobRule(species);
|
||||||
|
CHECK_EQ(rule.species, species);
|
||||||
|
CHECK(rule.maxCount >= rule.minCount);
|
||||||
|
|
||||||
|
for (int sample = 0; sample < 40; ++sample) {
|
||||||
|
LootContext context = contextWith(ItemId::IronSword, seedFor(sample));
|
||||||
|
context.playerKill = sample % 2 == 0;
|
||||||
|
context.fiery = sample % 3 == 0;
|
||||||
|
if (sample % 5 == 0) addEnchant(context.toolEnchants, EnchantKind::Looting, 3);
|
||||||
|
|
||||||
|
for (const ItemSlot& stack : registry.rollMob(species, context).items) {
|
||||||
|
bool declared = stack.item == rule.drop || stack.item == rule.firedDrop ||
|
||||||
|
stack.item == rule.rareDrop;
|
||||||
|
if (!declared) ++strayItems;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
CHECK_EQ(strayItems, 0);
|
||||||
|
CHECK(registry.rollMob(Species::Count, contextWith(ItemId::None, 1u)).empty());
|
||||||
|
}
|
||||||
|
|
||||||
|
VF_TEST(theRegistryAgreesWithTheSalvagedMobTablesOnEverySharedSpecies) {
|
||||||
|
const LootRegistry& registry = lootRegistry();
|
||||||
|
const struct { Species species; LootTable::MobKind kind; } shared[] = {
|
||||||
|
{Species::Zombie, LootTable::MobKind::Zombie},
|
||||||
|
{Species::Skeleton, LootTable::MobKind::Skeleton},
|
||||||
|
{Species::Creeper, LootTable::MobKind::Creeper},
|
||||||
|
{Species::Sheep, LootTable::MobKind::Sheep},
|
||||||
|
{Species::Cow, LootTable::MobKind::Cow},
|
||||||
|
{Species::Pig, LootTable::MobKind::Pig},
|
||||||
|
};
|
||||||
|
for (const auto& pair : shared) {
|
||||||
|
const MobDropRule& rule = registry.mobRule(pair.species);
|
||||||
|
const LootTable& salvaged = mobLootTable(pair.kind);
|
||||||
|
CHECK_EQ(rule.drop, firstEntryOfPool(salvaged, 0));
|
||||||
|
if (rule.rareDrop != ItemId::None) {
|
||||||
|
CHECK_EQ(rule.rareDrop, firstEntryOfPool(salvaged, 1));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
VF_TEST(livestockComesOutCookedWhenTheKillWasFieryAndRawOtherwise) {
|
||||||
|
const LootRegistry& registry = lootRegistry();
|
||||||
|
const struct { Species species; ItemId raw; ItemId cooked; } herd[] = {
|
||||||
|
{Species::Sheep, ItemId::RawMutton, ItemId::CookedMutton},
|
||||||
|
{Species::Cow, ItemId::RawBeef, ItemId::CookedBeef},
|
||||||
|
{Species::Pig, ItemId::RawPorkchop, ItemId::CookedPorkchop},
|
||||||
|
};
|
||||||
|
for (const auto& animal : herd) {
|
||||||
|
CHECK(!registry.mobRule(animal.species).hostile);
|
||||||
|
int raw = 0;
|
||||||
|
int cooked = 0;
|
||||||
|
for (int i = 0; i < 300; ++i) {
|
||||||
|
LootContext cold = contextWith(ItemId::IronSword, seedFor(i));
|
||||||
|
LootRoll rawRoll = registry.rollMob(animal.species, cold);
|
||||||
|
raw += rawRoll.countOf(animal.raw);
|
||||||
|
CHECK_EQ(rawRoll.countOf(animal.cooked), 0);
|
||||||
|
|
||||||
|
LootContext burning = cold;
|
||||||
|
burning.fiery = true;
|
||||||
|
LootRoll cookedRoll = registry.rollMob(animal.species, burning);
|
||||||
|
cooked += cookedRoll.countOf(animal.cooked);
|
||||||
|
CHECK_EQ(cookedRoll.countOf(animal.raw), 0);
|
||||||
|
}
|
||||||
|
CHECK(raw > 0);
|
||||||
|
CHECK_EQ(raw, cooked);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
VF_TEST(dropsThatNeedAPlayerKillNeverShowUpFromAnEnvironmentalDeath) {
|
||||||
|
const LootRegistry& registry = lootRegistry();
|
||||||
|
CHECK(registry.mobRule(Species::Blaze).dropNeedsPlayerKill);
|
||||||
|
|
||||||
|
int fromPlayers = 0;
|
||||||
|
int fromTheWorld = 0;
|
||||||
|
int witchGlowstoneFromTheWorld = 0;
|
||||||
|
for (int i = 0; i < 2000; ++i) {
|
||||||
|
LootContext byPlayer = contextWith(ItemId::IronSword, seedFor(i));
|
||||||
|
byPlayer.playerKill = true;
|
||||||
|
LootContext byWorld = contextWith(ItemId::None, seedFor(i));
|
||||||
|
|
||||||
|
fromPlayers += registry.rollMob(Species::Blaze, byPlayer).countOf(ItemId::CoalLump);
|
||||||
|
fromTheWorld += registry.rollMob(Species::Blaze, byWorld).countOf(ItemId::CoalLump);
|
||||||
|
witchGlowstoneFromTheWorld +=
|
||||||
|
registry.rollMob(Species::Witch, byWorld).countOf(ItemId::Glowstone);
|
||||||
|
}
|
||||||
|
CHECK(fromPlayers > 0);
|
||||||
|
CHECK_EQ(fromTheWorld, 0);
|
||||||
|
|
||||||
|
// A witch is the counter example: her glowstone is not player gated.
|
||||||
|
CHECK(!registry.mobRule(Species::Witch).rareNeedsPlayerKill);
|
||||||
|
CHECK(witchGlowstoneFromTheWorld > 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
VF_TEST(ironGolemsIgnoreLootingWhileZombiesDoNot) {
|
||||||
|
const LootRegistry& registry = lootRegistry();
|
||||||
|
CHECK(!registry.mobRule(Species::IronGolem).lootingRaisesCount);
|
||||||
|
|
||||||
|
int golemLowest = 100;
|
||||||
|
int golemHighest = 0;
|
||||||
|
int golemLootedHighest = 0;
|
||||||
|
int zombieHighest = 0;
|
||||||
|
int zombieLootedHighest = 0;
|
||||||
|
for (int i = 0; i < 1500; ++i) {
|
||||||
|
LootContext plain = contextWith(ItemId::IronSword, seedFor(i));
|
||||||
|
plain.playerKill = true;
|
||||||
|
LootContext looted = plain;
|
||||||
|
addEnchant(looted.toolEnchants, EnchantKind::Looting, 3);
|
||||||
|
|
||||||
Reference in new issue
Block a user