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
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
|
namespace LivingVillage.Desktop
open LivingVillage.Kernel
type MenuPage =
| MainMenu
| Settings
| Controls
| Playing
| LoadError
type MenuItem =
| ContinueItem
| NewGameItem
| LoadItem
| SettingsItem
| ControlsItem
| ExitItem
type MenuSetting =
| OneXSetting
| TwoXSetting
| FiveXSetting
| PauseSetting
type MenuInput =
| Up
| Down
| Confirm
| Back
type MenuCommand =
| NoCommand
| ContinueGame
| StartNewGame
| LoadGame
| ApplySettings of SimulationControl
| ExitGame
type ControlRow =
{ Keys: string
Action: string }
type MenuState =
{ Page: MenuPage
Selected: int
HasCurrentWorld: bool
ReturnPage: MenuPage
Settings: SimulationControl
Error: string option }
module MenuState =
let settings : MenuSetting list =
[ OneXSetting; TwoXSetting; FiveXSetting; PauseSetting ]
let controlRows : ControlRow list =
[ { Keys = "WASD"; Action = "移动" }
{ Keys = "E"; Action = "互动" }
{ Keys = "1-6"; Action = "对话" }
{ Keys = "Q"; Action = "观察" }
{ Keys = "TAB"; Action = "需求" }
{ Keys = "C"; Action = "年鉴" }
{ Keys = "L"; Action = "关系" }
{ Keys = "P"; Action = "暂停" }
{ Keys = "F1/F2/F3"; Action = "速度" }
{ Keys = "F6"; Action = "保存" }
{ Keys = "F7"; Action = "读取" }
{ Keys = "ESC"; Action = "菜单或关闭" }
{ Keys = "UP/DOWN"; Action = "选择" }
{ Keys = "ENTER"; Action = "确认" } ]
let create (simulationControl: SimulationControl) (hasCurrentWorld: bool) : MenuState =
{ Page = MainMenu
Selected = 0
HasCurrentWorld = hasCurrentWorld
ReturnPage = MainMenu
Settings = simulationControl
Error = None }
let items (state: MenuState) : MenuItem list =
if state.HasCurrentWorld then
[ ContinueItem; NewGameItem; LoadItem; SettingsItem; ControlsItem; ExitItem ]
else
[ NewGameItem; LoadItem; SettingsItem; ControlsItem; ExitItem ]
let settingsLabel setting : string =
match setting with
| OneXSetting -> "1倍速"
| TwoXSetting -> "2倍速"
| FiveXSetting -> "5倍速"
| PauseSetting -> "暂停"
let itemLabel item : string =
match item with
| ContinueItem -> "继续游戏"
| NewGameItem -> "开始新游戏"
| LoadItem -> "读取存档"
| SettingsItem -> "设置"
| ControlsItem -> "操作说明"
| ExitItem -> "退出"
let private optionsFor (state: MenuState) : int =
match state.Page with
| MainMenu -> items state |> List.length
| Settings -> settings.Length
| _ -> 0
let private moveBy (delta: int) (state: MenuState) : MenuState =
let count = optionsFor state
if count = 0 then
state
else
let next = (state.Selected + delta) % count
let wrapped = if next < 0 then next + count else next
{ state with Selected = wrapped }
let move direction state =
match direction with
| Up -> moveBy -1 state
| Down -> moveBy 1 state
| Confirm
| Back -> state
let selectedItem (state: MenuState) : MenuItem =
items state |> List.item state.Selected
let selectedSetting (state: MenuState) : MenuSetting =
settings |> List.item state.Selected
let private selectedControl (state: MenuState) : SimulationControl =
match selectedSetting state with
| OneXSetting -> { state.Settings with Paused = false; Speed = OneX }
| TwoXSetting -> { state.Settings with Paused = false; Speed = TwoX }
| FiveXSetting -> { state.Settings with Paused = false; Speed = FiveX }
| PauseSetting -> { state.Settings with Paused = true }
let enterGame (state: MenuState) : MenuState =
{ state with Page = Playing; Selected = 0; ReturnPage = MainMenu; Error = None }
let setCurrentWorld (hasCurrentWorld: bool) (state: MenuState) : MenuState =
{ state with HasCurrentWorld = hasCurrentWorld }
let setSettings (simulationControl: SimulationControl) (state: MenuState) : MenuState =
{ state with Settings = simulationControl }
let openMain (state: MenuState) : MenuState =
{ state with Page = MainMenu; Selected = 0; ReturnPage = MainMenu; Error = None }
let openSettings (state: MenuState) : MenuState =
{ state with Page = Settings; Selected = 0; ReturnPage = state.Page; Error = None }
let openControls (state: MenuState) : MenuState =
{ state with Page = Controls; Selected = 0; ReturnPage = state.Page; Error = None }
/// P key: open the full key list as a pause-help page. The simulation is paused while
/// the page is open; the caller keeps the previous control and restores it on exit.
let enterPauseHelp (control: SimulationControl) (state: MenuState) : MenuState * SimulationControl =
openControls state, { control with Paused = true }
let showLoadError (message: string) (state: MenuState) : MenuState =
{ state with Page = LoadError; Selected = 0; ReturnPage = MainMenu; Error = Some message }
let private returnFromSubpage (state: MenuState) : MenuState =
match state.ReturnPage with
| Playing -> { state with Page = Playing; Selected = 0; Error = None }
| _ -> openMain state
/// Leaving the pause-help page returns to the page it was opened from and restores
/// the exact simulation control the player had before.
let exitPauseHelp (original: SimulationControl) (state: MenuState) : MenuState * SimulationControl =
returnFromSubpage state, original
let private confirmMain (state: MenuState) : MenuState * MenuCommand =
match selectedItem state with
| ContinueItem -> enterGame state, ContinueGame
| NewGameItem -> state, StartNewGame
| LoadItem -> state, LoadGame
| SettingsItem -> openSettings state, NoCommand
| ControlsItem -> openControls state, NoCommand
| ExitItem -> state, ExitGame
let update (input: MenuInput) (state: MenuState) : MenuState * MenuCommand =
match input with
| Up -> move Up state, NoCommand
| Down -> move Down state, NoCommand
| Back ->
match state.Page with
| MainMenu -> state, ExitGame
| Settings
| Controls -> returnFromSubpage state, NoCommand
| LoadError -> openMain state, NoCommand
| Playing -> openMain state, NoCommand
| Confirm ->
match state.Page with
| MainMenu -> confirmMain state
| Settings ->
let nextSettings = selectedControl state
{ state with Settings = nextSettings }, ApplySettings nextSettings
| Controls -> state, NoCommand
| LoadError -> openMain state, NoCommand
| Playing -> state, NoCommand
|