diff --git a/app/Main.hs b/app/Main.hs index 0321876..20d6f72 100644 --- a/app/Main.hs +++ b/app/Main.hs @@ -7,52 +7,101 @@ import Data.List.NonEmpty (NonEmpty(..)) import Data.List.NonEmpty qualified as NonEmpty import Data.Set qualified as Set import System.Environment ( getArgs ) +import Data.Map (Map) +import Data.Map qualified as Map import Model import Rendering ( render ) import Parser (parse) data Game = Game { - gameWorlds :: NonEmpty World, - gameFlat :: Bool + gameFlat :: Bool, + gameMode :: GameMode, + gameSelect :: Int } +data GameMode + = MenuMode + | PlayMode (NonEmpty World) + main :: IO () main = do args <- getArgs - case args of - x:_ | Just w <- Map.lookup x worldList -> - bracket (mkVty =<< userConfig) shutdown \vty -> + bracket (mkVty =<< userConfig) shutdown \vty -> + case args of + x:_ | Just w <- Map.lookup x worldList -> loop vty Game { - gameWorlds = pure w, - gameFlat = True + gameMode = PlayMode (pure w), + gameFlat = True, + gameSelect = Map.findIndex x worldList + } - _ -> - do putStrLn "Usage: parabox " - putStrLn "" - putStrLn "Available worlds:" - mapM_ putStrLn (Map.keys worldList) + _ -> + loop vty Game { + gameMode = MenuMode, + gameFlat = True, + gameSelect = 0 + } loop :: Vty -> Game -> IO () loop vty game = - do let world = NonEmpty.head (gameWorlds game) - update vty (render (gameFlat game) (NonEmpty.head (gameWorlds game))) - ev <- nextEvent vty - case ev of - EvKey key _modifier -> - case key of - KUp -> loop vty game{ gameWorlds = NonEmpty.cons (move world (-1,0)) (gameWorlds game) } - KDown -> loop vty game{ gameWorlds = NonEmpty.cons (move world (1,0) ) (gameWorlds game) } - KLeft -> loop vty game{ gameWorlds = NonEmpty.cons (move world (0,-1)) (gameWorlds game) } - KRight -> loop vty game{ gameWorlds = NonEmpty.cons (move world (0,1) ) (gameWorlds game) } - KChar 'r' -> loop vty game{ gameWorlds = pure (NonEmpty.last (gameWorlds game)) } - KChar 'z' - | Just worlds <- NonEmpty.nonEmpty (NonEmpty.tail (gameWorlds game)) -> - loop vty game{ gameWorlds = worlds } - KEsc -> pure () - KChar 'f' -> loop vty game{ gameFlat = not (gameFlat game) } + case gameMode game of + MenuMode -> + do let (a,b) = Map.splitAt (gameSelect game) worldList + 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 } + KEnter | Just (world,_) <- Map.minView b -> loop vty game{ gameMode = PlayMode (pure world) } + _ -> loop vty game _ -> 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 + _ -> loop vty game + + +renderMenu :: DisplayRegion -> Map String World -> Map String World -> Image +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') + ] worldList :: Map.Map String World @@ -63,6 +112,9 @@ worldList = Map.fromList , ("center13", center13) , ("clone11", clone11) , ("transfer14", transfer14) + , ("transfer20", transfer20) + , ("transfer26", transfer26) + , ("transfer27", transfer27) , ("open4", open4) , ("cycle10", cycle10) , ("player10", player10) @@ -1267,4 +1319,213 @@ multiInfinite8 = parse \▓▓▓▓▓▓▓▓▓\n\ \▓▓▓▓▓▓▓▓▓\n\ \▓▓▓▓▓▓▓▓▓\n\ + \▓▓▓▓▓▓▓▓▓\n" + + +transfer20 :: World +transfer20 = parse + "player p\n\ + \block t blue boring\n\ + \▓▓▓▓▓▓▓▓▓\n\ + \▓▓▓▓▓▓▓▓▓\n\ + \▓▓▓▓▓▓▓▓▓\n\ + \▓▓▓▓▓▓▓▓▓\n\ + \▓▓▓▓t▓a▓▓\n\ + \▓▓▓▓▓▓▓▓▓\n\ + \▓▓▓▓▓▓▓▓▓\n\ + \▓▓▓▓▓▓▓▓▓\n\ + \▓▓▓▓▓▓▓▓▓\n\ + \block a blue interesting\n\ + \▓▓▓▓▓▓▓▓▓\n\ + \▓ ▓ ▓\n\ + \▓ ▓\n\ + \▓ ABC ▓\n\ + \▓ p ▓\n\ + \▓ XYZ ▓\n\ + \▓ ▓\n\ + \▓ ▓ ▓\n\ + \▓▓▓▓▓▓▓▓▓\n\ + \link A C cyan\n\ + \link B C cyan\n\ + \block C cyan interesting\n\ + \▓▓▓▓▓ ▓ ▓\n\ + \▓▓▓▓▓ ▓\n\ + \▓▓▓▓▓▓▓▓▓\n\ + \▓▓▓▓▓▓▓▓▓\n\ + \ ▓▓▓▓▓▓▓\n\ + \▓ ▓▓▓▓▓▓▓\n\ + \▓ ▓▓▓▓▓▓▓\n\ + \▓ ▓▓▓▓▓=▓\n\ + \▓ ▓▓▓▓▓ ▓\n\ + \link X Z green\n\ + \link Y Z green\n\ + \block Z green interesting\n\ + \▓ ▓ ▓▓▓▓▓\n\ + \▓ ▓▓▓▓▓\n\ + \▓▓▓▓▓▓▓▓▓\n\ + \▓▓▓▓▓▓▓▓▓\n\ + \▓▓▓▓▓▓▓▓▓\n\ + \▓▓▓▓▓▓▓▓▓\n\ + \▓▓▓▓▓▓▓▓▓\n\ + \▓▓▓ ▓▓▓\n\ + \▓▓▓ ▓ ▓▓▓\n\ + \block p magenta boring\n\ + \▓▓▓▓▓▓▓▓▓\n\ + \▓▓▓▓▓▓▓▓▓\n\ + \▓▓ ▓▓▓ ▓▓\n\ + \▓▓▓▓▓▓▓▓▓\n\ + \▓▓▓▓▓▓▓▓▓\n\ + \▓▓▓▓▓▓▓▓▓\n\ + \▓▓▓▓▓▓▓▓▓\n\ + \▓▓▓▓▓▓▓▓▓\n\ + \▓▓▓▓▓▓▓▓▓\n" + +transfer26 :: World +transfer26 = parse + "player p\n\ + \block t blue boring\n\ + \▓▓▓▓▓▓▓▓▓\n\ + \▓▓▓▓▓▓▓▓▓\n\ + \▓▓▓▓▓▓▓▓▓\n\ + \▓▓▓▓▓▓▓▓▓\n\ + \▓▓▓▓t▓a▓▓\n\ + \▓▓▓▓▓▓▓▓▓\n\ + \▓▓▓▓▓▓▓▓▓\n\ + \▓▓▓▓▓▓▓▓▓\n\ + \▓▓▓▓▓▓▓▓▓\n\ + \block a blue interesting\n\ + \▓▓▓▓▓▓▓▓▓\n\ + \▓▓▓▓▓▓▓▓▓\n\ + \▓▓ ▓▓\n\ + \▓ ▓\n\ + \▓ T x 1 ▓\n\ + \▓ ▓\n\ + \▓ p 3 2 ▓\n\ + \▓ ▓\n\ + \▓▓▓▓▓▓▓▓▓\n\ + \block x yellow boring\n\ + \▓▓▓▓▓▓▓▓▓\n\ + \▓▓▓▓▓▓▓▓▓\n\ + \▓▓▓▓▓▓▓▓▓\n\ + \▓▓▓▓▓▓▓▓▓\n\ + \▓▓▓▓▓▓▓▓▓\n\ + \▓▓▓▓▓▓▓▓▓\n\ + \▓▓▓▓▓▓▓▓▓\n\ + \▓▓▓▓▓▓▓▓▓\n\ + \▓▓▓▓▓▓▓▓▓\n\ + \block 1 cyan interesting\n\ + \▓▓▓▓▓▓▓▓▓\n\ + \▓▓▓▓▓▓▓▓▓\n\ + \▓▓▓▓▓▓ ▓▓\n\ + \ ▓▓ ▓▓\n\ + \ ▓▓ ▓▓\n\ + \ ▓▓ ▓▓\n\ + \▓▓▓▓▓▓ ▓▓\n\ + \▓▓▓▓▓▓▓▓▓\n\ + \▓▓▓▓▓▓▓▓▓\n\ + \block 2 cyan interesting\n\ + \▓▓▓▓▓▓▓▓▓\n\ + \▓▓▓▓▓▓▓▓▓\n\ + \▓▓▓▓▓▓ ▓\n\ + \ ▓▓▓ ▓\n\ + \ ▓▓▓ ▓\n\ + \ ▓▓ ▓▓\n\ + \▓▓▓▓▓▓ ▓\n\ + \▓▓▓▓▓▓▓▓▓\n\ + \▓▓▓▓▓▓▓▓▓\n\ + \block 3 cyan interesting\n\ + \▓▓▓▓▓▓▓▓▓\n\ + \▓▓▓▓▓▓▓▓▓\n\ + \▓ ▓▓▓▓▓▓\n\ + \▓▓ ▓▓ \n\ + \▓ ▓▓ \n\ + \▓▓ ▓▓ \n\ + \▓ ▓▓▓▓▓▓\n\ + \▓▓▓▓▓▓▓▓▓\n\ + \▓▓▓▓▓▓▓▓▓\n\ + \block T green interesting\n\ + \▓▓▓▓▓▓▓▓▓\n\ + \▓▓▓▓▓▓▓▓▓\n\ + \▓▓▓▓▓▓▓▓▓\n\ + \ \n\ + \▓▓▓ = ▓▓▓\n\ + \▓▓▓ ▓▓▓\n\ + \▓▓▓▓▓▓▓▓▓\n\ + \▓▓▓▓▓▓▓▓▓\n\ + \▓▓▓▓▓▓▓▓▓\n\ + \block p magenta boring\n\ + \▓▓▓▓▓▓▓▓▓\n\ + \▓▓▓▓▓▓▓▓▓\n\ + \▓▓ ▓▓▓ ▓▓\n\ + \▓▓▓▓▓▓▓▓▓\n\ + \▓▓▓▓▓▓▓▓▓\n\ + \▓▓▓▓▓▓▓▓▓\n\ + \▓▓▓▓▓▓▓▓▓\n\ + \▓▓▓▓▓▓▓▓▓\n\ + \▓▓▓▓▓▓▓▓▓\n" + +transfer27 :: World +transfer27 = parse + "player p\n\ + \block t white boring\n\ + \▓▓▓▓▓▓▓▓▓\n\ + \▓▓▓▓▓▓▓▓▓\n\ + \▓▓▓▓▓▓▓▓▓\n\ + \▓▓▓▓▓▓▓▓▓\n\ + \▓▓▓▓t▓ab▓\n\ + \▓▓▓▓▓▓▓▓▓\n\ + \▓▓▓▓▓▓▓▓▓\n\ + \▓▓▓▓▓▓▓▓▓\n\ + \▓▓▓▓▓▓▓▓▓\n\ + \link A a blue\n\ + \block a blue interesting\n\ + \▓▓ y \n\ + \▓▓ \n\ + \▓▓ \n\ + \▓ \n\ + \▓B p \n\ + \▓ \n\ + \▓▓ \n\ + \▓▓ \n\ + \▓▓ x \n\ + \link B b red\n\ + \block b red interesting\n\ + \ ▓▓\n\ + \ ▓▓\n\ + \ ▓▓\n\ + \ ▓\n\ + \ =-- A▓\n\ + \ ▓\n\ + \ ▓▓\n\ + \ ▓▓\n\ + \ ▓▓\n\ + \block x yellow boring\n\ + \▓▓▓▓▓▓▓▓▓\n\ + \▓▓▓▓▓▓▓▓▓\n\ + \▓▓▓▓▓▓▓▓▓\n\ + \▓▓▓▓▓▓▓▓▓\n\ + \▓▓▓▓▓▓▓▓▓\n\ + \▓▓▓▓▓▓▓▓▓\n\ + \▓▓▓▓▓▓▓▓▓\n\ + \▓▓▓▓▓▓▓▓▓\n\ + \▓▓▓▓▓▓▓▓▓\n\ + \block y yellow boring\n\ + \▓▓▓▓▓▓▓▓▓\n\ + \▓▓▓▓▓▓▓▓▓\n\ + \▓▓▓▓▓▓▓▓▓\n\ + \▓▓▓▓▓▓▓▓▓\n\ + \▓▓▓▓▓▓▓▓▓\n\ + \▓▓▓▓▓▓▓▓▓\n\ + \▓▓▓▓▓▓▓▓▓\n\ + \▓▓▓▓▓▓▓▓▓\n\ + \▓▓▓▓▓▓▓▓▓\n\ + \block p magenta boring\n\ + \▓▓▓▓▓▓▓▓▓\n\ + \▓▓▓▓▓▓▓▓▓\n\ + \▓▓ ▓▓▓ ▓▓\n\ + \▓▓▓▓▓▓▓▓▓\n\ + \▓▓▓▓▓▓▓▓▓\n\ + \▓▓▓▓▓▓▓▓▓\n\ + \▓▓▓▓▓▓▓▓▓\n\ + \▓▓▓▓▓▓▓▓▓\n\ \▓▓▓▓▓▓▓▓▓\n" \ No newline at end of file