diff --git a/core/include/vf/Brewing.h b/core/include/vf/Brewing.h new file mode 100644 index 0000000..11b747f --- /dev/null +++ b/core/include/vf/Brewing.h @@ -0,0 +1,149 @@ +#pragma once + +#include +#include +#include + +#include "Effect.h" + +namespace vf { + +// A stand holds three bottles, one ingredient stack and a blaze powder fuel bar. +// Every completed brew converts all three bottles at once and spends a single +// unit of ingredient and a single unit of fuel. +constexpr int brewingBottleSlots = 3; +constexpr int brewDurationTicks = 400; +constexpr int brewsPerBlazePowder = 20; + +// Ordering is load bearing: the profile table in Brewing.cpp is indexed by this +// enum and static_asserts against Count. +enum class PotionKind : uint8_t { + Water, + Mundane, + Thick, + Awkward, + Healing, + Harming, + Regeneration, + Poison, + Swiftness, + Slowness, + Leaping, + Strength, + Weakness, + FireResistance, + WaterBreathing, + NightVision, + Invisibility, + Count +}; + +struct PotionItem { + // Gunpowder turns a drinkable into a splash. Dragon breath turns a splash + // into a lingering cloud, which the stand itself cannot do: see + // catalogBrewStep in PotionCatalog.h. + enum class Form : uint8_t { Drinkable, Splash, Lingering }; + + PotionKind kind = PotionKind::Water; + Form form = Form::Drinkable; + // Glowstone raises the level and shortens the potion, redstone lengthens it + // at level one. The two are mutually exclusive, the later one wins. + bool upgraded = false; + bool extended = false; + + bool operator==(const PotionItem& other) const { + return kind == other.kind && form == other.form && upgraded == other.upgraded && + extended == other.extended; + } + bool operator!=(const PotionItem& other) const { return !(*this == other); } +}; + +struct BrewRecipe { + enum class Ingredient : uint8_t { + None, + NetherWart, + Redstone, + Glowstone, + Gunpowder, + DragonBreath, + FermentedSpiderEye, + SpiderEye, + GhastTear, + Sugar, + RabbitFoot, + BlazePowder, + GlisteringMelon, + MagmaCream, + PufferFish, + GoldenCarrot, + Count + }; + + PotionKind input = PotionKind::Water; + Ingredient ingredient = Ingredient::None; + PotionKind output = PotionKind::Water; +}; + +struct BrewingSlot { + PotionItem potion; + bool occupied = false; +}; + +struct BrewingStandState { + std::array bottles{}; + BrewRecipe::Ingredient ingredient = BrewRecipe::Ingredient::None; + int ingredientCount = 0; + // Measured in brews, not in items: addFuel multiplies by brewsPerBlazePowder. + int fuel = 0; + int progressTicks = 0; + bool brewing = false; +}; + +// Every kind-changing recipe the stand knows. Modifier ingredients (glowstone, +// redstone, gunpowder) are not recipes and do not appear here. +const std::vector& brewRecipes(); + +// Resolves one ingredient against one bottle. A kind change wipes the glowstone +// and redstone modifiers but keeps the form. Sets `changed` to false when the +// ingredient does nothing to that bottle, in which case the input is returned +// unaltered. +PotionItem brewResult(const PotionItem& base, BrewRecipe::Ingredient ingredient, bool& changed); + +// Fails when the slot index is out of range or already holds a bottle. +bool setBottle(BrewingStandState& stand, int slot, const PotionItem& potion); +PotionItem takeBottle(BrewingStandState& stand, int slot, bool& taken); + +// Stacks onto the existing ingredient when it matches, refuses a different one +// while the current stack is not empty. +bool setIngredient(BrewingStandState& stand, BrewRecipe::Ingredient ingredient, int count); +bool addFuel(BrewingStandState& stand, int blazePowderCount); + +// True when fuel, ingredient and at least one bottle that the ingredient would +// actually change are all present. +bool canBrew(const BrewingStandState& stand); + +// One tick of the stand. Returns true on the tick the brew completes, which is +// the brewDurationTicks-th consecutive tick with canBrew holding. Any tick where +// canBrew fails resets the progress bar to zero. +bool tickBrewing(BrewingStandState& stand); + +// Water, mundane, thick and awkward carry no effect. +bool potionHasEffect(const PotionItem& potion); + +// The effect a bottle would apply, with the level and duration its glowstone or +// redstone modifier calls for. Returns an EffectKind::Count instance for the +// four effectless kinds. +EffectInstance potionEffect(const PotionItem& potion); + +// Drinkable form only. Returns whatever applyEffect reports. +bool drinkPotion(EffectSet& target, const PotionItem& potion); + +// Linear falloff from the impact point, clamped to 0..1. +float splashPotency(float distance, float radius); + +// Splash form only. Instant effects scale their magnitude by the potency, timed +// effects keep their level and lose duration: three quarters at the impact +// point, less further out. +bool splashPotion(EffectSet& target, const PotionItem& potion, float distance, float radius); + +} // namespace vf