forked from gophergala2016/spacegophers
/
game_state.go
133 lines (104 loc) · 2.95 KB
/
game_state.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
package main
import (
"time"
"github.com/apex/log"
)
// DefaultStateUpdateLoopInterval describes the default interval for which the
// server should be sending to it's users the current state of the game.
const DefaultStateUpdateLoopInterval = time.Second / DefaultUpdateFPS
// NewGameState creates a new game state given a logging context.
func NewGameState(ctx log.Interface) GameState {
return GameState{
Users: make(map[*User]bool),
Shots: make(map[*Shot]bool),
Log: ctx.WithField("module", "GameState"),
UpdateInterval: DefaultStateUpdateLoopInterval,
simulate: make(chan []Command),
updateState: make(chan *GameState),
}
}
// GameState stores the state of the game at any given time.
type GameState struct {
Users map[*User]bool
Shots map[*Shot]bool
Log log.Interface
UpdateInterval time.Duration
simulate chan []Command
updateState chan *GameState
}
// Loop recieves commands from the CommandProcessor to simulate and also
// triggers the state update channel to update users connected to the game.
func (gs *GameState) Loop() {
updateTimer := time.NewTicker(gs.UpdateInterval)
for {
select {
case commands := <-gs.simulate:
// if there are commands in the first place...
if commands != nil {
// then process them
for _, command := range commands {
if !command.User.Gopher.Alive {
continue
}
if command.Message == "fire" {
// if i can shoot...
if !command.User.Gopher.NoShots {
// shoot!
shot := NewShot(command.User, command.User.Gopher)
// add the shot
gs.Shots[&shot] = true
// mark that the gopher shot
command.User.Gopher.Shoot()
}
} else {
command.User.Gopher.Process(command)
}
}
}
for user := range gs.Users {
user.Gopher.Simulate()
if user.Gopher.NoShots {
user.Gopher.MaybeShootAgain()
}
}
for shot := range gs.Shots {
if shot.IsAlive() {
shot.Simulate()
shot.Lifecycles--
} else {
delete(gs.Shots, shot)
}
}
for user := range gs.Users {
// if the gopher is still alive...
if user.Gopher.Alive {
// see if we just killed it
bb := user.Gopher.BoundingBox()
for shot := range gs.Shots {
// we can't shoot ourselves...
if shot.Gopher == user.ID {
continue
}
// if we are hitting the shot
if shot.Position.Inside(bb) {
// kill the gopher
user.Gopher.Kill()
// award the points
shot.User.Gopher.Points += pointsPerKill
gs.Log.Infof("%d points awarded to Gopher %s", pointsPerKill, shot.User.Gopher.UserID)
// kill the shot
delete(gs.Shots, shot)
// this gopher already died! can't kill it again!
break
}
}
} else {
// otherwise, see if we can resurrect it
user.Gopher.MaybeResurrect()
}
}
case <-updateTimer.C:
gs.updateState <- gs
}
}
}