aboutsummaryrefslogtreecommitdiff
path: root/internal/game/cmd_cook.go
diff options
context:
space:
mode:
authorhistoria <[not public]>2026-06-16 04:17:43 -0400
committerhistoria <[not public]>2026-06-16 04:17:43 -0400
commit085e728d22a3369bd110b77409914cfbe9ebe611 (patch)
treeeeb88cb1ceb91cc9ea2b5a1877c3c66328fab503 /internal/game/cmd_cook.go
parent43f21aabae8924f35c12c8485e690aeefe0089fb (diff)
downloadthehouseoficarus-085e728d22a3369bd110b77409914cfbe9ebe611.tar.gz
feat: recipe system, combining items, cooking skill
Diffstat (limited to 'internal/game/cmd_cook.go')
-rw-r--r--internal/game/cmd_cook.go246
1 files changed, 246 insertions, 0 deletions
diff --git a/internal/game/cmd_cook.go b/internal/game/cmd_cook.go
new file mode 100644
index 0000000..be5fa3a
--- /dev/null
+++ b/internal/game/cmd_cook.go
@@ -0,0 +1,246 @@
+package game
+
+import (
+ "fmt"
+ "strconv"
+ "strings"
+
+ "thirdcollapse/internal/action"
+ "thirdcollapse/internal/net"
+ "thirdcollapse/internal/player"
+)
+
+type cookableEntry struct {
+ ItemName string
+ Recipe action.RecipeDef
+}
+
+func (g *Game) doCook(sess *net.Session, input string) {
+ p := sess.Player.(*player.Player)
+ g.CancelAction(p)
+
+ stationName := g.findCookingStation(p.RoomID)
+ if stationName == "" {
+ sess.WriteLine("You need a fire or cooking range to cook.")
+ return
+ }
+
+ allRecipes, err := g.RecipeStore.LoadAll()
+ if err != nil {
+ sess.WriteLine("Error loading recipes.")
+ return
+ }
+
+ if input == "" {
+ g.showCookMenu(sess, p, allRecipes, stationName)
+ return
+ }
+
+ qty, itemName := parseQty(input)
+ _ = qty
+
+ matches := g.findInventoryMatches(itemName, p)
+ if len(matches) == 0 {
+ sess.WriteLine(fmt.Sprintf("You don't have any '%s'.", itemName))
+ return
+ }
+
+ unique := uniqueItemNames(matches)
+ if len(unique) > 1 {
+ sess.WriteLine("Which one?")
+ for _, name := range unique {
+ sess.WriteLine(fmt.Sprintf(" - %s", name))
+ }
+ return
+ }
+
+ itemID := matches[0].ID
+
+ var recipes []action.RecipeDef
+ for _, r := range allRecipes {
+ if r.Type != "cooking" {
+ continue
+ }
+ if stationOK(stationName, r.Station) && r.MatchesEntry(itemID) && r.HasAllItems(p.HasItem) {
+ recipes = append(recipes, r)
+ }
+ }
+
+ if len(recipes) == 0 {
+ sess.WriteLine("You can't cook that here.")
+ return
+ }
+
+ if len(recipes) == 1 {
+ g.startCook(sess, p, &recipes[0], stationName)
+ return
+ }
+
+ sess.PendingRecipeItem = itemID
+ sess.PendingCookMenu = recipesToMenuData(recipes)
+ sess.State = net.StateCookRecipe
+ sess.WriteLine(fmt.Sprintf("\nWhat would you like to cook on the %s?", stationName))
+ for i, r := range recipes {
+ sess.WriteLine(fmt.Sprintf(" %d) %s", i+1, g.recipeName(sess, &r)))
+ }
+ sess.WriteLine(fmt.Sprintf(" %d) Nothing", len(recipes)+1))
+}
+
+func (g *Game) showCookMenu(sess *net.Session, p *player.Player, allRecipes []action.RecipeDef, stationName string) {
+ seen := make(map[string]bool)
+ var cookables []cookableEntry
+
+ for _, r := range allRecipes {
+ if r.Type != "cooking" {
+ continue
+ }
+ if !stationOK(stationName, r.Station) {
+ continue
+ }
+ if seen[r.ID] {
+ continue
+ }
+ if !r.HasAllItems(p.HasItem) {
+ continue
+ }
+ seen[r.ID] = true
+ cookables = append(cookables, cookableEntry{g.recipeName(sess, &r), r})
+ }
+
+ if len(cookables) == 0 {
+ sess.WriteLine("You don't have anything you can cook.")
+ return
+ }
+
+ if len(cookables) == 1 {
+ g.startCook(sess, p, &cookables[0].Recipe, stationName)
+ return
+ }
+
+ sess.PendingRecipeItem = ""
+ sess.State = net.StateCookRecipe
+ sess.WriteLine(fmt.Sprintf("\nWhat would you like to cook on the %s?", stationName))
+ for i, c := range cookables {
+ sess.WriteLine(fmt.Sprintf(" %d) %s", i+1, c.ItemName))
+ }
+ sess.WriteLine(fmt.Sprintf(" %d) Nothing", len(cookables)+1))
+
+ sess.PendingCookMenu = makeCookMenuData(cookables)
+}
+
+func (g *Game) recipeName(sess *net.Session, r *action.RecipeDef) string {
+ if r.Output != "" {
+ if def, err := g.ItemStore.Load(r.Output); err == nil {
+ return g.itemColorize(sess, def, def.Name)
+ }
+ }
+ return r.DisplayName()
+}
+
+func makeCookMenuData(cookables []cookableEntry) []map[string]string {
+ out := make([]map[string]string, len(cookables))
+ for i, c := range cookables {
+ out[i] = map[string]string{"recipe_id": c.Recipe.ID}
+ }
+ return out
+}
+
+func recipesToMenuData(recipes []action.RecipeDef) []map[string]string {
+ out := make([]map[string]string, len(recipes))
+ for i, r := range recipes {
+ out[i] = map[string]string{"recipe_id": r.ID}
+ }
+ return out
+}
+
+func stationOK(stationName string, recipeStations []string) bool {
+ if len(recipeStations) == 0 {
+ return false
+ }
+ for _, rs := range recipeStations {
+ if (rs == "fire" && stationName == "fire") || (rs == "cooking_range" && stationName == "cooking range") {
+ return true
+ }
+ }
+ return false
+}
+
+func (g *Game) findCookingStation(roomID int) string {
+ for _, obj := range g.World.AllObjInstances(roomID) {
+ if obj.Depleted {
+ continue
+ }
+ if obj.DefID == "fire" {
+ return "fire"
+ }
+ if obj.DefID == "cooking_range" {
+ return "cooking range"
+ }
+ }
+ return ""
+}
+
+func (g *Game) handleCookRecipe(sess *net.Session, input string) {
+ p := sess.Player.(*player.Player)
+ input = strings.TrimSpace(input)
+ if input == "" {
+ return
+ }
+
+ choice, err := strconv.Atoi(input)
+ if err != nil {
+ sess.State = net.StateGame
+ sess.PendingRecipeItem = ""
+ sess.PendingCookMenu = nil
+ sess.WriteLine("You decide not to cook anything.")
+ g.reprompt(sess)
+ return
+ }
+
+ if len(sess.PendingCookMenu) > 0 {
+ menuData := sess.PendingCookMenu
+ sess.PendingCookMenu = nil
+ if choice <= 0 || choice > len(menuData)+1 {
+ sess.WriteLine("Invalid choice.")
+ return
+ }
+ sess.State = net.StateGame
+ if choice == len(menuData)+1 {
+ sess.WriteLine("You decide not to cook anything.")
+ g.reprompt(sess)
+ return
+ }
+ entry := menuData[choice-1]
+
+ if cid, ok := entry["combine_item"]; ok {
+ def, err := g.ItemStore.Load(cid)
+ if err == nil {
+ p := sess.Player.(*player.Player)
+ g.produceCombined(sess, p, def)
+ }
+ g.reprompt(sess)
+ return
+ }
+
+ all, err := g.RecipeStore.LoadAll()
+ if err != nil {
+ g.reprompt(sess)
+ return
+ }
+ for _, r := range all {
+ if r.ID == entry["recipe_id"] {
+ stationName := "inventory"
+ if len(r.Station) > 0 {
+ stationName = g.findCookingStation(p.RoomID)
+ }
+ g.startCook(sess, p, &r, stationName)
+ return
+ }
+ }
+ g.reprompt(sess)
+ return
+ }
+
+ sess.State = net.StateGame
+ g.reprompt(sess)
+}