#include #include #include "testHarness.h" #include "vf/PotionCatalog.h" using namespace vf; namespace { using Ingredient = BrewRecipe::Ingredient; PotionItem bottleOf(PotionKind kind, bool upgraded = false, bool extended = false, PotionItem::Form form = PotionItem::Form::Drinkable) { PotionItem potion; potion.kind = kind; potion.upgraded = upgraded; potion.extended = extended; potion.form = form; return potion; } // Runs a whole ingredient list through an actual brewing stand, one full // four hundred tick brew per step. PotionItem replayOnAStand(const std::vector& steps) { BrewingStandState stand; setBottle(stand, 0, PotionItem{}); addFuel(stand, blazePowderForBrews(static_cast(steps.size())) + 1); for (Ingredient ingredient : steps) { setIngredient(stand, ingredient, 1); for (int tick = 0; tick < brewDurationTicks; ++tick) tickBrewing(stand); } return stand.bottles[0].potion; } PotionItem replayThroughSteps(const std::vector& steps) { PotionItem bottle; for (Ingredient ingredient : steps) { bool changed = false; bottle = catalogBrewStep(bottle, ingredient, changed); if (!changed) return PotionItem{}; } return bottle; } int splashDurationThroughTheStand(const PotionItem& potion) { PotionItem splash = potion; splash.form = PotionItem::Form::Splash; EffectSet target; if (!splashPotion(target, splash, 0.0f, 4.0f)) return 0; return target.active[0].durationTicks; } } // namespace VF_TEST(theCatalogsPublishedConstantsAreTheOnesTheNumbersWereDerivedFrom) { CHECK_NEAR(splashDurationScale, 0.75f, 0.0001f); CHECK_NEAR(lingeringDoseScale, 0.25f, 0.0001f); CHECK_EQ(lingeringCloudTicks, 600); CHECK_EQ(lingeringCloudTicks, 30 * ticksPerSecond); // Pinned against literals rather than the constants, so shifting a constant // cannot quietly shift the expectations with it. const PotionCatalog& catalog = potionCatalog(); PotionItem swiftness = bottleOf(PotionKind::Swiftness); CHECK_EQ(catalog.variantTicks(swiftness), 3600); swiftness.form = PotionItem::Form::Splash; CHECK_EQ(catalog.variantTicks(swiftness), 2700); swiftness.form = PotionItem::Form::Lingering; CHECK_EQ(catalog.variantTicks(swiftness), 900); CHECK_EQ(catalog.cloudTicks(swiftness), 600); } VF_TEST(theCatalogHasExactlyOneRowPerPotionKindInEnumOrder) { const PotionCatalog& catalog = potionCatalog(); CHECK_EQ(catalog.size(), static_cast(PotionKind::Count)); for (int index = 0; index < static_cast(PotionKind::Count); ++index) { PotionKind kind = static_cast(index); const PotionEntry& row = catalog.entry(kind); CHECK_EQ(row.kind, kind); CHECK(row.name != nullptr); CHECK(std::strlen(row.name) > 0); } int duplicateNames = 0; const std::vector& rows = catalog.entries(); for (std::size_t i = 0; i < rows.size(); ++i) { for (std::size_t j = i + 1; j < rows.size(); ++j) { if (std::strcmp(rows[i].name, rows[j].name) == 0) ++duplicateNames; } } CHECK_EQ(duplicateNames, 0); } VF_TEST(everyCatalogDurationAndLevelMatchesWhatTheStandActuallyApplies) { const PotionCatalog& catalog = potionCatalog(); for (int index = 0; index < static_cast(PotionKind::Count); ++index) { PotionKind kind = static_cast(index); const PotionEntry& row = catalog.entry(kind); const struct { bool upgraded; bool extended; } modifiers[] = { {false, false}, {true, false}, {false, true}, {true, true}}; for (const auto& modifier : modifiers) { PotionItem potion = bottleOf(kind, modifier.upgraded, modifier.extended); EffectInstance applied = potionEffect(potion); CHECK_EQ(catalog.variantTicks(potion), applied.durationTicks); if (row.carriesEffect()) { CHECK_EQ(catalog.variantLevel(potion), applied.level()); CHECK_EQ(row.effect, applied.kind); } else { CHECK_EQ(catalog.variantLevel(potion), 0); CHECK_EQ(applied.kind, EffectKind::Count); } } } } VF_TEST(theSplashColumnOfTheCatalogMatchesWhatASplashBottleLandsAtTheImpactPoint) { const PotionCatalog& catalog = potionCatalog(); int checkedTimedPotions = 0; for (const PotionEntry& row : catalog.entries()) { if (!row.carriesEffect()) continue; const struct { bool upgraded; bool extended; } modifiers[] = { {false, false}, {true, false}, {false, true}}; for (const auto& modifier : modifiers) { PotionItem drinkable = bottleOf(row.kind, modifier.upgraded, modifier.extended); PotionItem splash = drinkable; splash.form = PotionItem::Form::Splash; CHECK_EQ(catalog.variantTicks(splash), splashDurationThroughTheStand(drinkable)); CHECK_EQ(catalog.variantLevel(splash), catalog.variantLevel(drinkable)); if (!row.instant) ++checkedTimedPotions; } } CHECK(checkedTimedPotions >= 30); } VF_TEST(lingeringBottlesDoseAQuarterAndLeaveACloudBehindThem) { const PotionCatalog& catalog = potionCatalog(); PotionItem swiftness = bottleOf(PotionKind::Swiftness); CHECK_EQ(catalog.variantTicks(swiftness), 3600); CHECK_EQ(catalog.cloudTicks(swiftness), 0); PotionItem lingering = bottleOf(PotionKind::Swiftness, false, false, PotionItem::Form::Lingering); CHECK_EQ(catalog.variantTicks(lingering), 900); CHECK_EQ(catalog.cloudTicks(lingering), lingeringCloudTicks); PotionItem lingeringStrong = bottleOf(PotionKind::Swiftness, true, false, PotionItem::Form::Lingering); CHECK_EQ(catalog.variantTicks(lingeringStrong), 450); CHECK_EQ(catalog.variantLevel(lingeringStrong), 2);