diff options
| author | workhorse <workhorse@localhost.localdomain> | 2026-06-19 11:51:17 -0400 |
|---|---|---|
| committer | workhorse <workhorse@localhost.localdomain> | 2026-06-19 11:51:17 -0400 |
| commit | 575a71dcfed3b9fa44af244836f638a23df05a9a (patch) | |
| tree | eb5eff16eab86247a3dd93ff4d7fd3781f291b64 /internal/game/cmd_farm.go | |
| parent | 52a3ce6a4b4a254dc5d3067979a09e93c060fa20 (diff) | |
| download | thehouseoficarus-575a71dcfed3b9fa44af244836f638a23df05a9a.tar.gz | |
feat: farming roughly implemented
Diffstat (limited to 'internal/game/cmd_farm.go')
| -rw-r--r-- | internal/game/cmd_farm.go | 523 |
1 files changed, 523 insertions, 0 deletions
diff --git a/internal/game/cmd_farm.go b/internal/game/cmd_farm.go new file mode 100644 index 0000000..0a374f0 --- /dev/null +++ b/internal/game/cmd_farm.go @@ -0,0 +1,523 @@ +package game + +import ( + "fmt" + "strings" + + "thehouseoficarus/internal/action" + "thehouseoficarus/internal/net" + "thehouseoficarus/internal/player" +) + +func (g *Game) doPlant(sess *net.Session, input string) { + p := sess.Player.(*player.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 = &action.Action{ + Type: "plant", + TargetID: seedID, + TargetName: seedDef.Name, + WaitLeft: 2, + Data: map[string]any{ + "seed_id": seedID, + "prefix": prefix, + "xp": float64(seedDef.FarmPlantXP), + }, + } + p.ActionState = &ActionState{Type: ActionPlanting, TargetName: seedDef.Name} + + g.AccountStore.SaveCharacter(p) +} + +func (g *Game) doHarvest(sess *net.Session, input string) { + p := sess.Player.(*player.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 = &action.Action{ + Type: "harvest", + TargetID: prefix, + TargetName: productName, + WaitLeft: 3, + Data: map[string]any{ + "prefix": prefix, + "seed_id": seedID, + "product": product, + "min_yield": float64(minYield), + "max_yield": float64(maxYield), + "xp": float64(harvestXP), + }, + } + p.ActionState = &ActionState{Type: ActionHarvesting, TargetName: productName} + + g.AccountStore.SaveCharacter(p) +} + +func (g *Game) doRake(sess *net.Session, input string) { + p := sess.Player.(*player.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 = &action.Action{ + Type: "rake", + TargetID: prefix, + TargetName: patchName, + WaitLeft: 4, + Data: map[string]any{ + "prefix": prefix, + }, + } + p.ActionState = &ActionState{Type: ActionRaking, TargetName: patchName} + + g.AccountStore.SaveCharacter(p) +} + +func (g *Game) doWater(sess *net.Session, input string) { + p := sess.Player.(*player.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 = &action.Action{ + Type: "water", + TargetID: prefix, + TargetName: patchName, + WaitLeft: 2, + Data: map[string]any{ + "prefix": prefix, + }, + } + p.ActionState = &ActionState{Type: ActionWatering, TargetName: patchName} + + g.AccountStore.SaveCharacter(p) +} + +func (g *Game) doCure(sess *net.Session, input string) { + p := sess.Player.(*player.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 = &action.Action{ + Type: "cure", + TargetID: prefix, + TargetName: patchName, + WaitLeft: 2, + Data: map[string]any{ + "prefix": prefix, + }, + } + p.ActionState = &ActionState{Type: ActionCuring, TargetName: patchName} + + g.AccountStore.SaveCharacter(p) +} + +func (g *Game) doInspect(sess *net.Session, input string) { + p := sess.Player.(*player.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("\n=== %s%s ===", patchName, indexStr)) + + weeds, _ := p.Flags[fp.prefix+"_weeds"].(bool) + if weeds { + sess.WriteLine(" Status: Weeds") + sess.WriteLine(" (Rake to clear before planting)") + continue + } + + seedID, _ := p.Flags[fp.prefix+"_seed"].(string) + if seedID == "" { + sess.WriteLine(" Status: Empty") + sess.WriteLine(" (Ready to plant)") + continue + } + + seedName := seedID + if def, err := g.ItemStore.Load(seedID); err == nil { + seedName = def.Name + } + + dead, _ := p.Flags[fp.prefix+"_dead"].(bool) + if dead { + sess.WriteLine(fmt.Sprintf(" Status: Dead")) + sess.WriteLine(fmt.Sprintf(" Planted: %s", seedName)) + sess.WriteLine(" (Rake to clear)") + continue + } + + diseased, _ := p.Flags[fp.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)") + continue + } + + ready, _ := p.Flags[fp.prefix+"_ready"].(bool) + if ready { + sess.WriteLine(fmt.Sprintf(" Status: Ready to harvest!")) + sess.WriteLine(fmt.Sprintf(" Planted: %s", seedName)) + continue + } + + stage := intFlag(p.Flags, fp.prefix+"_stage") + maxStages := 4 + if def, err := g.ItemStore.Load(seedID); err == nil && def.FarmStages > 0 { + maxStages = def.FarmStages + } + watered, _ := p.Flags[fp.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 +} |
