packages feed

scroll-1.20250228: Main.hs

import Control.Monad.ST
import Control.Monad.State.Strict
import UI.NCurses (Curses, screenSize, Event(..))
import Options.Applicative
import Data.Maybe

import Types
import WorldSetup
import Control
import View
import Term
import Term.Curses
import Rand
import Unicode
import Level
import qualified Level.File
import Pager

data Opts = Opts
	{ fileOpt :: Maybe FilePath
	, randOpt :: Maybe String
	, timeoutOpt :: Maybe String
	}

parseOpts :: Parser Opts
parseOpts = Opts <$> optional pFileOpt <*> optional pRandOpt <*> optional pTimeoutOpt
  where
	pFileOpt = argument str $ 
		metavar "FILE" <>
		help "page a file"
	pRandOpt = strOption $
		short 'r' <>
		long "rand" <>
		metavar "RAND" <>
		help "RNG seed (0 for unshuffled levels)"
	pTimeoutOpt = strOption $
		short 't' <>
		long "timeout" <>
		metavar "TIMEOUT" <>
		help "quit if no keypress in this many seconds"

game :: ParserInfo Opts
game = info (helper <*> parseOpts)
	( fullDesc
	<> header "scroll - a roguelike pager" )

main :: IO ()
main = execParser game >>= setup

setup :: Opts -> IO ()
setup opts = do
	primeUnicodeTable
	rand <- initRand (randOpt opts)
	finalmsg <- inCurses $ \palette -> do
		level <- case fileOpt opts of
			Nothing -> do
				pipedinput <- liftIO stdinPager
				case pipedinput of
					Nothing -> levelFor rand 
						<$> Level.select palette timeoutms
					Just s -> return $ 
						Level.File.level rand s
			Just f -> liftIO $ Level.File.level rand <$> readFile f
		if emptyLevel (fst level)
			then return (Just "Game Over")
			else do
				(ymax, _) <- screenSize
				s <- liftIO $ stToIO $ makeWorld level ymax rand
				run s mainLoop (InputEvent EventResized) palette timeoutms
					initialViewOffset
	maybe (return ()) putStrLn finalmsg
  where
	timeoutms = (* 1000) . read <$> timeoutOpt opts

run :: S -> Step -> InputEvent -> Palette -> Maybe Integer -> ViewOffset -> Curses (Maybe String)
run s step input palette timeoutms offset = do
	(NextStep view n, s') <- liftIO $ stToIO $ flip runStateT s $
		step input
	(mevent, offset') <- displayView view palette timeoutms offset
	case mevent of
		Nothing -> return $ Just $ "Quitting due to " ++ show (fromMaybe 0 timeoutms `div` 1000) ++ " second idleness timeout"
		Just event -> maybe (return Nothing) (\a' -> run s' a' event palette timeoutms offset') n