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
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240namespace Duets.Cli.Components.Commands
open Duets.Agents
open Duets.Cli
open Duets.Cli.Components
open Duets.Cli.SceneIndex
open Duets.Cli.Text
open Duets.Cli.Text.Prompts
open Duets.Entities
open Duets.Simulation
type private DriveChoice =
| DriveWithinCity
| DriveToCity of CityId
[<RequireQualifiedAccess>]
module DriveCommand =
/// Command that allows the player to travel within a city and to another
/// city (if connected by road) by driving a car.
let rec create currentCarPosition car reachableCities =
{ Name = "drive"
Description =
"Allows you to drive your car to a place within the current city, or to a different, reachable city"
Handler =
fun _ ->
showSeparator None
lineBreak ()
// Ask if driving within city or to another city
let choice = chooseDriveType reachableCities
match choice with
| None ->
Travel.driveCancelled |> showMessage
lineBreak ()
Scene.World
| Some DriveWithinCity -> driveWithinCity currentCarPosition car
| Some(DriveToCity destinationCityId) ->
driveToCity destinationCityId currentCarPosition car }
and private chooseDriveType reachableCities =
let choices =
[| ("Within current city", DriveWithinCity)
yield!
reachableCities
|> List.map (fun (cityId, _) ->
(Generic.cityName cityId, DriveToCity cityId)) |]
showOptionalChoicePrompt
"Where do you want to drive to?"
Generic.cancel
fst
choices
|> Option.map snd
and private driveWithinCity currentCarPosition car =
let destination = showMap ()
match destination with
| None ->
Travel.driveCancelled |> showMessage
lineBreak ()
Scene.World
| Some place ->
planAndConfirmDriveWithinCity place currentCarPosition car
and private planAndConfirmDriveWithinCity
(destination: Place)
currentCarCoords
car
=
let state = State.get ()
showSeparator None
Travel.driveCalculatingRoute |> showMessage
lineBreak ()
let planResult = Vehicles.Car.planWithinCityDrive state destination
match planResult with
| Error Vehicles.Car.AlreadyAtDestination ->
Travel.driveAlreadyAtDestination |> showMessage
lineBreak ()
Scene.World
| Error Vehicles.Car.CannotReachDestination ->
Travel.driveCannotReachDestination |> showMessage
lineBreak ()
Scene.World
| Ok(_, travelTime) ->
Travel.driveRouteEstimate travelTime destination.Name |> showMessage
lineBreak ()
let confirmed = showConfirmationPrompt Travel.driveConfirmRoute
if confirmed then
driveToDestinationWithinCity destination currentCarCoords car
else
Travel.driveCancelled |> showMessage
lineBreak ()
Scene.World
and private driveToDestinationWithinCity
(destination: Place)
currentCarCoords
car
=
let state = State.get ()
showSeparator None
Travel.driveStarting destination.Name |> showMessage
lineBreak ()
wait 500<millisecond>
takeWithinCityDrive destination.Name car
let moveEffects =
Vehicles.Car.driveWithinCity state destination currentCarCoords car
Effect.applyMultiple moveEffects
Scene.WorldAfterMovement
and private driveToCity destinationCityId currentCarPosition car =
let state = State.get ()
let planResult =
Vehicles.Car.planIntercityDrive state destinationCityId car
match planResult with
| Error Vehicles.Car.AlreadyAtDestination ->
Travel.driveAlreadyAtDestination |> showMessage
lineBreak ()
Scene.World
| Error Vehicles.Car.CannotReachDestination ->
Travel.driveCannotReachDestination |> showMessage
lineBreak ()
Scene.World
| Ok(distance, travelTime, tripDuration) ->
showSeparator None
Travel.driveIntercityEstimate
(Generic.cityName destinationCityId)
distance
travelTime
|> showMessage
lineBreak ()
Travel.driveIntercityWarning |> showMessage
lineBreak ()
let confirmed = showConfirmationPrompt Travel.driveConfirmRoute
if confirmed then
executeIntercityDrive
destinationCityId
currentCarPosition
car
tripDuration
else
Travel.driveCancelled |> showMessage
lineBreak ()
Scene.World
and private executeIntercityDrive
destinationCityId
currentCarPosition
car
tripDuration
=
let state = State.get ()
showSeparator None
Travel.driveStarting (Generic.cityName destinationCityId) |> showMessage
lineBreak ()
wait 500<millisecond>
takeIntercityDrive destinationCityId car
let moveEffects =
Vehicles.Car.driveToCity
state
destinationCityId
currentCarPosition
car
tripDuration
Effect.applyMultiple moveEffects
Scene.WorldAfterMovement
and private takeWithinCityDrive destinationName car =
let state = State.get ()
generateWithinCityDrivingMoment state destinationName car
wait 2000<millisecond>
showSeparator None
Travel.driveArrivedAtDestination destinationName |> showMessage
lineBreak ()
and private takeIntercityDrive destinationCityId car =
let state = State.get ()
let originCityId, _, _ = state.CurrentPosition
generateIntercityDrivingMoment state originCityId destinationCityId car
wait 2000<millisecond>
showSeparator None
Travel.driveArrivedAtDestination (Generic.cityName destinationCityId)
|> showMessage
lineBreak ()
and private generateWithinCityDrivingMoment state destinationName car =
Driving.createWithinCityDrivingMomentPrompt state destinationName car
|> LanguageModel.streamMessage
|> streamStyled Styles.event
lineBreak ()
and private generateIntercityDrivingMoment
state
originCityId
destinationCityId
car
=
Driving.createIntercityDrivingMomentPrompt
state
originCityId
destinationCityId
car
|> LanguageModel.streamMessage
|> streamStyled Styles.event
lineBreak ()