From a3e028e9163488c08ea5a31595cb083e4f83082c Mon Sep 17 00:00:00 2001 From: Steve Date: Tue, 24 May 2022 18:26:34 -0400 Subject: [PATCH] add techs, research, player log --- craft.go | 67 +++++++++++++++++++++++++++++++++++++++++ mainscreen.go | 18 +++++++++-- simulator/item_table.go | 15 ++++++++- simulator/player.go | 35 ++++++++++++++++++++- simulator/science.go | 8 +++++ simulator/simulator.go | 18 ++++++++++- 6 files changed, 156 insertions(+), 5 deletions(-) create mode 100644 craft.go create mode 100644 simulator/science.go diff --git a/craft.go b/craft.go new file mode 100644 index 0000000..6e70791 --- /dev/null +++ b/craft.go @@ -0,0 +1,67 @@ +package main + +import ( + sim "git.saintnet.tech/stryan/spacetea/simulator" + "github.com/charmbracelet/bubbles/list" + tea "github.com/charmbracelet/bubbletea" +) + +type craftModel struct { + list list.Model +} + +type craftMsg string + +func (c craftModel) buildCraftMsg() tea.Msg { + i := c.list.SelectedItem().(item) + return craftMsg(i.ID()) +} + +func newCraftModel(entries []sim.ItemEntry, m tea.Model) craftModel { + var c craftModel + items := []list.Item{} + for _, v := range entries { + items = append(items, item{v.Name(), "no description", v.ID()}) + } + //w,h + c.list = list.New(items, list.NewDefaultDelegate(), 32, 32) + c.list.Title = "What do you want to craft?" + c.list.DisableQuitKeybindings() + return c +} + +// Init is the first function that will be called. It returns an optional +// initial command. To not perform an initial command return nil. +func (c craftModel) Init() tea.Cmd { + return tea.EnterAltScreen +} + +// Update is called when a message is received. Use it to inspect messages +// and, in response, update the model and/or send a command. +func (c craftModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) { + switch msg := msg.(type) { + case tea.WindowSizeMsg: + h, v := docStyle.GetFrameSize() + c.list.SetSize(msg.Width-h, msg.Height-v) + return c, nil + case tea.KeyMsg: + switch keypress := msg.String(); keypress { + case "ctrl-c": + return c, tea.Quit + case "esc": + return initMainscreen(), heartbeat() + case "enter": + return initMainscreen(), tea.Batch(c.buildCraftMsg, heartbeat()) + } + } + + var cmd tea.Cmd + c.list, cmd = c.list.Update(msg) + return c, cmd +} + +// View renders the program's UI, which is just a string. The view is +// rendered after every Update. +func (c craftModel) View() string { + return c.list.View() +} diff --git a/mainscreen.go b/mainscreen.go index 2bbefdc..5079779 100644 --- a/mainscreen.go +++ b/mainscreen.go @@ -65,6 +65,10 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { simc := fmt.Sprintf("place %v", string(msg)) m.s.Input(simc) return m, nil + case craftMsg: + simc := fmt.Sprintf("craft %v", string(msg)) + m.s.Input(simc) + return m, nil case tea.KeyMsg: switch msg.Type { case tea.KeyCtrlC: @@ -106,6 +110,14 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { } } return newPlaceModel(res, nil), nil + case "c": + var res []sim.ItemEntry + for _, k := range sim.GlobalTechList { + if _, ok := m.s.Player.Techs[k]; ok { + res = append(res, sim.LookupTech(k)) + } + } + return newCraftModel(res, nil), nil } return m, nil } @@ -118,7 +130,9 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { func (m model) View() string { var render string - display := lipgloss.JoinHorizontal(0, style.Render(m.s.Place.String()), style.Render(fmt.Sprintf("%v", m.s.Player.String()))) - render = fmt.Sprintf("%v\n%v\n%v\n", style.Render(fmt.Sprintf("Current Time: %v", strconv.Itoa(m.s.Time))), display, style.Render(m.input.View())) + playerAndLog := lipgloss.JoinVertical(0, style.Render(fmt.Sprintf("%v", m.s.Player.String())), style.Render(fmt.Sprintf("%v", m.s.Player.Log()))) + placeAndInput := lipgloss.JoinVertical(0, style.Render(m.s.Place.String()), style.Render(m.input.View())) + display := lipgloss.JoinHorizontal(0, placeAndInput, playerAndLog) + render = fmt.Sprintf("%v\n%v\n", style.Render(fmt.Sprintf("Current Time: %v", strconv.Itoa(m.s.Time))), display) return render } diff --git a/simulator/item_table.go b/simulator/item_table.go index 5492207..711662d 100644 --- a/simulator/item_table.go +++ b/simulator/item_table.go @@ -25,11 +25,14 @@ const ( //GlobalItemList of all items var GlobalItemList = []itemType{itemPlantTea, itemPlantWood, convertPulper} +//GlobalTechList list of all techs +var GlobalTechList = []Tech{techPulper} + //Lookup returns a human friendly item entry func Lookup(id itemType) ItemEntry { switch id { case itemPlantTea: - return plantEntry{itemPlantTea, 3, "tea"} + return plantEntry{itemPlantTea, 1, "tea"} case itemPlantWood: return plantEntry{itemPlantWood, 10, "wood"} case convertPulper: @@ -37,3 +40,13 @@ func Lookup(id itemType) ItemEntry { } return nil } + +//LookupTech converts a tech ID to an item ID +func LookupTech(id Tech) ItemEntry { + switch id { + case techPulper: + return converterEntry{convertPulper, 5, "teaConverter", itemPlantTea, itemPlantWood} + } + return nil + +} diff --git a/simulator/player.go b/simulator/player.go index dd30de1..2abc7e3 100644 --- a/simulator/player.go +++ b/simulator/player.go @@ -2,17 +2,21 @@ package simulator import ( "fmt" + "strconv" ) //Player is a player controlled mob type Player struct { Resources map[itemType]int + Techs map[Tech]struct{} CurrentTile *Tile + log []string + logIndex int } //NewPlayer initializes a player func NewPlayer() *Player { - return &Player{Resources: make(map[itemType]int)} + return &Player{Resources: make(map[itemType]int), Techs: make(map[Tech]struct{})} } func (p *Player) String() string { @@ -29,3 +33,32 @@ func (p *Player) String() string { } return res } + +func (p *Player) research() { + for k, v := range p.Resources { + if k == itemPlantTea && v > 10 { + if _, ok := p.Techs[techPulper]; !ok { + p.Techs[techPulper] = struct{}{} + p.Announce("New Tech: Pulper") + } + } + } +} + +//Announce adds an entry to a players log +func (p *Player) Announce(msg string) { + p.logIndex++ + p.log = append(p.log, strconv.Itoa(p.logIndex)+" "+msg) + if len(p.log) > 3 { + p.log = p.log[1:] + } +} + +//Log returns the player log +func (p *Player) Log() string { + res := "Log:\n" + for _, v := range p.log { + res += v + "\n" + } + return res +} diff --git a/simulator/science.go b/simulator/science.go new file mode 100644 index 0000000..715103b --- /dev/null +++ b/simulator/science.go @@ -0,0 +1,8 @@ +package simulator + +//Tech is a tech level +type Tech int + +const ( + techPulper Tech = iota +) diff --git a/simulator/simulator.go b/simulator/simulator.go index a114ad9..6e3ee43 100644 --- a/simulator/simulator.go +++ b/simulator/simulator.go @@ -1,6 +1,7 @@ package simulator import ( + "fmt" "strings" "time" ) @@ -20,7 +21,7 @@ func NewSimulator() *Simulator { player := NewPlayer() pod.Place(newPlant(itemPlantTea), 4, 4) pod.Tiles[0][0].User = player - player.Resources[convertPulper] = 1 + player.Announce("Game started") return &Simulator{pod, player, 0, 0, 0, make(chan bool)} } @@ -46,6 +47,7 @@ func (s *Simulator) Input(cmd string) { prod := build.Get() if prod.Kind != 0 && prod.Value > 0 { s.Player.Resources[prod.Kind] = s.Player.Resources[prod.Kind] + prod.Value + s.Player.Announce(fmt.Sprintf("Gathered %v %v", prod.Value, Lookup(prod.Kind).Name())) } } } @@ -65,7 +67,20 @@ func (s *Simulator) Input(cmd string) { s.Player.Resources[convertPulper] = s.Player.Resources[convertPulper] - 1 } } + case "craft": + if len(cmdS) < 2 { + return + } + item := cmdS[1] + if item == convertPulper.String() { + if _, ok := s.Player.Techs[techPulper]; ok { + if s.Player.Resources[itemPlantTea] > 5 { + s.Player.Resources[convertPulper] = s.Player.Resources[convertPulper] + 1 + s.Player.Resources[itemPlantTea] = s.Player.Resources[itemPlantTea] - 5 + } + } + } case "left": res := s.Place.MovePlayer(s.Px, s.Py, s.Px, s.Py-1) if res != nil { @@ -104,6 +119,7 @@ func (s *Simulator) main() { case <-ticker.C: s.Time = s.Time + 1 s.Place.Tick() + s.Player.research() } } }