diff --git a/internal/sim/entity.go b/internal/sim/entity.go index da6f3bf..fd1fa70 100644 --- a/internal/sim/entity.go +++ b/internal/sim/entity.go @@ -23,3 +23,7 @@ type behavior interface { // update is the standard tick function update(time.Duration) } + +type doNothing struct{} + +func (d doNothing) update(time.Duration) {} diff --git a/internal/sim/player.go b/internal/sim/player.go index ae2a96a..7faddd4 100644 --- a/internal/sim/player.go +++ b/internal/sim/player.go @@ -33,7 +33,9 @@ func (m *Move) exec(r *room, p *player, seq int) result { if r.tiles[n].here != nil { return result{reply: wire.Errorf("target cell (%d, %d) is occupied", target[0], target[1])} } + r.tiles[p.entity.Position[1]*r.width+p.entity.Position[0]].here = nil p.entity.Position = target + r.tiles[n].here = p.entity return result{reply: p.entity, announce: p.entity} } @@ -44,6 +46,8 @@ type SpawnPlayer struct { queued bool } +var lastEntityID = 0 + func (s *SpawnPlayer) exec(r *room, _ *player, seq int) result { if !s.queued { r.Info("spawn player requested for: %s", s.Name) @@ -53,6 +57,7 @@ func (s *SpawnPlayer) exec(r *room, _ *player, seq int) result { return result{} } + lastEntityID++ p := &player{ Log: r.Log.Child("players").Child(s.Name), room: r, @@ -60,13 +65,15 @@ func (s *SpawnPlayer) exec(r *room, _ *player, seq int) result { outbox: s.Outbox, pending: make([]Request, 0, 32), entity: &Entity{ - ID: 999, + ID: lastEntityID, Position: [2]int{0, 0}, Glyph: '@', + behavior: doNothing{}, }, } p.pending = append(p.pending, Request{Seq: seq, From: s.Name, Wants: s}) r.players[s.Name] = p + r.tiles[0].here = p.entity s.queued = true return result{} } diff --git a/internal/sim/room.go b/internal/sim/room.go index 55582a5..67199cd 100644 --- a/internal/sim/room.go +++ b/internal/sim/room.go @@ -22,6 +22,12 @@ func (r *room) update(dt time.Duration) { for _, req := range p.pending { res := req.Wants.exec(r, p, req.Seq) p.outbox <- wire.Response{Re: req.Seq, Body: res.reply} + for _, p2 := range r.players { + if p2 == p { + continue + } + p2.outbox <- wire.Response{Body: res.reply} + } } p.pending = p.pending[0:0] } diff --git a/internal/ui/mode.go b/internal/ui/mode.go index 970febc..c03b275 100644 --- a/internal/ui/mode.go +++ b/internal/ui/mode.go @@ -3,17 +3,19 @@ package ui import ( "github.com/gdamore/tcell/v2" "github.com/jordanorelli/astro-domu/internal/sim" + "github.com/jordanorelli/astro-domu/internal/wire" ) type Mode interface { handleEvent(*UI, tcell.Event) bool + notify(wire.Value) draw(*UI) } type roomDisplay struct { width int height int - position point + entities map[int]sim.Entity } func (m *roomDisplay) handleEvent(ui *UI, e tcell.Event) bool { @@ -38,16 +40,19 @@ func (m *roomDisplay) handleEvent(ui *UI, e tcell.Event) bool { return true } +func (m *roomDisplay) notify(v wire.Value) { + if e, ok := v.(*sim.Entity); ok { + m.entities[e.ID] = *e + } +} + func (m *roomDisplay) move(ui *UI, dx, dy int) { reply, err := ui.client.Send(sim.Move{dx, dy}) if err != nil { return } e := reply.Body.(*sim.Entity) - m.position.x = e.Position[0] - m.position.y = e.Position[1] - // m.position.x = clamp(m.position.x+dx, 0, m.width-1) - // m.position.y = clamp(m.position.y+dy, 0, m.height-1) + m.entities[e.ID] = *e } func (m *roomDisplay) draw(ui *UI) { @@ -74,6 +79,7 @@ func (m *roomDisplay) draw(ui *UI) { ui.screen.SetContent(offset.x+m.width, y+offset.y, '│', nil, tcell.StyleDefault) } - // add all characters - ui.screen.SetContent(m.position.x+offset.x, m.position.y+offset.y, '@', nil, tcell.StyleDefault) + for _, e := range m.entities { + ui.screen.SetContent(e.Position[0]+offset.x, e.Position[1]+offset.y, e.Glyph, nil, tcell.StyleDefault) + } } diff --git a/internal/ui/ui.go b/internal/ui/ui.go index d5b3714..fa1e4dd 100644 --- a/internal/ui/ui.go +++ b/internal/ui/ui.go @@ -40,9 +40,14 @@ func (ui *UI) Run() { } ui.Info("spawned into room %s", welcome.Room) + entities := make(map[int]sim.Entity, len(welcome.Contents)) + for _, e := range welcome.Contents { + entities[e.ID] = e + } ui.mode = &roomDisplay{ - width: welcome.Size[0], - height: welcome.Size[1], + width: welcome.Size[0], + height: welcome.Size[1], + entities: entities, } ui.Info("running ui") if ui.handleUserInput() { @@ -97,6 +102,9 @@ func (ui *UI) clearTerminal() { func (ui *UI) handleNotifications(c <-chan wire.Response) { for n := range c { ui.Info("ignoring notification: %v", n) + ui.mode.notify(n.Body) + ui.mode.draw(ui) + ui.screen.Show() } ui.Info("notifications channel is closed so we must be done") ui.Info("clearing and finalizing screen from notifications goroutine")