{-# LANGUAGE CPP #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE RecordWildCards #-}
{-# LANGUAGE MultiWayIf #-}
{-# LANGUAGE BangPatterns #-}
module Main where
import Data.Bool
import Data.Function
import qualified Data.Map as M
import Data.Monoid
import Miso
import Miso.String
#ifdef IOS
import Language.Javascript.JSaddle.WKWebView as JSaddle
runApp :: JSM () -> IO ()
runApp = JSaddle.run
#else
import qualified Language.Javascript.JSaddle.Warp as JSaddle
#ifdef ghcjs_HOST_OS
runApp :: JSM () -> IO ()
runApp = JSaddle.run 8080
#else
import Network.Wai.Application.Static
import qualified Network.Wai as Wai
import qualified Network.Wai.Handler.Warp as Warp
import Network.WebSockets
runApp :: JSM () -> IO ()
runApp f =
Warp.runSettings (Warp.setPort 8080 (Warp.setTimeout 3600 Warp.defaultSettings)) =<<
JSaddle.jsaddleOr defaultConnectionOptions (f >> syncPoint) app
where app req sendResp =
case Wai.pathInfo req of
("imgs" : _) -> staticApp (defaultWebAppSettings "examples/mario") req sendResp
_ -> JSaddle.jsaddleApp req sendResp
#endif
#endif
data Action
= GetArrows !Arrows
| Time !Double
| WindowCoords !(Int,Int)
| NoOp
spriteFrames :: [MisoString]
spriteFrames = ["0 0", "-74px 0","-111px 0","-148px 0","-185px 0","-222px 0","-259px 0","-296px 0"]
main :: IO ()
main = runApp $ do
time <- now
let m = mario { time = time }
startApp App { model = m
, initialAction = NoOp
, ..
}
where
update = updateMario
view = display
events = defaultEvents
subs = [ arrowsSub GetArrows
, windowCoordsSub WindowCoords
]
mountPoint = Nothing
data Model = Model
{ x :: !Double
, y :: !Double
, vx :: !Double
, vy :: !Double
, dir :: !Direction
, time :: !Double
, delta :: !Double
, arrows :: !Arrows
, window :: !(Int,Int)
} deriving (Show, Eq)
data Direction
= L
| R
deriving (Show,Eq)
mario :: Model
mario = Model
{ x = 0
, y = 0
, vx = 0
, vy = 0
, dir = R
, time = 0
, delta = 0
, arrows = Arrows 0 0
, window = (0,0)
}
updateMario :: Action -> Model -> Effect Action Model
updateMario NoOp m = step m
updateMario (GetArrows arrs) m = noEff newModel
where
newModel = m { arrows = arrs }
updateMario (Time newTime) m = step newModel
where
newModel = m { delta = (newTime - time m) / 20
, time = newTime
}
updateMario (WindowCoords coords) m = noEff newModel
where
newModel = m { window = coords }
step :: Model -> Effect Action Model
step m@Model{..} = k <# do Time <$> now
where
k = m & gravity delta
& jump arrows
& walk arrows
& physics delta
jump :: Arrows -> Model -> Model
jump Arrows{..} m@Model{..} =
if arrowY > 0 && vy == 0
then m { vy = 6 }
else m
gravity :: Double -> Model -> Model
gravity dt m@Model{..} =
m { vy = if y > 0 then vy - (dt / 4) else 0 }
physics :: Double -> Model -> Model
physics dt m@Model{..} =
m { x = x + dt * vx
, y = max 0 (y + dt * vy)
}
walk :: Arrows -> Model -> Model
walk Arrows{..} m@Model{..} =
m { vx = fromIntegral arrowX
, dir = if | arrowX < 0 -> L
| arrowX > 0 -> R
| otherwise -> dir
}
display :: Model -> View action
display m@Model{..} = marioImage
where
(h,w) = window
groundY = 62 - (fromIntegral (fst window) / 2)
marioImage =
div_ [ height_ $ ms h
, width_ $ ms w
]
[ nodeHtml "style" [] ["@keyframes play { 100% { background-position: -296px; } }"]
, div_ [ style_ (marioStyle m groundY) ] []
]
marioStyle :: Model -> Double -> M.Map MisoString MisoString
marioStyle Model {..} gy =
M.fromList [ ("transform", matrix dir x $ abs (y + gy) )
, ("display", "block")
, ("width", "37px")
, ("height", "37px")
, ("background-color", "transparent")
, ("background-image", "url(imgs/mario.png)")
, ("background-repeat", "no-repeat")
, ("background-position", spriteFrames !! frame)
, bool mempty ("animation", "play 0.8s steps(8) infinite") (y == 0 && vx /= 0)
]
where
frame | y > 0 = 1
| otherwise = 0
matrix :: Direction -> Double -> Double -> MisoString
matrix dir x y =
"matrix("
<> (if dir == L then "-1" else "1")
<> ",0,0,1,"
<> ms x
<> ","
<> ms y
<> ")"