Files
warren/src/alchemy.lua
T

103 lines
4.8 KiB
Lua

-- Herb-based potion brewing, layered on top of items.lua's existing potion
-- catalog rather than inventing a second one: a cauldron turns two foraged
-- herbs into the same potions rollItem already scatters as loot, so a
-- brewed potion drinks, throws (throwing.lua) and identifies exactly like a
-- found one. Recipes are fixed data, the same way crafting.lua's elemental
-- riders are fixed - the uncertainty here is not "what does this pair do"
-- but whether two ill-matched herbs curdle instead of brewing cleanly. See
-- Game:brewAt for how a curdle is resolved against the brewer.
local Items = require("items")
local Alchemy = {}
Alchemy.SUNPETAL = "sunpetal"
Alchemy.BLOODROOT = "bloodroot"
Alchemy.EMBERLEAF = "emberleaf"
Alchemy.FROSTMOSS = "frostmoss"
Alchemy.NIGHTCAP = "nightcap"
Alchemy.MOONBELL = "moonbell"
-- Fixed order so a given rng draw always names the same herb. New herbs
-- append at the end (see builder.lua's own comment on Alchemy.order) so an
-- existing seed's earlier picks never change which herb they name.
Alchemy.order = {
Alchemy.BLOODROOT, Alchemy.EMBERLEAF, Alchemy.FROSTMOSS, Alchemy.NIGHTCAP, Alchemy.SUNPETAL,
Alchemy.MOONBELL,
}
Alchemy.names = {
[Alchemy.SUNPETAL] = "sunpetal",
[Alchemy.BLOODROOT] = "bloodroot",
[Alchemy.EMBERLEAF] = "emberleaf",
[Alchemy.FROSTMOSS] = "frostmoss",
[Alchemy.NIGHTCAP] = "nightcap",
[Alchemy.MOONBELL] = "moonbell",
}
local CURDLE = { curdle = true }
-- Canonical order-independent key for a herb pair: the two herb keys sorted
-- and joined, so brewing (A, B) and (B, A) always land on the same recipe.
local function pairKey(a, b)
if a > b then a, b = b, a end
return a .. "+" .. b
end
Alchemy.pairKey = pairKey
-- Same-herb pairs concentrate that herb's own aspect; cross pairs blend two
-- aspects into a shared potion, except the pairings whose aspects outright
-- oppose each other (light versus shade, fire versus frost, day versus
-- dream), which curdle instead of brewing.
Alchemy.recipes = {
[pairKey(Alchemy.SUNPETAL, Alchemy.SUNPETAL)] = { effect = "heal", power = 18 },
[pairKey(Alchemy.SUNPETAL, Alchemy.BLOODROOT)] = { effect = "heal", power = 10 },
[pairKey(Alchemy.SUNPETAL, Alchemy.EMBERLEAF)] = { effect = "haste" },
[pairKey(Alchemy.SUNPETAL, Alchemy.FROSTMOSS)] = { effect = "fortify" },
[pairKey(Alchemy.SUNPETAL, Alchemy.NIGHTCAP)] = CURDLE,
[pairKey(Alchemy.BLOODROOT, Alchemy.BLOODROOT)] = { effect = "strength" },
[pairKey(Alchemy.BLOODROOT, Alchemy.EMBERLEAF)] = { effect = "strength" },
[pairKey(Alchemy.BLOODROOT, Alchemy.FROSTMOSS)] = { effect = "fortify" },
[pairKey(Alchemy.BLOODROOT, Alchemy.NIGHTCAP)] = { effect = "poison", power = 4 },
[pairKey(Alchemy.EMBERLEAF, Alchemy.EMBERLEAF)] = { effect = "haste" },
[pairKey(Alchemy.EMBERLEAF, Alchemy.FROSTMOSS)] = CURDLE,
[pairKey(Alchemy.EMBERLEAF, Alchemy.NIGHTCAP)] = { effect = "poison", power = 5 },
[pairKey(Alchemy.FROSTMOSS, Alchemy.FROSTMOSS)] = { effect = "fortify" },
[pairKey(Alchemy.FROSTMOSS, Alchemy.NIGHTCAP)] = { effect = "poison", power = 5 },
[pairKey(Alchemy.NIGHTCAP, Alchemy.NIGHTCAP)] = { effect = "poison", power = 7 },
-- Moonbell's own aspect is dream-fog, so concentrating it addles the
-- brewer's own mind rather than anyone else's - the same "own aspect
-- turned inward" shape as every other same-herb pair above. Sunlight is
-- the one aspect it outright opposes (wakeful day versus dreaming night,
-- the same shape as sunpetal/nightcap's light-versus-shade curdle above),
-- so that pairing curdles instead of brewing.
[pairKey(Alchemy.MOONBELL, Alchemy.MOONBELL)] = { effect = "confuse" },
[pairKey(Alchemy.SUNPETAL, Alchemy.MOONBELL)] = CURDLE,
[pairKey(Alchemy.BLOODROOT, Alchemy.MOONBELL)] = { effect = "clarity" },
[pairKey(Alchemy.EMBERLEAF, Alchemy.MOONBELL)] = { effect = "blind" },
[pairKey(Alchemy.FROSTMOSS, Alchemy.MOONBELL)] = { effect = "levitate" },
[pairKey(Alchemy.NIGHTCAP, Alchemy.MOONBELL)] = { effect = "poison", power = 6 },
}
function Alchemy.herb(key)
return { kind = Items.HERB, herb = key, name = Alchemy.names[key] .. " herb", glyph = ";" }
end
-- The fixed outcome for brewing herbA with herbB together, order
-- independent. Returns nil only when either key names no known herb.
function Alchemy.lookup(herbA, herbB)
if not Alchemy.names[herbA] or not Alchemy.names[herbB] then return nil end
return Alchemy.recipes[pairKey(herbA, herbB)]
end
-- Resolves a completed brew into a fresh potion item, or (nil, "curdle") for
-- an opposed pairing - the caller decides what a curdle does to the brewer.
function Alchemy.brew(herbA, herbB)
local recipe = Alchemy.lookup(herbA, herbB)
if not recipe then return nil, "unknown herb" end
if recipe.curdle then return nil, "curdle" end
return Items.potionOf(recipe.effect, recipe.power)
end
return Alchemy