diff --git a/README.md b/README.md index 1f831dd..0a3bec4 100644 --- a/README.md +++ b/README.md @@ -22,3 +22,18 @@ terminal-tiller This will create a new save file in `~/.terminal-tiller` See the on-screen usage instructions and start planting! When you close the game, the state is saved and your crops will continue growing! + +## Conveniently Get Status Notifications + +The `terminal-tiller -status` command will output a convenient summary for your farm without having to fully open it: + +``` +-------------------------------------------------------------- +9 plants can be harvested, earning 135g +27 plants are still growing. Come back in 1m55s to harvest +13 plots are readed to be sowed +You currently have 729g +-------------------------------------------------------------- +``` + +Add this command to your `~/.zshrc`, `~/.bashrc`, or `~/.config/fish/config.fish` to conveniently and non-disruptively receive the status message when you open a new terminal session. diff --git a/farm/farm.go b/farm/farm.go index 7b6247e..b3d2a7d 100644 --- a/farm/farm.go +++ b/farm/farm.go @@ -4,6 +4,7 @@ import ( "encoding/json" "errors" "fmt" + "strings" "time" ) @@ -124,3 +125,53 @@ func (f *Farm) Harvest(row, col int) error { return nil } + +// Status returns a string describing the farm's status +func (f *Farm) Status() string { + var numHarvestable, harvestableMoney int + var numGrowing int + var empty int + var nextReady time.Time + + for row := 0; row < f.h; row++ { + for col := 0; col < f.w; col++ { + cur := f.Get(row, col) + + switch { + case cur == nil: + empty++ + case cur.ReadyToHarvest(f.TimeScale()): + numHarvestable++ + harvestableMoney += cur.Type.MarketPrice() + default: + numGrowing++ + harvestTime := cur.HarvestTime(f.timeScale) + if harvestTime.Before(nextReady) || nextReady.IsZero() { + nextReady = harvestTime + } + } + } + } + + sb := &strings.Builder{} + sb.WriteString("--------------------------------------------------------------\n") + sb.WriteString(fmt.Sprintf("%d plants can be harvested", numHarvestable)) + if numHarvestable > 0 { + sb.WriteString(fmt.Sprintf(", earning %dg", harvestableMoney)) + } + sb.WriteString("\n") + + if numGrowing > 0 { + sb.WriteString(fmt.Sprintf("%d plants are still growing. ", numGrowing)) + sb.WriteString(fmt.Sprintf("Come back in %s to harvest\n", time.Until(nextReady).Truncate(time.Second).String())) + } + + if empty > 0 { + sb.WriteString(fmt.Sprintf("%d plots are readed to be sowed\n", empty)) + } + + sb.WriteString(fmt.Sprintf("You currently have %dg\n", f.Money())) + sb.WriteString("--------------------------------------------------------------\n") + + return sb.String() +} diff --git a/game/game.go b/game/game.go index 639f745..c305b3e 100644 --- a/game/game.go +++ b/game/game.go @@ -46,7 +46,7 @@ type game struct { filename string } -func New(filename, farmName string) (tea.Model, error) { +func New(filename, farmName string) (tea.Model, *farm.Farm, error) { if filename == "" { saveFiles, err := findSaveFiles() if err != nil { @@ -60,25 +60,25 @@ func New(filename, farmName string) (tea.Model, error) { dir, err := terminalTillerDir() if err != nil { - return nil, fmt.Errorf("error determining save file directory: %w", err) + return nil, nil, fmt.Errorf("error determining save file directory: %w", err) } var f *farm.Farm switch { case filename == "" && farmName != "": - return nil, fmt.Errorf("cannot create new farm without filename") + return nil, nil, fmt.Errorf("cannot create new farm without filename") case filename == "" && farmName == "": // create new farm with default name filename = filepath.Join(dir, DEFAULT_FILENAME) f = farm.New("My Farm", DEFAULT_WIDTH, DEFAULT_HEIGHT, DEFAULT_SCALE) case filename != "" && farmName == "": // load existing data, err := os.ReadFile(filename) if err != nil { - return nil, fmt.Errorf("error opening file: %w", err) + return nil, nil, fmt.Errorf("error opening file: %w", err) } f, err = farm.Load(data) if err != nil { - return nil, fmt.Errorf("error loading from save file: %w", err) + return nil, nil, fmt.Errorf("error loading from save file: %w", err) } case filename != "" && farmName != "": // new with name f = farm.New(farmName, DEFAULT_WIDTH, DEFAULT_HEIGHT, DEFAULT_SCALE) @@ -91,7 +91,7 @@ func New(filename, farmName string) (tea.Model, error) { seedSelect: newSeedSelectView(f.TimeScale()), selectedCropType: farm.Lettuce, filename: filename, - }, nil + }, f, nil } func (g *game) Init() tea.Cmd { diff --git a/main.go b/main.go index ea81085..af8ef03 100644 --- a/main.go +++ b/main.go @@ -2,6 +2,7 @@ package main import ( "flag" + "fmt" "github.com/calvinmclean/terminal-tiller/game" @@ -11,13 +12,19 @@ import ( func main() { filename := flag.String("file", "", "save file to load from") farmName := flag.String("name", "", "use this flag to create new farm with the provided name. filename is required when creating a new farm") + status := flag.Bool("status", false, "print out the game status") flag.Parse() - g, err := game.New(*filename, *farmName) + g, f, err := game.New(*filename, *farmName) if err != nil { panic(err) } + if *status { + fmt.Println(f.Status()) + return + } + p := tea.NewProgram(g, tea.WithAltScreen()) _, err = p.Run()