/* Hurry Curry! - a game about cooking Copyright 2024 metamuffin This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, version 3 of the License only. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see . */ //? Is is a good idea? Probably not. export interface Recipe { tile?: string, inputs: (string | null)[], outputs: (string | null)[], action: "instant" | "passive" | "active" | "demand" | "demand" duration?: number revert_duration?: number, warn?: boolean, points?: number, } function trash_output(ifull: string) { const [i, ic] = get_container(ifull) if (i == "plate") return ifull if (i == "glass") return ifull if (i == "pot") return ifull if (i == "foodprocessor") return ifull if (i == "dirty") return ifull if (ic == "glass") return "glass" if (ic == "plate") return "dirty-plate" return null } export const all_items = new Set() export function auto_trash() { for (const ifull of all_items) { let [i, ic] = get_container(ifull) if (i == "plate") continue if (i == "glass") continue if (i == "pot") continue if (i == "foodprocessor") continue if (i == "dirty") continue if (ic == "glass") ic = "glass" if (ic == "plate") ic = "dirty-plate" out({ action: "instant", tile: ic == "glass" ? "sink" : "trash", inputs: [ifull], outputs: [ic] }) } } export function out(r: Recipe) { r.inputs.forEach(i => i ? all_items.add(i) : void 0) r.outputs.forEach(i => i ? all_items.add(i) : void 0) r.inputs = r.inputs.filter(e => e) r.outputs = r.outputs.filter(e => e) console.log(`- ${JSON.stringify(r).replaceAll("\"active\"", "!active").replaceAll("\"passive\"", "!passive").replaceAll("\"instant\"", "!instant")}`); } export function edible(item: string) { let i = item if (!item.endsWith("-plate") && !item.endsWith("-glass")) { i += "-plate" out({ action: "instant", inputs: [item], outputs: [i] }) } out({ action: "demand", inputs: [i], outputs: [trash_output(i)], duration: 10 }) } export function cut(from: string, to?: string, to2?: string) { out({ action: "active", duration: 2, tile: "cuttingboard", inputs: [from], outputs: [to ?? ("sliced-" + from), to2 ?? null] }) } export function cook(from: string, to?: string) { const i = from.endsWith("-pot") ? from : from + "-pot" const o = (to ?? ("cooked-" + from)) + "-pot" if (!from.endsWith("-pot")) out({ action: "instant", inputs: ["pot", from], outputs: [i] }) out({ action: "passive", duration: 20, revert_duration: 40, tile: "stove", inputs: [i], outputs: [o] }) out({ action: "passive", duration: 5, revert_duration: 10, tile: "stove", inputs: [o], outputs: ["burned-pot"], warn: true }) } export function process(from: string, to?: string) { const i = from.endsWith("-foodprocessor") ? from : from + "-foodprocessor" const o = (to ?? (from + "-juice")) + "-foodprocessor" if (!from.endsWith("-foodprocessor")) out({ action: "instant", inputs: ["foodprocessor", from], outputs: [i] }) out({ action: "passive", duration: 5, inputs: [i], outputs: [o] }) } export function bake(from: string, to?: string) { const o = (to ?? ("baked-" + from)) out({ action: "passive", duration: 25, tile: "oven", inputs: [from], outputs: [o] }) out({ action: "passive", duration: 15, revert_duration: 20, tile: "oven", inputs: [o], outputs: ["burned"], warn: true }) } export function crate(item: string) { out({ action: "instant", tile: item + "-crate", inputs: [], outputs: [item], points: -1 }) } export function get_container(ifull: string): [string, string | null] { const iparts = ifull.split("-") const ic = iparts.pop() if (ic && iparts.length && ["pot", "plate", "foodprocessor", "glass"].includes(ic)) return [iparts.join("-"), ic] return [ifull, null] } export function combine(container: string, ...inputs: string[]) { const open = inputs.map(ifull => { const [i, ic] = get_container(ifull) out({ action: "instant", inputs: [container, ifull], outputs: [i + "-" + container, ic] }) return [i] }) const seen = new Set() while (1) { const cur = open.pop() if (!cur) break; const c = cur.join("-") + "-" + container for (const ifull of inputs) { const [i, ic] = get_container(ifull) if (cur.includes(i)) continue const rkey = cur.join("-") + "#" + i if (seen.has(rkey)) continue seen.add(rkey) const parts = [...cur, i] parts.sort() const o = parts.join("-") + "-" + container open.push(parts) out({ action: "instant", inputs: [c, ifull], outputs: [o, ic] }) } } } if (import.meta.main) { out({ action: "active", duration: 2, tile: "sink", inputs: ["dirty-plate"], outputs: ["plate"] }) crate("tomato") crate("raw-steak") crate("flour") crate("leek") cut("tomato") cook("raw-steak", "steak") // bread process("flour", "dough") out({ action: "instant", inputs: ["dough-foodprocessor"], outputs: ["foodprocessor", "dough"] }) bake("dough", "bread") cut("bread", "bread-slice", "bread-slice") // tomato soup process("tomato") combine("pot", "leek", "tomato-juice-foodprocessor") cook("leek-tomato-juice-pot", "tomato-soup") out({ action: "instant", inputs: ["tomato-soup-pot", "plate"], outputs: ["pot", "tomato-soup-plate"] }) combine("plate", "steak-pot", "sliced-tomato", "bread-slice") edible("steak-plate") edible("bread-slice-steak-plate") edible("bread-slice-sliced-tomato-plate") edible("bread-slice-sliced-tomato-steak-plate") out({ action: "demand", inputs: ["bread"], outputs: [], duration: 0 }) crate("rice") crate("fish") crate("coconut") crate("strawberry") // Rice and nigiri cut("fish") cook("rice") out({ action: "instant", inputs: ["sliced-fish", "cooked-rice-pot"], outputs: ["nigiri", "pot"] }) out({ action: "instant", inputs: ["plate", "cooked-rice-pot"], outputs: ["cooked-rice-plate", "pot"] }) edible("cooked-rice-plate") edible("nigiri") // coconut milk and strawberry puree process("strawberry", "strawberry-puree") process("coconut", "milk") out({ action: "instant", inputs: ["milk-foodprocessor", "strawberry"], outputs: ["strawberry-milk-foodprocessor"] }) out({ action: "instant", inputs: ["strawberry-puree-foodprocessor", "coconut"], outputs: ["strawberry-milk-foodprocessor"] }) process("strawberry-milk-foodprocessor", "strawberrymilk") process("coconut-strawberry-puree-foodprocessor", "strawberrymilk") // icecream out({ action: "passive", inputs: ["strawberrymilk-foodprocessor"], outputs: ["strawberry-icecream-foodprocessor"], tile: "freezer", duration: 20 }) out({ action: "instant", inputs: ["strawberry-icecream-foodprocessor", "plate"], outputs: ["foodprocessor", "strawberry-icecream-plate"] }) edible("strawberry-icecream-plate") // drinks out({ action: "instant", inputs: ["glass"], outputs: ["water-glass"], tile: "sink" }) out({ action: "instant", inputs: ["glass", "milk-foodprocessor"], outputs: ["milk-glass", "foodprocessor"] }) out({ action: "instant", inputs: ["glass", "strawberrymilk-foodprocessor"], outputs: ["strawberrymilk-glass", "foodprocessor"] }) edible("water-glass") edible("strawberrymilk-glass") auto_trash() }