package game import ( "fmt" "strings" "thehouseoficarus/internal/behavior" "thehouseoficarus/internal/net" "thehouseoficarus/internal/player" ) func (g *Game) executePlant(sess *net.Session, args []string, rawInput string) { p := sess.Player g.cancelAction(p) if len(args) == 0 { sess.WriteLine("Plant what?") } else { g.doPlant(sess, strings.Join(args, " ")) } } func (g *Game) executeHarvest(sess *net.Session, args []string, rawInput string) { p := sess.Player g.cancelAction(p) if len(args) == 0 { g.doHarvest(sess, "") } else { g.doHarvest(sess, strings.Join(args, " ")) } } func (g *Game) executeRake(sess *net.Session, args []string, rawInput string) { p := sess.Player g.cancelAction(p) if len(args) == 0 { g.doRake(sess, "") } else { g.doRake(sess, strings.Join(args, " ")) } } func (g *Game) executeWater(sess *net.Session, args []string, rawInput string) { p := sess.Player g.cancelAction(p) if len(args) == 0 { g.doWater(sess, "") } else { g.doWater(sess, strings.Join(args, " ")) } } func (g *Game) executeCure(sess *net.Session, args []string, rawInput string) { p := sess.Player g.cancelAction(p) if len(args) == 0 { g.doCure(sess, "") } else { g.doCure(sess, strings.Join(args, " ")) } } func (g *Game) doPlant(sess *net.Session, input string) { p := sess.Player if input == "" { sess.WriteLine("Plant what?") return } matches := g.findInventoryMatches(input, p) if len(matches) == 0 { sess.WriteLine(fmt.Sprintf("You don't have any '%s'.", input)) return } unique := uniqueItemNames(matches) if len(unique) > 1 { g.showWhichOne(sess, matches) return } seedID := matches[0].ID seedDef, err := g.ItemStore.Load(seedID) if err != nil || seedDef.FarmPatchType == "" { sess.WriteLine("You can't plant that.") return } farmLevel := p.Level(player.Farming) if seedDef.FarmLevel > 0 && farmLevel < seedDef.FarmLevel { sess.WriteLine(fmt.Sprintf("You need level %d farming to plant that. Your farming level is %d.", seedDef.FarmLevel, farmLevel)) return } if !g.hasToolType(p, "spade") { sess.WriteLine("You need a spade to plant seeds.") return } patchDefID := seedDef.FarmPatchType + "_patch" objInstances := g.World.FindObjInstances(p.RoomID, patchDefID) if len(objInstances) == 0 { sess.WriteLine("There's no suitable patch here to plant that.") return } var prefix string for _, obj := range objInstances { pref := farmFlagPrefix(patchDefID, obj.Index) if pref == "" { continue } g.ensureFarmState(p, pref) weeds, _ := p.Flags[pref+"_weeds"].(bool) seedPlanted, _ := p.Flags[pref+"_seed"].(string) if !weeds && seedPlanted == "" { prefix = pref break } } if prefix == "" { sess.WriteLine("All patches here have something in them. Rake them first.") return } p.RemoveItem(seedID, 1) p.Action = &behavior.Action{ Type: "plant", TargetID: seedID, TargetName: seedDef.Name, WaitLeft: 2, Data: &behavior.FarmData{ SeedID: seedID, Prefix: prefix, XP: float64(seedDef.FarmPlantXP), }, } g.AccountStore.SaveCharacter(p) } func (g *Game) doHarvest(sess *net.Session, input string) { p := sess.Player prefix, _, _ := g.findFarmPatch(sess, p, input, func(flags map[string]any, prefix string) bool { ready, _ := flags[prefix+"_ready"].(bool) return ready }) if prefix == "" { if input != "" { sess.WriteLine("That patch isn't ready to harvest.") } else { sess.WriteLine("There's nothing ready to harvest here.") } return } g.ensureFarmState(p, prefix) ready, _ := p.Flags[prefix+"_ready"].(bool) if !ready { sess.WriteLine("There's nothing ready to harvest here.") return } if !g.hasToolType(p, "spade") { sess.WriteLine("You need a spade to harvest.") return } seedID, _ := p.Flags[prefix+"_seed"].(string) if seedID == "" { sess.WriteLine("There's nothing planted here.") return } seedDef, err := g.ItemStore.Load(seedID) if err != nil { sess.WriteLine("Error loading seed data.") return } product := seedDef.FarmProduct if product == "" { sess.WriteLine("This crop has no harvest product defined.") return } productDef, _ := g.ItemStore.Load(product) productName := product if productDef != nil { productName = productDef.Name } if p.FreeSlots() <= 0 && (productDef == nil || !productDef.Stackable || p.CountItem(product) == 0) { sess.WriteLine("Your inventory is full.") return } minYield := seedDef.FarmMinYield maxYield := seedDef.FarmMaxYield if minYield <= 0 { minYield = 3 } if maxYield <= 0 { maxYield = 10 } harvestXP := seedDef.FarmHarvestXP p.Action = &behavior.Action{ Type: "harvest", TargetID: prefix, TargetName: productName, WaitLeft: 3, Data: &behavior.FarmData{ Prefix: prefix, SeedID: seedID, Product: product, MinYield: float64(minYield), MaxYield: float64(maxYield), XP: float64(harvestXP), }, } g.AccountStore.SaveCharacter(p) } func (g *Game) doRake(sess *net.Session, input string) { p := sess.Player if !g.hasToolType(p, "rake") { sess.WriteLine("You need a rake to do that.") return } prefix, _, _ := g.findFarmPatch(sess, p, input, func(flags map[string]any, prefix string) bool { weeds, _ := flags[prefix+"_weeds"].(bool) dead, _ := flags[prefix+"_dead"].(bool) return weeds || dead }) if prefix == "" { if input != "" { sess.WriteLine("That patch doesn't need raking.") } else { sess.WriteLine("There's nothing here that needs raking.") } return } g.ensureFarmState(p, prefix) weeds, _ := p.Flags[prefix+"_weeds"].(bool) dead, _ := p.Flags[prefix+"_dead"].(bool) if !weeds && !dead { sess.WriteLine("The patch doesn't need raking.") return } patchName := patchDisplayName(prefix) p.Action = &behavior.Action{ Type: "rake", TargetID: prefix, TargetName: patchName, WaitLeft: 4, Data: &behavior.FarmData{ Prefix: prefix, }, } g.AccountStore.SaveCharacter(p) } func (g *Game) doWater(sess *net.Session, input string) { p := sess.Player if !g.hasToolType(p, "watering_can") { sess.WriteLine("You need a watering can to do that.") return } prefix, _, _ := g.findFarmPatch(sess, p, input, func(flags map[string]any, prefix string) bool { seed, _ := flags[prefix+"_seed"].(string) if seed == "" { return false } ready, _ := flags[prefix+"_ready"].(bool) dead, _ := flags[prefix+"_dead"].(bool) watered, _ := flags[prefix+"_watered"].(bool) return !dead && !ready && !watered }) if prefix == "" { if input != "" { sess.WriteLine("That patch doesn't need watering.") } else { sess.WriteLine("There's nothing here that needs watering.") } return } g.ensureFarmState(p, prefix) seedID, _ := p.Flags[prefix+"_seed"].(string) if seedID == "" { sess.WriteLine("There's nothing planted here to water.") return } ready, _ := p.Flags[prefix+"_ready"].(bool) if ready { sess.WriteLine("The crop is already fully grown.") return } watered, _ := p.Flags[prefix+"_watered"].(bool) if watered { sess.WriteLine("The patch is already watered.") return } patchName := patchDisplayName(prefix) p.Action = &behavior.Action{ Type: "water", TargetID: prefix, TargetName: patchName, WaitLeft: 2, Data: &behavior.FarmData{ Prefix: prefix, }, } g.AccountStore.SaveCharacter(p) } func (g *Game) doCure(sess *net.Session, input string) { p := sess.Player if !p.HasItem("plant_cure") { sess.WriteLine("You don't have any plant cure.") return } prefix, _, _ := g.findFarmPatch(sess, p, input, func(flags map[string]any, prefix string) bool { diseased, _ := flags[prefix+"_diseased"].(bool) return diseased }) if prefix == "" { if input != "" { sess.WriteLine("That patch isn't diseased.") } else { sess.WriteLine("There's nothing here that needs curing.") } return } g.ensureFarmState(p, prefix) diseased, _ := p.Flags[prefix+"_diseased"].(bool) if !diseased { sess.WriteLine("The patch isn't diseased.") return } patchName := patchDisplayName(prefix) p.Action = &behavior.Action{ Type: "cure", TargetID: prefix, TargetName: patchName, WaitLeft: 2, Data: &behavior.FarmData{ Prefix: prefix, }, } g.AccountStore.SaveCharacter(p) } func (g *Game) doInspect(sess *net.Session, input string) { p := sess.Player objInstances := g.World.AllObjInstances(p.RoomID) var farmPatches []struct { prefix string defID string index int } for _, obj := range objInstances { if !farmPatchDefIDs[obj.DefID] { continue } pref := farmFlagPrefix(obj.DefID, obj.Index) if pref == "" { continue } farmPatches = append(farmPatches, struct { prefix string defID string index int }{pref, obj.DefID, obj.Index}) } if len(farmPatches) == 0 { sess.WriteLine("There are no farming patches here.") return } if input != "" { lower := strings.ToLower(input) var filtered []struct { prefix string defID string index int } for _, fp := range farmPatches { def, _ := g.ObjectStore.Load(fp.defID) if def != nil && strings.Contains(strings.ToLower(def.Name), lower) { filtered = append(filtered, fp) } } if len(filtered) == 0 { for _, fp := range farmPatches { if strings.Contains(fp.prefix, lower) || strings.Contains(strings.ToLower(fp.defID), lower) { filtered = append(filtered, fp) } } } farmPatches = filtered } for _, fp := range farmPatches { g.ensureFarmState(p, fp.prefix) def, _ := g.ObjectStore.Load(fp.defID) patchName := fp.defID if def != nil { patchName = def.Name } indexStr := "" if len(farmPatches) > 1 || fp.index > 0 { indexStr = fmt.Sprintf(" %d", fp.index+1) } sess.WriteLine(fmt.Sprintf("=== %s%s ===", patchName, indexStr)) g.ShowFarmPatchDetail(sess, p, fp.prefix, fp.defID, fp.index) } } func (g *Game) ShowFarmPatchDetail(sess *net.Session, p *player.Player, prefix, defID string, index int) { weeds, _ := p.Flags[prefix+"_weeds"].(bool) if weeds { sess.WriteLine(" Status: Weeds") sess.WriteLine(" (Rake to clear before planting)") return } seedID, _ := p.Flags[prefix+"_seed"].(string) if seedID == "" { sess.WriteLine(" Status: Empty") sess.WriteLine(" (Ready to plant)") return } seedName := seedID if def, err := g.ItemStore.Load(seedID); err == nil { seedName = def.Name } dead, _ := p.Flags[prefix+"_dead"].(bool) if dead { sess.WriteLine(fmt.Sprintf(" Status: Dead")) sess.WriteLine(fmt.Sprintf(" Planted: %s", seedName)) sess.WriteLine(" (Rake to clear)") return } diseased, _ := p.Flags[prefix+"_diseased"].(bool) if diseased { sess.WriteLine(fmt.Sprintf(" Status: Diseased!")) sess.WriteLine(fmt.Sprintf(" Planted: %s", seedName)) sess.WriteLine(" (Use plant cure to save it)") return } ready, _ := p.Flags[prefix+"_ready"].(bool) if ready { sess.WriteLine(fmt.Sprintf(" Status: Ready to harvest!")) sess.WriteLine(fmt.Sprintf(" Planted: %s", seedName)) return } stage := intFromFlag(p.Flags, prefix+"_stage") maxStages := 4 if def, err := g.ItemStore.Load(seedID); err == nil && def.FarmStages > 0 { maxStages = def.FarmStages } watered, _ := p.Flags[prefix+"_watered"].(bool) sess.WriteLine(fmt.Sprintf(" Status: Growing (stage %d/%d)", stage, maxStages)) sess.WriteLine(fmt.Sprintf(" Planted: %s", seedName)) sess.WriteLine(fmt.Sprintf(" Watered: %s", boolToYes(watered))) sess.WriteLine(fmt.Sprintf(" Diseased: %s", boolToYes(diseased))) } func boolToYes(b bool) string { if b { return "Yes" } return "No" } func (g *Game) findFarmPatch(sess *net.Session, p *player.Player, input string, matcher func(map[string]any, string) bool) (prefix string, defID string, index int) { objInstances := g.World.AllObjInstances(p.RoomID) if len(objInstances) == 0 { return "", "", -1 } type patchInfo struct { prefix string defID string index int name string } var patches []patchInfo for _, obj := range objInstances { if !farmPatchDefIDs[obj.DefID] { continue } g.ensureFarmState(p, farmFlagPrefix(obj.DefID, obj.Index)) } for _, obj := range objInstances { if !farmPatchDefIDs[obj.DefID] { continue } pref := farmFlagPrefix(obj.DefID, obj.Index) if pref == "" { continue } if !matcher(p.Flags, pref) { continue } var name string if def, err := g.ObjectStore.Load(obj.DefID); err == nil { name = def.Name } else { name = obj.DefID } patches = append(patches, patchInfo{prefix: pref, defID: obj.DefID, index: obj.Index, name: name}) } if len(patches) == 0 { return "", "", -1 } if input == "" { return patches[0].prefix, patches[0].defID, patches[0].index } lower := strings.ToLower(input) for _, pi := range patches { if strings.Contains(lower, strings.ToLower(pi.name)) { return pi.prefix, pi.defID, pi.index } } for _, pi := range patches { if strings.Contains(lower, pi.prefix) { return pi.prefix, pi.defID, pi.index } } return "", "", -1 }