-
Notifications
You must be signed in to change notification settings - Fork 3
/
Update.elm
170 lines (150 loc) · 5.7 KB
/
Update.elm
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
module Update exposing (..)
import Msg exposing (Msg(..))
import Model exposing (..)
import Types exposing (Block(..), Pos)
import Definitions exposing (..)
type WalkStatus
= Won
| Walked Pos (List Pos)
| Fail
update : Msg -> Model -> (Model, Cmd Msg)
update msg model =
case msg of
Nop ->
model ! []
PlayerAnimation ->
{ model | player_animation = not model.player_animation } ! []
Menu ->
{ model | state = GSMenu } ! []
Tick t ->
{ model
| time = t
, levels = -- fail animation when try to enter in an unlocked level
mapWhere (.failStep >> (/=) 0) (\lvl ->
{ lvl | failStep = -lvl.failStep + clamp -1 1 lvl.failStep })
model.levels
} ! []
Enter ->
if model.state /= GSMenu then
model ! []
else
let
m_levelInfo =
model.levels
|> List.drop (model.selected - 1)
|> List.head
in
case m_levelInfo of
Nothing ->
model ! []
Just levelInfo ->
if levelInfo.unlocked then
{ model
| state = GSPlaying
, timeStart = model.time
, current = levelInfo.level
} ! []
else
{ model | levels =
mapWhere (\lvl -> lvl.id == model.selected)
(\lvl -> { lvl | failStep = 4 })
model.levels
} ! []
Arrows offsetXY ->
if model.state == GSMenu then
{ model
| selected =
fixBetween
(model.selected + (arrowToIconId offsetXY))
(1, List.length model.levels)
} ! []
else
case walk offsetXY model.current of
Won ->
{ model | state = GSMenu, levels = updateLevels model } ! []
Walked pos boxes ->
{ model
| current = let current = model.current in
{ current
| player = pos
, boxes = boxes
, steps = model.current.steps + 1
}
} ! []
Fail ->
model ! []
RestartLevel ->
let
current =
model.levels
|> List.filter (.id >> (==) model.selected)
|> List.head
|> Maybe.map .level
|> Maybe.withDefault level_error
in
{ model
| time = model.time
, timeStart = model.time
, current = current
} ! []
moveBoxes : Pos -> Pos -> List Pos -> List Tile -> Maybe (List Pos)
moveBoxes player_pos (dx, dy) boxes tiles =
let
cannot_walk =
tiles
|> List.filter (.pos >> (==) player_pos)
|> List.head
|> Maybe.map (.block >> (/=) BFloor)
|> Maybe.withDefault True
boxes_ =
boxes
|> mapWhere ((==) player_pos) (\(bx, by) -> (bx + dx, by + dy))
|> List.indexedMap (\i n -> (i + 1, n))
collided_with_box =
flip List.any boxes_ (\(i, bpos) ->
flip List.any boxes_ (\(i2, bpos2) -> i /= i2 && bpos == bpos2))
collided_with_wall =
flip List.any boxes_ (\(_, bpos) ->
flip List.any tiles (\t -> t.pos == bpos && t.block == BWall))
in
if cannot_walk || collided_with_box || collided_with_wall then
Nothing
else
Just <| List.map Tuple.second boxes_
walk : Pos -> Level -> WalkStatus
walk (x, y) level =
let
-- new position
pos =
(\(x_, y_) -> (x + x_, y + y_)) level.player
-- `Maybe Pos` with the list of the new box positions, if not collided
boxes_ =
moveBoxes pos (x, y) level.boxes level.tiles
-- Check if all the boxes are placed correctly
won =
boxes_
|> Maybe.withDefault level.boxes
|> List.all (\box -> List.any ((==) box) level.holes)
in
if won then
Won
else
boxes_
|> Maybe.map (Walked pos)
|> Maybe.withDefault Fail
updateLevels : Model -> List LevelInfo
updateLevels model =
model.levels
|> mapWhere -- set best step and best time to the current level
(.id >> (==) model.selected)
(\lvl ->
{ lvl
| beat = True
, bestStep = Basics.min lvl.bestStep (model.current.steps + 1)
, bestTime =
model.time - model.timeStart
|> flip (/) 100 >> round >> toFloat >> flip (/) 10
|> Basics.min lvl.bestTime })
|> mapWhere -- unlock next level
(.id >> (==) (model.selected + 1))
(\lvl -> { lvl | unlocked = True })