-
Notifications
You must be signed in to change notification settings - Fork 2
/
ReMario.elm
231 lines (192 loc) · 7.9 KB
/
ReMario.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
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
import Keyboard
import Window
import Debug
import Graphics.Element exposing (..)
import Graphics.Collage exposing (..)
import Color exposing (..)
import Time exposing (fps,second)
import Signal
import Signal.Time
import Signal.Extra
import List exposing (..)
-- App imports
import Generic exposing (..)
-- MODEL
type alias World = List Sprite
type alias History = List (Float,Keys)
type alias Past = List BasicSprite
type alias Behavior = (Float, Keys) -> World -> PlatformSprite -> PlatformSprite
type Sprite = Sky | Platform PlatformSprite Behavior | Cloud PlatformSprite Behavior | Player BasicSprite History Past | Ghost BasicSprite History History
type alias Common a =
{ a |
x : Float
, y : Float
, w : Float
, h : Float
, vx : Float
, vy : Float
}
type alias BasicSprite =
Common { dir : Direction }
type alias PlatformSprite =
Common { t : Float , c : Color }
type Direction = Left | Right
type alias Keys = { x:Int, y:Int }
column common = (common.x-common.w/2,common.x+common.w/2)
row common = (common.y,common.y+common.h)
feet common = (common.y+common.h-2,common.y+common.h)
hair common = (common.y,common.y+2)
start_mario = { x = 0 , y = 0 , w = 16 , h = 26 , vx = 0 , vy = 0 , dir = Right }
pink = rgb 150 10 50
start_state : World
start_state = [
Player start_mario [] [],
Platform { x = -55 , y = 80 , w = 20 , h = 20 , c = red , t = 0, vx = 0, vy = 0} nothing,
Platform { x = -80 , y = 60 , w = 20 , h = 20 , c = red , t = 0, vx = 0, vy = 0} nothing,
Cloud { x = 0 , y = 70 , w = 20 , h = 20 , c = red , t = 0, vx = 0, vy = 0} sway,
Platform { x = -80 , y = 60 , w = 20 , h = 20 , c = red , t = 0, vx = 0, vy = 0} nothing,
Platform { x = 40 , y = 0 , w = 20 , h = 20 , c = blue , t = 0, vx = 0, vy = 0} nothing,
Platform { x = 40 , y = 160 , w = 20 , h = 20 , c = green , t = 0, vx = 0, vy = 0} nothing,
Cloud { x = -40 , y = 120 , w = 20 , h = 20 , c = purple , t = 0, vx = 0, vy = 0} lift,
Platform { x = -60 , y = 26 , w = 20 , h = 4 , c = blue , t = 0, vx = 0, vy = 0} change,
-- the floor
Platform { x = 0 , y = -50 , w = 9999 , h = 50 , c = rgb 74 167 43 , t = 0, vx = 0, vy = 0} nothing,
Sky ]
-- UPDATE
type Update = Rewind Bool | Spawn Bool | Move (Float, Keys)
step : Update -> World -> World
step u (player :: rest) =
let ghost one = case one of
Player _ history _ -> let backward = reverse history in Ghost start_mario backward backward
_ -> one
rewind one = case one of
Player sprite (h::hs) (x::xs) -> Player x hs xs
_ -> one
reset one = case one of
Player _ _ _ -> Player start_mario [] []
Ghost _ history copy -> Ghost start_mario copy copy
_ -> one
world = player :: rest
in
case u of
Spawn True -> reset player :: ghost player :: map reset rest
Rewind True -> rewind player :: rest
Move move -> mapAllBut (stepOne move) world
_ -> world
nothing _ _ sprite = sprite
sway (dt,_) world sprite = {sprite | t <- sprite.t+dt, x <- sprite.x + dt * sprite.vx, vx <- sin(sprite.t/50)}
lift (dt,_) world sprite = {sprite | t <- sprite.t+dt, y <- sprite.y + dt * sprite.vy, vy <- sin(sprite.t/50)}
change fff world sprite =
let t = Debug.watch "time" sprite.t
in if sprite.t > 100 then lift fff world sprite else sway fff world sprite
stepOne : (Float, Keys) -> World -> Sprite -> Sprite
stepOne move world sprite = case sprite of
Player sprite' h past -> Player (stepPlayer move world sprite') (move :: h) (sprite' :: past)
Ghost sprite' [] copy -> let (dt,keys) = move in Ghost (stepPlayer (dt,{x=0,y=0}) world sprite') [] copy
Ghost sprite' (h::hs) copy -> Ghost (stepPlayer h world sprite') hs copy
Platform sprite' behavior -> Platform (behavior move world sprite') behavior
Cloud sprite' behavior -> Cloud (behavior move world sprite') behavior
_ -> sprite
stepPlayer : (Float, Keys) -> World -> BasicSprite -> BasicSprite
stepPlayer (dt, keys) world mario =
let n = 6 in
mario
|> jump keys
|> walk keys
|> iterate (physics (dt/n) world) n
jump : Keys -> BasicSprite -> BasicSprite
jump keys mario=
if keys.y > 0 && mario.vy == 0 then { mario | vy <- 4.0} else mario
walk : Keys -> BasicSprite -> BasicSprite
walk keys mario =
{ mario |
vx <- toFloat keys.x,
dir <- if | keys.x < 0 -> Left
| keys.x > 0 -> Right
| otherwise -> mario.dir
}
blocks mario p =
case p of
Platform g _ -> intersects (column g) (column mario) && intersects (row g) (row mario)
Cloud g _ -> intersects (column g) (column mario) && intersects (feet g) (hair mario) && mario.vy <= 0
Ghost g _ _ -> intersects (column g) (column mario) && intersects (feet g) (hair mario) && mario.vy <= 0
Player g _ _ -> intersects (column g) (column mario) && intersects (feet g) (hair mario) && mario.vy <= 0
_ -> False
physics : Float -> World -> BasicSprite -> BasicSprite
physics dt world mario =
let newx = mario.x + dt * mario.vx
newy = mario.y + dt * (mario.vy - 0.01) -- fudge factor to "test" our feet
support = filter (blocks {mario | y <- newy}) world
newvy = if (isEmpty support) then mario.vy - dt/8 else 0
extrax = if (isEmpty support) || (mario.vx /= 0) then 0 else
case head support of
Just (Player pl _ _) -> dt * pl.vx
Just (Platform pl _) -> dt * pl.vx
Just (Cloud pl _) -> dt * pl.vx
_ -> 0
extray = if (isEmpty support) || (mario.vy /= 0) then 0 else
case head support of
Just (Player pl _ _) -> dt * pl.vy
Just (Platform pl _) -> dt * pl.vy
Just (Cloud pl _) -> dt * pl.vy
_ -> 0
blockers = filter (blocks {mario | x <- newx + extrax}) world
in
{ mario |
x <- if (isEmpty blockers) then newx+extrax else mario.x,
y <- if (isEmpty support) then newy else mario.y+extray,
vy <- newvy
}
-- DISPLAY
display : (Int, Int) -> World -> Element
display (w',h') world =
let (w,h) = (toFloat w', toFloat h')
in collage w' h' (map (displayOne (w,h)) (reverse world))
displayOne : (Float, Float) -> Sprite -> Form
displayOne dims sprite = case sprite of
Player shape _ _ -> Debug.trace "mario" <| displayPlayer dims "mario" (Debug.watch "mario" shape)
Ghost shape _ _ -> displayPlayer dims "ghost" shape
Platform shape _ -> displayPlatform dims shape
Cloud shape _ -> displayPlatform dims shape
Sky -> displaySky dims
displayPlayer : (Float, Float) -> String -> BasicSprite -> Form
displayPlayer dims who mario =
let verb = if | (abs mario.vy) > 0 -> "jump"
| mario.vx /= 0 -> "walk"
| otherwise -> "stand"
dir = case mario.dir of
Left -> "left"
Right -> "right"
src = "imgs/" ++ who ++ "/"++ verb ++ "/" ++ dir ++ ".gif"
marioImage = image 35 35 src
in
marioImage
|> toForm
|> move (displayCoords dims mario)
displayPlatform : (Float, Float) -> PlatformSprite -> Form
displayPlatform (w,h) pl =
let pw = min w pl.w
ph = min h pl.h
in
rect pw ph
|> filled pl.c
|> move (displayCoords (w,h) pl)
displaySky : (Float, Float) -> Form
displaySky (w,h) =
rect w h |> filled (rgb 174 238 238)
displayCoords (w,h) common = let base = 50 in (common.x, common.y + common.h/2 + base - h/2)
-- SIGNALS
main : Signal Element
main =
let states = Signal.foldp step start_state input
in
Signal.map2 display Window.dimensions states
input : Signal Update
input =
let delta = Signal.map (\t -> t/20) (fps 30)
deltaArrows = Signal.map2 (,) delta Keyboard.arrows
moves = Signal.map Move (Signal.sampleOn delta deltaArrows)
spawns = Signal.map Spawn (Signal.Time.dropWithin second Keyboard.space)
rewind = Signal.map Rewind (Signal.Extra.keepWhen Keyboard.shift True (Signal.sampleOn delta Keyboard.shift))
in
Signal.mergeMany [rewind, spawns, moves]