package sim import ( "github.com/jordanorelli/astro-domu/internal/math" "github.com/jordanorelli/astro-domu/internal/wire" "github.com/jordanorelli/blammo" ) // player is a player session in the simulation, eek type player struct { *blammo.Log *room name string outbox chan wire.Response pending []Request avatar *entity } type Move math.Vec func (Move) NetTag() string { return "move" } func (m *Move) exec(r *room, p *player, seq int) result { pos := p.avatar.Position target := pos.Add(math.Vec(*m)) p.Info("running move for player %s from %v to %v", p.name, *m, target) if !p.room.bounds.Contains(target) { return result{reply: wire.Errorf("target cell (%d, %d) is out of bounds", target.X, target.Y)} } currentTile := r.getTile(pos) nextTile := r.getTile(target) if nextTile.here != nil { return result{reply: wire.Errorf("target cell (%d, %d) is occupied", target.X, target.Y)} } currentTile.here, nextTile.here = nil, p.avatar p.avatar.Position = target e := wire.Entity{ ID: p.avatar.ID, Position: p.avatar.Position, Glyph: p.avatar.Glyph, } return result{reply: e, announce: e} } // SpawnPlayer is a request to spawn a player type SpawnPlayer struct { Outbox chan wire.Response Name string queued bool } var lastEntityID = 0 func (s *SpawnPlayer) exec(r *room, p *player, seq int) result { if !s.queued { r.Info("spawn player requested for: %s", s.Name) if _, ok := r.players[s.Name]; ok { s.Outbox <- wire.ErrorResponse(seq, "a player is already logged in as %q", s.Name) return result{} } lastEntityID++ avatar := &entity{ ID: lastEntityID, Position: math.Vec{0, 0}, Glyph: '@', behavior: doNothing{}, } p := &player{ Log: r.Log.Child("players").Child(s.Name), room: r, name: s.Name, outbox: s.Outbox, pending: make([]Request, 0, 32), avatar: avatar, } p.pending = append(p.pending, Request{Seq: seq, From: s.Name, Wants: s}) r.players[s.Name] = p r.tiles[0].here = p.avatar s.queued = true return result{} } welcome := wire.Welcome{ Rooms: make(map[string]wire.Room), Players: make(map[string]wire.Player), } ents := make(map[int]*wire.Entity) for id, e := range r.allEntities() { ents[id] = &wire.Entity{ ID: id, Position: e.Position, Glyph: e.Glyph, } } welcome.Rooms[r.name] = wire.Room{ Name: r.name, Bounds: r.bounds, Entities: ents, } for _, p := range r.players { welcome.Players[p.name] = wire.Player{ Name: p.name, Avatar: p.avatar.ID, Room: r.name, } } return result{ reply: welcome, announce: wire.Entity{ ID: p.avatar.ID, Position: p.avatar.Position, Glyph: p.avatar.Glyph, }, } } func (SpawnPlayer) NetTag() string { return "player/spawn" } // PlayerSpawned is an announcement that a player has spawned type PlayerSpawned struct { Name string `json:"name"` Position [2]int `json:"position"` } func (PlayerSpawned) NetTag() string { return "player/spawned" } func init() { wire.Register(func() wire.Value { return new(Move) }) }