func (e *Engine) processActionRequest(req *action.Request) bool { // [GAME-SPECIFIC] Handle action requests according to game rules switch req.Action.Id { case game.ThrowBallAction: log.Println("Accepting ball", req) initiator := e.entity.Get(req.Action.Initiator) ball := entity.New() ball.Type = game.BallEntity ball.Sprite = game.BallSprite ball.Position = initiator.Position ball.Speed.Norm = float64(e.config.DefaultBallSpeed) ball.Speed.Angle = float64(req.Action.Params[0].(float32)) ball.Attributes[game.TicksLeftAttr] = e.config.DefaultBallLifespan ball.Attributes[game.SenderAttr] = initiator.Id r := entity.NewCreateRequest(req.GetTick(), ball) if err := e.entity.AcceptRequest(r); err != nil { // This shouldn't fail panic(err) } session := e.auth.GetSessionByEntity(req.Action.Initiator) // TODO: error handling e.clients[session.Id].Write(func(w io.Writer) error { return builder.SendEntityIdChange(w, req.Action.Params[1].(message.EntityId), ball.Id) }) } return true }
// Compute an entity's new position. // Returns an EntityDiff if the entity has changed, nil otherwise. func (m *Mover) UpdateEntity(ent *entity.Entity, now message.AbsoluteTick) (req *entity.UpdateRequest, collidesWith interface{}) { // TODO: remove Mover.lastUpdates? var last message.AbsoluteTick var ok bool if last, ok = m.lastUpdates[ent.Id]; !ok { last = now } m.lastUpdates[ent.Id] = now dt := time.Duration(now-last) * clock.TickDuration // Convert to seconds if dt == 0 { return } if dt < 0 { return // TODO: figure out if it's the right thing to do here } speed := ent.Speed pos := ent.Position nextPos := speed.GetNextPosition(pos, dt) if nextPos == nil { return } // Check terrain // TODO: use brensenham algorithm // See http://tech-algorithm.com/articles/drawing-line-using-bresenham-algorithm/ route := terrain.GetRouteBetween(pos, nextPos) stoppedAt, collidesWith := checkRoute(route, ent, m.engine.ctx.Terrain, m.engine.ctx.Entity.List()) if stoppedAt != nil { // The entity has been stopped while moving nextPos = stoppedAt } newEnt := entity.New() newEnt.Id = ent.Id newEnt.Position = nextPos diff := &message.EntityDiff{Position: true} req = entity.NewUpdateRequest(now, newEnt, diff) return }
func (e *Engine) moveEntities(t message.AbsoluteTick) { for _, ent := range e.entity.List() { req, collidesWith := e.mover.UpdateEntity(ent, t) // [GAME-SPECIFIC] Destroy balls when they collide if collidesWith != nil && ent.Type == game.BallEntity { req := entity.NewDeleteRequest(t, ent.Id) if err := e.entity.AcceptRequest(req); err != nil { panic(err) // This shouldn't happen } // If the ball collides with a player, decrease his health if collidesWith, ok := collidesWith.(*message.Entity); ok && collidesWith.Type == game.PlayerEntity { sender := ent.Attributes[game.SenderAttr].(message.EntityId) if sender != collidesWith.Id { health := collidesWith.Attributes[game.HealthAttr].(game.Health) updated := entity.New() updated.Attributes[game.HealthAttr] = health - 1 diff := message.NewEntityDiff() diff.Attributes = true req := entity.NewUpdateRequest(t, updated, diff) if err := e.entity.AcceptRequest(req); err != nil { panic(err) // This shouldn't happen } } } continue } if req != nil { // Let's assume mover already did all security checks if err := e.entity.AcceptRequest(req); err != nil { // This should not fail panic(err) } } } }