-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathcommand.go
159 lines (134 loc) · 2.84 KB
/
command.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
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
package main
import (
"fmt"
"strconv"
"strings"
)
const (
COMMANDS_WITHOUT_ARG = "SMPHK%="
COMMANDS_WITH_ARG = "LRA"
)
type Command struct {
valid bool
delayed int
callsign rune
command rune
arg int
}
func (c *Command) Apply(p *Plane) string {
if !c.valid {
return "--- Say Again? ---"
}
if p == nil {
return "---------"
}
if c.command == 'S' && p.IsActive() {
return p.StateMessage()
}
if !p.AcceptsCommands() {
return "---------"
}
var res bool
switch c.command {
case 'L': // turn left 0-4
res = p.DoTurn(-c.arg)
case 'R': // turn right 0-4
res = p.DoTurn(c.arg)
case 'A': // change altitude 0-5 (0: aproach)
res = p.DoHeight(c.arg)
// case 'S': handled above
case 'M': // maintain current altitude
res = p.DoHeight(p.height)
case 'P': // proceed current heading
res = p.DoTurn(0)
case 'H': // hold at navaid
res = p.DoHold()
case 'K': // keep position
res = p.DoKeep()
case '%', '=':
res = p.TurnAtNavaid(c.command)
default:
panic("should not happen")
}
if res {
return "Roger"
} else {
return "Unable"
}
}
type CommandInterpreter struct {
buf string
last string
reply string
delayed_commands []*Command
last_commanded_plane *Plane
}
func (ci *CommandInterpreter) KeyPressed(g *GameState, key rune) {
ci.buf += string(key)
cmd := ci.parse_command(ci.buf)
if cmd == nil {
// incomplete
return
}
ci.last = ci.buf
ci.buf = ""
if cmd.delayed > 0 {
ci.delayed_commands = append(ci.delayed_commands, cmd)
} else {
plane := g.FindPlane(cmd.callsign)
ci.reply = cmd.Apply(plane)
ci.last_commanded_plane = plane
}
}
func (ci *CommandInterpreter) Tick(g *GameState) {
ci.last_commanded_plane = nil
// TODO: old commands stay in delayed_commands with delayed < 0
for _, cmd := range ci.delayed_commands {
cmd.delayed -= 1
if cmd.delayed == 0 {
plane := g.FindPlane(cmd.callsign)
_ = cmd.Apply(plane) // apply silently
}
}
}
func (ci *CommandInterpreter) Clear() {
ci.buf = ""
ci.last = ""
ci.reply = ""
}
func (ci CommandInterpreter) StatusLine() string {
if len(ci.buf) > 0 {
return ci.buf
} else {
return fmt.Sprintf("%s %s", ci.last, ci.reply)
}
}
func (ci *CommandInterpreter) parse_command(s string) *Command {
var cmd Command
var state int
for _, char := range s {
switch {
case state == 0 && char == '.':
cmd.delayed += 1
case state == 0 && char >= 'A' && char <= 'Z':
state = 1
cmd.callsign = char
case state == 1 && strings.ContainsRune(COMMANDS_WITHOUT_ARG, char):
cmd.command = char
cmd.valid = true
return &cmd
case state == 1 && strings.ContainsRune(COMMANDS_WITH_ARG, char):
cmd.command = char
state = 2
case state == 2 && char >= '0' && char <= '9':
arg, _ := strconv.Atoi(string(char))
cmd.arg = arg
cmd.valid = true
return &cmd
default:
// valid == false
return &cmd
}
}
return nil
}