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
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268module Duets.Simulation.Tests.Events.Character
open FsUnit
open NUnit.Framework
open Test.Common
open Test.Common.Generators
open Duets.Entities
open Duets.Simulation
let lowCharacterHealthEffect state =
let character = Queries.Characters.playableCharacter state
CharacterAttributeChanged(
character.Id,
CharacterAttribute.Health,
Diff(15, 5)
)
[<Test>]
let ``tick of low character health should generate health depleted`` () =
let state = State.generateOne State.defaultOptions
Simulation.tickOne state (lowCharacterHealthEffect state)
|> fst
|> List.item 1
|> should be (ofCase <@ CharacterHealthDepleted @>)
[<Test>]
let ``tick of low character health should hospitalize character`` () =
let state = State.generateOne State.defaultOptions
Simulation.tickOne state (lowCharacterHealthEffect state)
|> fst
|> List.item 2
|> should be (ofCase <@ CharacterHospitalized @>)
[<Test>]
let ``tick of low character health during concert should cancel concert`` () =
let state =
State.generateOne
{ State.defaultOptions with
FutureConcertsToGenerate = 0 }
let stateOnConcert =
Situations.inConcert
{ Concert = dummyConcert
Events = []
Checklist =
{ MerchStandSetup = false
SoundcheckDone = false }
Points = 0<quality> }
|> State.Root.applyEffect state
Simulation.tickOne stateOnConcert (lowCharacterHealthEffect state)
|> fst
|> List.item 1
|> should be (ofCase <@ ConcertCancelled @>)
[<Test>]
let ``tick of low character health advances one week`` () =
let state = State.generateOne State.defaultOptions
let oneWeekLater = Calendar.gameBeginning |> Calendar.Ops.addDays 7<days>
Simulation.tickOne state (lowCharacterHealthEffect state)
|> fst
|> should contain (TimeAdvanced oneWeekLater)
let private assertAttributeChanged attribute amount effect =
let state = State.generateOne State.defaultOptions
let character = Queries.Characters.playableCharacter state
Simulation.tickOne state effect
|> fst
|> List.choose (fun effect ->
match effect with
| CharacterAttributeChanged(characterId, attr, diff) ->
Some(characterId, attr, diff)
| _ -> None)
|> List.head
|> fun (characterId, attr, Diff(_, currentAmount)) ->
characterId |> should equal character.Id
attr |> should equal attribute
currentAmount |> should equal amount
[<Test>]
let ``tick of song improved should decrease energy`` () =
SongImproved(dummyBand, Diff(dummyUnfinishedSong, dummyUnfinishedSong))
|> assertAttributeChanged CharacterAttribute.Energy 80
[<Test>]
let ``tick of song started should decrease energy`` () =
SongStarted(dummyBand, dummyUnfinishedSong)
|> assertAttributeChanged CharacterAttribute.Energy 80
[<Test>]
let ``tick of song practiced should decrease energy`` () =
SongPracticed(dummyBand, dummyFinishedSong)
|> assertAttributeChanged CharacterAttribute.Energy 90
[<Test>]
let ``tick of passing time should decrease character's drunkenness`` () =
let state = State.generateOne State.defaultOptions
let character = Queries.Characters.playableCharacter state
let stateAfterGettingDrunk =
Character.Attribute.add character CharacterAttribute.Drunkenness 15
|> State.Root.applyEffects state
let character = Queries.Characters.playableCharacter stateAfterGettingDrunk
let oneDayMomentLater =
Calendar.gameBeginning |> Calendar.Transform.changeDayMoment Morning
Simulation.tickOne stateAfterGettingDrunk (TimeAdvanced oneDayMomentLater)
|> fst
|> should
contain
(CharacterAttributeChanged(
character.Id,
CharacterAttribute.Drunkenness,
Diff(15, 10)
))
[<Test>]
let ``tick of passing time should decrease character's health when passing 85 in drunkenness``
()
=
let state = State.generateOne State.defaultOptions
let character = Queries.Characters.playableCharacter state
let stateAfterGettingDrunk =
Character.Attribute.add character CharacterAttribute.Drunkenness 95
|> State.Root.applyEffects state
let character = Queries.Characters.playableCharacter stateAfterGettingDrunk
let oneDayMomentLater =
Calendar.gameBeginning |> Calendar.Transform.changeDayMoment Morning
let expectedHealth = 100 + Config.LifeSimulation.drunkHealthReduceRate
Simulation.tickOne stateAfterGettingDrunk (TimeAdvanced oneDayMomentLater)
|> fst
|> should
contain
(CharacterAttributeChanged(
character.Id,
CharacterAttribute.Health,
Diff(100, expectedHealth)
))
let private testConcertEffect effect moodIncrease =
let state =
State.generateOne
{ State.defaultOptions with
CharacterMoodMin = 50
CharacterMoodMax = 50 }
let character = Queries.Characters.playableCharacter state
Simulation.tickOne state effect
|> fst
|> should
contain
(CharacterAttributeChanged(
character.Id,
CharacterAttribute.Mood,
Diff(50, 50 + moodIncrease)
))
let private testConcertFinishedEffect concertQuality =
ConcertFinished(
dummyBand,
PerformedConcert(dummyConcert, concertQuality),
0m<dd>
)
|> testConcertEffect
[<Test>]
let ``tick of ConcertCancelled should greatly decrease character's mood`` () =
let effect =
ConcertCancelled(
dummyBand,
FailedConcert(dummyConcert, BandDidNotMakeIt)
)
testConcertEffect effect Config.LifeSimulation.Mood.concertFailIncrease
[<Test>]
let ``tick of ConcertFinished with a bad concert should decrease character's mood``
()
=
testConcertFinishedEffect
20<quality>
Config.LifeSimulation.Mood.concertPoorResultIncrease
[<Test>]
let ``tick of ConcertFinished with an okay concert should increase character's mood``
()
=
testConcertFinishedEffect
50<quality>
Config.LifeSimulation.Mood.concertNormalResultIncrease
[<Test>]
let ``tick of ConcertFinished with a good concert should decrease character's mood``
()
=
testConcertFinishedEffect
80<quality>
Config.LifeSimulation.Mood.concertGoodResultIncrease
[<Test>]
let ``tick of BandFansChanged does nothing if character's fame is already at least half of the estimated fame of the band``
()
=
let state =
dummyState
|> State.Characters.setAttribute
dummyCharacter.Id
CharacterAttribute.Fame
20
let previousFans = [ Prague, 5000<fans> ] |> Map.ofList
let updatedFans = [ Prague, 10000<fans> ] |> Map.ofList
let effect = BandFansChanged(dummyBand, Diff(previousFans, updatedFans))
Simulation.tickOne state effect
|> fst
|> List.filter (function
| CharacterAttributeChanged _ -> true
| _ -> false)
|> should haveLength 0
[<Test>]
let ``tick of BandFansChanged updates the character's fame to be at least half of the estimated fame of the band``
()
=
let state =
dummyState
|> State.Characters.setAttribute
dummyCharacter.Id
CharacterAttribute.Fame
8
let previousFans = [ Prague, 5000<fans> ] |> Map.ofList
let updatedFans = [ Prague, 10000<fans> ] |> Map.ofList
let effect = BandFansChanged(dummyBand, Diff(previousFans, updatedFans))
Simulation.tickOne state effect
|> fst
|> should
contain
(CharacterAttributeChanged(
dummyCharacter.Id,
CharacterAttribute.Fame,
Diff(8, 16)
))