diff --git a/core/src/PotionCatalog.cpp b/core/src/PotionCatalog.cpp index fd2d16d..a25383d 100644 --- a/core/src/PotionCatalog.cpp +++ b/core/src/PotionCatalog.cpp @@ -155,3 +155,156 @@ int PotionCatalog::variantTicks(const PotionItem& potion) const { if (!row.carriesEffect()) return 0; if (row.instant) return 1; + int ticks = row.normalTicks; + if (potion.upgraded && row.acceptsGlowstone()) { + ticks = row.upgradedTicks; + } else if (potion.extended && row.acceptsRedstone()) { + ticks = row.extendedTicks; + } + + if (potion.form == PotionItem::Form::Splash) { + return static_cast(std::floor(static_cast(ticks) * splashDurationScale)); + } + if (potion.form == PotionItem::Form::Lingering) { + return static_cast(std::floor(static_cast(ticks) * lingeringDoseScale)); + } + return ticks; +} + +int PotionCatalog::cloudTicks(const PotionItem& potion) const { + if (potion.form != PotionItem::Form::Lingering) return 0; + if (!entry(potion.kind).carriesEffect()) return 0; + return lingeringCloudTicks; +} + +const PotionCatalog& potionCatalog() { + static const PotionCatalog catalog; + return catalog; +} + +int blazePowderForBrews(int brewCount) { + if (brewCount <= 0) return 0; + return (brewCount + brewsPerBlazePowder - 1) / brewsPerBlazePowder; +} + +PotionItem catalogBrewStep(const PotionItem& base, Ingredient ingredient, bool& changed) { + changed = false; + if (base.form == PotionItem::Form::Lingering) return base; + if (ingredient == Ingredient::DragonBreath) { + if (base.form != PotionItem::Form::Splash) return base; + PotionItem lingering = base; + lingering.form = PotionItem::Form::Lingering; + changed = true; + return lingering; + } + return brewResult(base, ingredient, changed); +} + +int BrewGraph::stateCount() { return potionKindCount * potionFormCount * potionModifierCount; } + +int BrewGraph::stateIndex(const PotionItem& potion) { + int kind = static_cast(potion.kind); + int form = static_cast(potion.form); + return (kind * potionFormCount + form) * potionModifierCount + modifierIndex(potion); +} + +PotionItem BrewGraph::stateAt(int index) { + PotionItem potion; + int modifier = index % potionModifierCount; + int rest = index / potionModifierCount; + potion.form = static_cast(rest % potionFormCount); + potion.kind = static_cast(rest / potionFormCount); + potion.upgraded = modifier == 1; + potion.extended = modifier == 2; + return potion; +} + +BrewGraph::BrewGraph(const PotionCatalog& source) : catalog(&source), nodes(stateCount()) { + int start = stateIndex(PotionItem{}); + nodes[static_cast(start)].depth = 0; + + std::deque frontier; + frontier.push_back(start); + while (!frontier.empty()) { + int current = frontier.front(); + frontier.pop_front(); + PotionItem bottle = stateAt(current); + + for (int step = 1; step < static_cast(Ingredient::Count); ++step) { + Ingredient ingredient = static_cast(step); + bool changed = false; + PotionItem brewed = catalogBrewStep(bottle, ingredient, changed); + if (!changed) continue; + + int next = stateIndex(brewed); + Node& node = nodes[static_cast(next)]; + if (node.depth >= 0) continue; + node.depth = nodes[static_cast(current)].depth + 1; + node.previous = current; + node.via = ingredient; + frontier.push_back(next); + } + } +} + +int BrewGraph::depthOf(const PotionItem& potion) const { + return nodes[static_cast(stateIndex(potion))].depth; +} + +bool BrewGraph::reachable(const PotionItem& potion) const { return depthOf(potion) >= 0; } + +BrewPath BrewGraph::shortestPath(const PotionItem& target) const { + BrewPath path; + path.target = target; + + int index = stateIndex(target); + if (nodes[static_cast(index)].depth < 0) return path; + + path.reachable = true; + while (nodes[static_cast(index)].previous >= 0) { + const Node& node = nodes[static_cast(index)]; + path.steps.push_back(node.via); + if (node.via == Ingredient::BlazePowder) ++path.blazePowderIngredient; + index = node.previous; + } + std::reverse(path.steps.begin(), path.steps.end()); + path.blazePowderFuel = blazePowderForBrews(path.stepCount()); + path.totalTicks = path.stepCount() * brewDurationTicks; + return path; +} + +BrewPath BrewGraph::shortestPath(PotionKind kind) const { + PotionItem bottle; + bottle.kind = kind; + return shortestPath(bottle); +} + +int BrewGraph::reachableStateCount() const { + int total = 0; + for (const Node& node : nodes) { + if (node.depth >= 0) ++total; + } + return total; +} + +std::vector BrewGraph::statesAtDepth(int steps) const { + std::vector found; + for (int index = 0; index < stateCount(); ++index) { + if (nodes[static_cast(index)].depth != steps) continue; + found.push_back(stateAt(index)); + } + return found; +} + +std::vector BrewGraph::drinkablePaths() const { + std::vector paths; + for (const PotionEntry& row : catalog->entries()) paths.push_back(shortestPath(row.kind)); + return paths; +} + +const BrewGraph& brewGraph() { + static const BrewGraph graph(potionCatalog()); + return graph; +} + +} // namespace vf