2022-12-05 08:13:26 -08:00
|
|
|
module Main (main) where
|
2022-11-30 13:38:12 -08:00
|
|
|
|
2022-12-02 21:33:44 -08:00
|
|
|
import Control.Exception ( bracket )
|
2022-11-30 13:38:12 -08:00
|
|
|
import Data.List.NonEmpty (NonEmpty(..))
|
|
|
|
import Data.List.NonEmpty qualified as NonEmpty
|
2022-12-04 19:55:30 -08:00
|
|
|
import Data.Map (Map)
|
|
|
|
import Data.Map qualified as Map
|
2022-12-05 08:13:26 -08:00
|
|
|
import Graphics.Vty
|
|
|
|
import System.Environment ( getArgs )
|
2022-12-05 21:15:18 -08:00
|
|
|
import System.Directory ( listDirectory )
|
|
|
|
import System.FilePath ( (</>), takeBaseName )
|
2022-11-30 13:38:12 -08:00
|
|
|
|
2022-12-02 10:54:31 -08:00
|
|
|
import Model
|
2022-12-02 21:33:44 -08:00
|
|
|
import Rendering ( render )
|
2022-12-04 16:45:15 -08:00
|
|
|
import Parser (parse)
|
2022-11-30 16:44:30 -08:00
|
|
|
|
2022-12-03 11:50:06 -08:00
|
|
|
data Game = Game {
|
2022-12-04 19:55:30 -08:00
|
|
|
gameFlat :: Bool,
|
|
|
|
gameMode :: GameMode,
|
|
|
|
gameSelect :: Int
|
2022-12-03 11:50:06 -08:00
|
|
|
}
|
2022-11-30 13:38:12 -08:00
|
|
|
|
2022-12-04 19:55:30 -08:00
|
|
|
data GameMode
|
|
|
|
= MenuMode
|
|
|
|
| PlayMode (NonEmpty World)
|
|
|
|
|
2022-12-05 17:00:19 -08:00
|
|
|
getWorldList :: IO (Map String FilePath)
|
|
|
|
getWorldList =
|
|
|
|
do paths <- listDirectory "levels"
|
|
|
|
pure (Map.fromList [(takeBaseName path, "levels" </> path) | path <- paths])
|
|
|
|
|
2022-11-30 13:38:12 -08:00
|
|
|
main :: IO ()
|
|
|
|
main =
|
2022-12-02 20:53:45 -08:00
|
|
|
do args <- getArgs
|
2022-12-05 17:00:19 -08:00
|
|
|
worldList <- getWorldList
|
2022-12-04 19:55:30 -08:00
|
|
|
bracket (mkVty =<< userConfig) shutdown \vty ->
|
|
|
|
case args of
|
2022-12-05 17:00:19 -08:00
|
|
|
x:_ | Just path <- Map.lookup x worldList ->
|
|
|
|
do w <- parse <$> readFile path
|
|
|
|
loop vty Game {
|
|
|
|
gameMode = PlayMode (pure w),
|
|
|
|
gameFlat = True,
|
|
|
|
gameSelect = Map.findIndex x worldList
|
|
|
|
}
|
2022-12-04 19:55:30 -08:00
|
|
|
_ ->
|
|
|
|
loop vty Game {
|
|
|
|
gameMode = MenuMode,
|
|
|
|
gameFlat = True,
|
|
|
|
gameSelect = 0
|
|
|
|
}
|
2022-11-30 13:38:12 -08:00
|
|
|
|
2022-12-03 11:50:06 -08:00
|
|
|
loop :: Vty -> Game -> IO ()
|
|
|
|
loop vty game =
|
2022-12-04 19:55:30 -08:00
|
|
|
case gameMode game of
|
|
|
|
MenuMode ->
|
2022-12-05 17:00:19 -08:00
|
|
|
do worldList <- getWorldList
|
|
|
|
let (a,b) = Map.splitAt (gameSelect game) worldList
|
2022-12-04 19:55:30 -08:00
|
|
|
bnds <- displayBounds (outputIface vty)
|
|
|
|
update vty (picForImage (renderMenu bnds a b))
|
|
|
|
ev <- nextEvent vty
|
|
|
|
case ev of
|
|
|
|
EvKey key _modifier ->
|
|
|
|
case key of
|
|
|
|
KEsc -> pure ()
|
|
|
|
KUp | gameSelect game > 0 -> loop vty game{ gameSelect = gameSelect game - 1 }
|
|
|
|
KDown | gameSelect game + 1 < Map.size worldList -> loop vty game{ gameSelect = gameSelect game + 1 }
|
2022-12-05 17:00:19 -08:00
|
|
|
KEnter | Just (path,_) <- Map.minView b ->
|
|
|
|
do world <- parse <$> readFile path
|
|
|
|
loop vty game{ gameMode = PlayMode (pure world) }
|
2022-12-04 19:55:30 -08:00
|
|
|
_ -> loop vty game
|
|
|
|
_ -> loop vty game
|
|
|
|
|
|
|
|
PlayMode worlds ->
|
|
|
|
do let world = NonEmpty.head worlds
|
|
|
|
update vty (render (gameFlat game) (NonEmpty.head worlds))
|
|
|
|
ev <- nextEvent vty
|
|
|
|
case ev of
|
|
|
|
EvKey key _modifier ->
|
|
|
|
case key of
|
|
|
|
KUp -> loop vty game{ gameMode = PlayMode (NonEmpty.cons (move world (-1,0)) worlds) }
|
|
|
|
KDown -> loop vty game{ gameMode = PlayMode (NonEmpty.cons (move world (1,0) ) worlds) }
|
|
|
|
KLeft -> loop vty game{ gameMode = PlayMode (NonEmpty.cons (move world (0,-1)) worlds) }
|
|
|
|
KRight -> loop vty game{ gameMode = PlayMode (NonEmpty.cons (move world (0,1) ) worlds) }
|
|
|
|
KChar 'm' -> loop vty game { gameMode = MenuMode }
|
|
|
|
KChar 'r' -> loop vty game{ gameMode = PlayMode (pure (NonEmpty.last worlds)) }
|
|
|
|
KChar 'z'
|
|
|
|
| Just worlds' <- NonEmpty.nonEmpty (NonEmpty.tail worlds) ->
|
|
|
|
loop vty game{ gameMode = PlayMode worlds' }
|
|
|
|
KEsc -> pure ()
|
|
|
|
KChar 'f' -> loop vty game{ gameFlat = not (gameFlat game) }
|
|
|
|
_ -> loop vty game
|
2022-12-03 11:50:06 -08:00
|
|
|
_ -> loop vty game
|
2022-12-04 19:55:30 -08:00
|
|
|
|
|
|
|
|
2022-12-05 17:00:19 -08:00
|
|
|
renderMenu :: DisplayRegion -> Map String a -> Map String a -> Image
|
2022-12-04 19:55:30 -08:00
|
|
|
renderMenu (w,h) before after =
|
|
|
|
pad ((w - imageWidth menu) `div` 2) 0 0 0 menu
|
|
|
|
where
|
|
|
|
menu =
|
|
|
|
case Map.minViewWithKey after of
|
|
|
|
Nothing -> string defAttr "empty menu"
|
|
|
|
Just ((k,_),after') ->
|
|
|
|
let len1 = (h-1)`div`2 in
|
|
|
|
pad 0 (len1 - Map.size before) 0 0 (vertCat
|
|
|
|
[ string defAttr x
|
|
|
|
| x <- drop (Map.size before - len1) (Map.keys before)
|
|
|
|
]) <->
|
|
|
|
string (defAttr `withBackColor` cyan `withForeColor` white) k
|
|
|
|
<->
|
|
|
|
vertCat
|
|
|
|
[ string defAttr x
|
|
|
|
| x <- drop (Map.size after' - h`div`2) (Map.keys after')
|
|
|
|
]
|