diff --git a/internal/app/game_view.go b/internal/app/game_view.go index dcd4c52..83efd6d 100644 --- a/internal/app/game_view.go +++ b/internal/app/game_view.go @@ -36,13 +36,13 @@ func (v *gameView) walkHandler(e *tcell.EventKey) change { if e.Key() == tcell.KeyRune { switch e.Rune() { case 'w': - return move{0, -1} + return move(math.Up) case 'a': - return move{-1, 0} + return move(math.Left) case 's': - return move{0, 1} + return move(math.Down) case 'd': - return move{1, 0} + return move(math.Right) case 'i': return openInventory{} case 'l': @@ -175,13 +175,10 @@ func (v *gameView) drawHeader(img canvas, st *state) { func (v *gameView) setFocus(yes bool) { v.inFocus = yes } -type move struct { - x int - y int -} +type move math.Vec func (m move) exec(ui *UI) { - go ui.client.Send(sim.Move{m.x, m.y}) + go ui.client.Send(sim.Move(m)) } type lookAt struct { @@ -243,7 +240,7 @@ func (p pickup) exec(ui *UI) { case *sim.Pickedup: ui.misc <- func(ui *UI) { ui.state.detail = textView(fmt.Sprintf("you picked up: %s", v.Name)) - ui.state.inventory.items = append(ui.state.inventory.items, item{name: v.Name}) + ui.state.inventory.items = append(ui.state.inventory.items, item{ID: v.ID, name: v.Name}) } case wire.Error: ui.misc <- func(ui *UI) { @@ -256,3 +253,29 @@ func (p pickup) exec(ui *UI) { } }() } + +type putdown sim.Putdown + +func (p putdown) exec(ui *UI) { + go func() { + res, err := ui.client.Send(sim.Putdown(p)) + if err != nil { + ui.state.detail = textView(err.Error()) + return + } + + switch v := res.Body.(type) { + case *wire.OK: + ui.state.inventory.removeItem(p.ID) + + case wire.Error: + ui.misc <- func(ui *UI) { + ui.state.detail = textView(v.Error()) + } + default: + ui.misc <- func(ui *UI) { + ui.state.detail = textView(fmt.Sprintf("unexpected putdown response type: %T", res.Body)) + } + } + }() +} diff --git a/internal/app/inventory.go b/internal/app/inventory.go index 0592d87..cd3ba17 100644 --- a/internal/app/inventory.go +++ b/internal/app/inventory.go @@ -5,51 +5,136 @@ import ( "github.com/gdamore/tcell/v2" "github.com/jordanorelli/astro-domu/internal/math" + "github.com/jordanorelli/astro-domu/internal/wire" ) type inventory struct { items []item } +func (inv *inventory) removeItem(id int) bool { + for i, item := range inv.items { + if item.ID == id { + inv.items = append(inv.items[:i], inv.items[i+1:]...) + return true + } + } + return false +} + type item struct { + ID int name string } type inventoryView struct { highlight int + avatar *wire.Entity // probably but not necessarily the player avatar *inventory + keyHandler func(*tcell.EventKey) change } func (v *inventoryView) handleEvent(e tcell.Event) change { + if v.keyHandler == nil { + v.keyHandler = v.selectHandler + } + switch t := e.(type) { case *tcell.EventKey: - key := t.Key() - switch key { - case tcell.KeyEnter: - case tcell.KeyDown: - if len(v.items) > 0 { - v.highlight = (v.highlight + 1) % len(v.items) + return v.keyHandler(t) + default: + // ui.Debug("screen saw unhandled event of type %T", e) + } + return nil +} + +func (v *inventoryView) selectHandler(e *tcell.EventKey) change { + key := e.Key() + + if key == tcell.KeyRune { + switch e.Rune() { + case 'p': + v.keyHandler = v.putdownHandler + return nil + } + } + + switch key { + case tcell.KeyEnter: + case tcell.KeyDown: + if len(v.items) > 0 { + v.highlight = (v.highlight + 1) % len(v.items) + } + + case tcell.KeyUp: + if len(v.items) > 0 { + v.highlight = (v.highlight - 1 + len(v.items)) % len(v.items) + } + + case tcell.KeyESC: + return changeFn(func(ui *UI) { + if ui.root == inGameView { + inGameView.focus(0) } + }) + } + + return nil +} + +func (v *inventoryView) putdownHandler(e *tcell.EventKey) change { + key := e.Key() - case tcell.KeyUp: - if len(v.items) > 0 { - v.highlight = (v.highlight - 1 + len(v.items)) % len(v.items) + if key == tcell.KeyRune { + switch e.Rune() { + case 'w': + return putdown{ + ID: v.items[v.highlight].ID, + Location: v.avatar.Position.Add(math.Up), + } + case 'a': + return putdown{ + ID: v.items[v.highlight].ID, + Location: v.avatar.Position.Add(math.Left), + } + case 's': + return putdown{ + ID: v.items[v.highlight].ID, + Location: v.avatar.Position.Add(math.Down), } + case 'd': + return putdown{ + ID: v.items[v.highlight].ID, + Location: v.avatar.Position.Add(math.Left), + } + } + } + + switch key { + case tcell.KeyEnter: + case tcell.KeyDown: + if len(v.items) > 0 { + v.highlight = (v.highlight + 1) % len(v.items) + } - case tcell.KeyESC: - return changeFn(func(ui *UI) { - if ui.root == inGameView { - inGameView.focus(0) - } - }) + case tcell.KeyUp: + if len(v.items) > 0 { + v.highlight = (v.highlight - 1 + len(v.items)) % len(v.items) } + + case tcell.KeyESC: + return changeFn(func(ui *UI) { + if ui.root == inGameView { + inGameView.focus(0) + } + }) } return nil } func (v *inventoryView) draw(img canvas, st *state) { - v.inventory = &st.inventory + v.avatar = st.avatar writeString(img, "Inventory", math.Vec{0, 0}, tcell.StyleDefault) for i, item := range st.inventory.items { line := fmt.Sprintf("- %s", item.name) @@ -64,7 +149,10 @@ type openInventory struct{} func (openInventory) exec(ui *UI) { if ui.root == inGameView { - ui.state.detail = &inventoryView{} + ui.state.detail = &inventoryView{ + avatar: ui.state.avatar, + inventory: &ui.state.inventory, + } inGameView.focus(1) } } diff --git a/internal/app/login.go b/internal/app/login.go index 56e8e89..d5d92e1 100644 --- a/internal/app/login.go +++ b/internal/app/login.go @@ -32,9 +32,9 @@ func (l login) exec(ui *UI) { ui.state.room = new(wire.Room) } p := welcome.Players[l.name] - // avi := p.Avatar room := welcome.Rooms[p.Room] - // e := room.Entities[p.Avatar] + e := room.Entities[p.Avatar] + ui.state.avatar = &e ui.state.room = &room ui.root = inGameView diff --git a/internal/app/state.go b/internal/app/state.go index 9eb32ad..dcfd6c1 100644 --- a/internal/app/state.go +++ b/internal/app/state.go @@ -8,6 +8,7 @@ import ( type state struct { playerName string + avatar *wire.Entity room *wire.Room history []sim.ChatMessage inventory inventory diff --git a/internal/app/ui.go b/internal/app/ui.go index 6a2a3a8..1b0c315 100644 --- a/internal/app/ui.go +++ b/internal/app/ui.go @@ -102,6 +102,9 @@ func (ui *UI) handleNotification(v wire.Value) bool { switch n := v.(type) { case *wire.Entity: + if ui.state.avatar != nil && n.ID == ui.state.avatar.ID { + *ui.state.avatar = *n + } ui.state.room.Entities[n.ID] = *n return true @@ -113,6 +116,13 @@ func (ui *UI) handleNotification(v wire.Value) bool { ui.state.room.Name = n.RoomName ui.state.room.Rect = n.RoomSize ui.state.room.Entities = n.Entities + if ui.state.avatar != nil { + e, ok := n.Entities[ui.state.avatar.ID] + if ok { + *ui.state.avatar = e + } + } + return true case *wire.Delta: @@ -120,6 +130,13 @@ func (ui *UI) handleNotification(v wire.Value) bool { ui.state.room.Rect = *n.RoomSize } + if ui.state.avatar != nil { + id := ui.state.avatar.ID + if ep, ok := n.Entities[id]; ok { + *ui.state.avatar = *ep + } + } + if len(n.Entities) > 0 { for id, e := range n.Entities { if e != nil { diff --git a/internal/math/vec.go b/internal/math/vec.go index 0b03201..c57c20b 100644 --- a/internal/math/vec.go +++ b/internal/math/vec.go @@ -5,6 +5,14 @@ import ( "fmt" ) +var ( + Origin = Vec{0, 0} + Up = Vec{0, -1} // w + Left = Vec{-1, 0} // a + Down = Vec{0, 1} // s + Right = Vec{1, 0} // d +) + type Vec struct { X int Y int diff --git a/internal/sim/player.go b/internal/sim/player.go index 8fbc022..17fc7cd 100644 --- a/internal/sim/player.go +++ b/internal/sim/player.go @@ -295,12 +295,13 @@ func (pu *Pickup) exec(w *world, r *room, pl *player, seq int) result { } nextTile.here = nextTile.here[0:0] pl.inventory = append(pl.inventory, e) - return result{reply: Pickedup{Name: e.name}} + return result{reply: Pickedup{ID: e.ID, Name: e.name}} } return result{reply: wire.Errorf("nothing here")} } type Pickedup struct { + ID int `json:"id"` Name string `json:"name"` }