-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathPlayer.hs
175 lines (149 loc) · 6.41 KB
/
Player.hs
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
-- | Module for the 'Player' type and related functions. 'Player' encodes
-- all the information about the (state of the) player that is relevant to
-- our combat model.
module Player where
import Types as T
import Rdice
import qualified Data.Map as Map
import Data.Maybe
-- | The base number by which the player's to-hit roll needs to exceed
--a monster's evasion roll for a critical hit to occur. This is 7.0 in
--Sil (and not likely to change).
playerBaseCritThres = 7.0
-- | The state of a player as far as fsil is concerned, so hit points
-- etc are not included, since we're currently just simulating damage
-- distributions for single attacks.
data Player = Player { name :: String,
attacks :: [Attack],
-- ^ All the player's melee attacks (only one element
-- unless the player has certain abilities).
evasion :: Int,
-- ^ Evasion score.
protDice :: [Dice],
-- ^ All the protection dice that the player has cu
lightRadius :: Int,
activeSongs :: Singing,
-- ^ Songs currently sung by the player.
resistances :: ResistanceMap,
abilities :: [Ability],
equipment :: EquipmentMap,
will :: Int,
song :: Int,
stealth :: Int,
onLitSquare :: Bool -- ^Standing on a lit square?
} deriving (Show)
blankPlayer = Player { name = "",
attacks = [],
evasion = 0,
protDice = [],
lightRadius = 0,
activeSongs = Quiet,
resistances = T.noResistance,
abilities = [],
equipment = Map.empty,
will = 0,
song = 0,
stealth = 0,
onLitSquare = False }
-- | Checks whether the player is using a one-handed weapon with an
-- empty off-hand (matters for the Subtlety ability).
fightingOneHanded :: Player -> Bool
fightingOneHanded p = offHandIsEmpty && isOneHanded mainHand
where
eq = equipment p
offHandIsEmpty = isNothing $ eq Map.! T.OffHand
mainHand = eq Map.! T.MainHand
isOneHanded Nothing = False
isOneHanded (Just (Equippable _ (Weapon {wpHandedness = h})))
= T.OneHanded == h
isOneHanded (Just (Equippable _ Other))
= error "mainhand contains non-weapon" -- shouldn't happen
-- isOneHanded (Just (Equippable _ (Weapon w))) = T.OneHanded == T.wpHandedness w
-- | Sets the player's activeSongs to the parameter.
singing :: Singing -> Player -> Player
singing singing' p = p {activeSongs = singing'}
-- | Gives the player additional abilities.
withAbilities :: [T.Ability] -> Player -> Player
withAbilities as p = p {abilities = (abilities p) ++ as}
-- | Checks whether the given song is currently being sung.
isActiveSong :: Song -> Player -> Bool
isActiveSong song' player =
case (activeSongs player) of
Quiet -> False
OneSong s -> song' == s
WovenThemes (s1, s2) -> (s1 == song' || s2 == song')
-- | Does the player have this ability?
hasAbility :: Player -> Ability -> Bool
hasAbility p a = a `elem` (abilities p)
-- | Light strength on player's square (not including effects from
--the environment or monsters)
playerLight :: Player -> Int
playerLight player = (lightRadius player) + 1 + innerLight
where
innerLight = if InnerLight `elem` (abilities player)
then 1 else 0
-- Apply effects of various songs; should actually maybe be in
-- CombatState.hs, since these are really only used there.
applySongTrees :: Player -> Player
applySongTrees p =
let extraLight = (getSongValue SongTrees p) `quot` 5
light = lightRadius p
in p {lightRadius = light + extraLight}
applySongStay :: Player -> Player
applySongStay p =
let extraProtSides = (getSongValue SongStay p) `quot` 3
newProt = (1 `d` extraProtSides) : (protDice p)
extraWill = extraProtSides
newWill = (will p) + extraWill
in p {protDice = newProt, will = newWill}
applySongSharp :: Player -> Player
applySongSharp p =
let sharpnessBonus = max 6 $ getSongValue SongSharp p
extraSharpness = 6.0 / (fromIntegral sharpnessBonus)
newAttacks = map multiplySharpness (attacks p) :: [Attack]
multiplySharpness a =
a {T.sharpness = (T.sharpness a) * extraSharpness}
in p {attacks = newAttacks}
dungeonLight :: Player -> Int
dungeonLight p = if (onLitSquare p) then 1 else 0
modifyEvasionWith :: (Int -> Int) -> Player -> Player
modifyEvasionWith func player = player {evasion = func $ evasion player}
modifyAccuracyWith :: (Int -> Int) -> Player -> Player
modifyAccuracyWith func player =
let newAttacks = Prelude.map (adjustAccuracy func) (attacks player)
adjustAccuracy func attack = attack { accuracy = func $ accuracy attack}
in player {attacks = newAttacks}
modifyCritThreshold :: (Double -> Double) -> Player -> Player
modifyCritThreshold func player =
let newAttacks = Prelude.map (adjustCritThres func) (attacks player)
adjustCritThres f a = a { critThreshold = f $ critThreshold a }
in player {attacks = newAttacks}
-- | Returns the effective song strength for the given Song:
--the player's song score if the player is currently singing that song
--(or song score - 5 if the song is a minor theme) and 0 otherwise.
getSongValue :: Song -> Player -> Int
getSongValue song' player =
case (activeSongs player) of
Quiet -> 0
OneSong s ->
if song' == s then (song player) else 0
WovenThemes (s1, s2)
| s1 == song' -> max 0 $ song player
| s2 == song' -> max 0 $ (song player) - 5
| otherwise -> 0
-- | Calculates a player's critical threshold modifiers from abilities
-- (Power, Finesse and Subtlety). The weapon's contribution is computed
-- elsewhere.
playerCritMods :: Player -> Double
playerCritMods player =
let finesse = if player `hasAbility` T.Finesse
then -1.0
else 0
subtlety = if player `hasAbility` T.Subtlety
&& fightingOneHanded player
then -2.0
else 0
power = if player `hasAbility` T.Power
then 1.0
else 0
in finesse + subtlety + power