scroll 1.20150313 → 1.20150314
raw patch · 10 files changed
+139/−74 lines, 10 filesdep +unixsetup-changed
Dependencies added: unix
Files
- CHANGELOG +12/−0
- Level/File.hs +2/−6
- Main.hs +10/−4
- Makefile +4/−0
- Setup.hs +2/−60
- Status.hs +1/−1
- WorldSetup.hs +60/−0
- portable/Pager.hs +6/−0
- scroll.cabal +17/−3
- unix/Pager.hs +25/−0
CHANGELOG view
@@ -1,3 +1,15 @@+scroll (1.20150314) unstable; urgency=medium++ * This is a post-7drl release that does not change any gameplay.+ * Support use as a pager, with input piped in on stdin, as long+ as the unix module is available.+ * Rename Setup.hs since cabal uses that name.+ Thanks to Sergei Trofimovich of Gentoo, which has packaged scroll!+ * Don't crash on exceedingly small input files when being used as a+ pager.++ -- Joey Hess <id@joeyh.name> Sat, 14 Mar 2015 15:32:28 -0400+ scroll (1.20150313) unstable; urgency=low * First release, at the end of 7DRL 2015!
Level/File.hs view
@@ -4,12 +4,8 @@ import Level.Shuffle import qualified Level.Tutorial -import Control.Applicative--level :: Rand -> FilePath -> IO (Level, Level)-level r f = do- ls <- lines <$> readFile f- return (rand $ ShuffleableSection ls, Level.Tutorial.level)+level :: Rand -> String -> (Level, Level)+level r s = (rand $ ShuffleableSection (lines s), Level.Tutorial.level) where rand = case r of Rand g -> shuffle g
Main.hs view
@@ -5,7 +5,7 @@ import Data.Maybe import Types-import Setup+import WorldSetup import Control import View import Curses@@ -13,6 +13,7 @@ import Unicode import Level import qualified Level.File+import Pager data Opts = Opts { fileOpt :: Maybe FilePath@@ -51,9 +52,14 @@ rand <- initRand (randOpt opts) finalmsg <- inCurses $ \palette -> do level <- case fileOpt opts of- Nothing -> levelFor rand - <$> Level.select palette timeoutms- Just f -> liftIO $ Level.File.level rand f+ 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
Makefile view
@@ -2,3 +2,7 @@ @find . | grep -v /.git/ | grep -v /tmp/ | grep -v /dist/ | grep -v /doc/ | egrep '\.hs$$' | xargs hothasktags 2>/dev/null | sort > tags cabal build ln -sf dist/build/scroll/scroll++hackage:+ @cabal sdist+ @cabal upload dist/*.tar.gz
Setup.hs view
@@ -1,60 +1,2 @@-module Setup where--import Control.Monad.ST-import Control.Monad.State.Strict-import qualified Data.Vector as V-import qualified Data.Set as S-import Data.Vector ((!))-import Control.Applicative-import Data.Default--import Types-import Level-import Level.Border-import Player-import Spell-import Poison-import Rand---- Creates a new game world from the specified level content.-makeWorld :: (Level, Level) -> Integer -> Rand -> ST RealWorld S-makeWorld (frontl, backl) screenheight r = do- (front, frontbuf) <- setup frontv- (back, backbuf) <- setup backv- addCap front- addCap back- let s = S- { world = front- , flipSide = back- , player = def { playerSpells = startingspells }- , bottomBuffer = (frontbuf, backbuf)- , topBuffer = 0- , peruser = def- , randSource = r- , helpShown = False- , messages = []- , spells = allSpells- , poisons = allPoisons- , windows = []- }- execStateT startingPosition s- where- (frontv, backv) = levelVectors (frontl, backl)-- -- 2 for each end of scroll- scrollsize = fromIntegral $ screenheight - 4-- setup :: V.Vector (V.Vector Char) -> ST RealWorld (World, Vec2 Char)- setup v = do- mv <- V.mapM V.thaw v- let (w, b) = V.splitAt (scrollsize - 1) mv- -- grow for scroll cap- let p = V.singleton (w ! 0)- let w' = V.concat [p,p,w,p,p]- (,)- <$> V.thaw w'- <*> V.thaw b-- startingspells = flip S.insert startingSpells $- optspells !! fst (randomR (0, length optspells - 1) (randGen r))- optspells = S.toList maybeStartingSpells+import Distribution.Simple+main = defaultMain
Status.hs view
@@ -17,7 +17,7 @@ stringAt (x, y) s = do w <- worldWidth h <- worldHeight- unless (y >= h) $ do+ unless (y >= h || y < 0 || x < 0) $ do let maxsz = w - 3 - x let s' = take maxsz s forM_ [0..length s' - 1] $ \n -> do
+ WorldSetup.hs view
@@ -0,0 +1,60 @@+module WorldSetup where++import Control.Monad.ST+import Control.Monad.State.Strict+import qualified Data.Vector as V+import qualified Data.Set as S+import Data.Vector ((!))+import Control.Applicative+import Data.Default++import Types+import Level+import Level.Border+import Player+import Spell+import Poison+import Rand++-- Creates a new game world from the specified level content.+makeWorld :: (Level, Level) -> Integer -> Rand -> ST RealWorld S+makeWorld (frontl, backl) screenheight r = do+ (front, frontbuf) <- setup frontv+ (back, backbuf) <- setup backv+ addCap front+ addCap back+ let s = S+ { world = front+ , flipSide = back+ , player = def { playerSpells = startingspells }+ , bottomBuffer = (frontbuf, backbuf)+ , topBuffer = 0+ , peruser = def+ , randSource = r+ , helpShown = False+ , messages = []+ , spells = allSpells+ , poisons = allPoisons+ , windows = []+ }+ execStateT startingPosition s+ where+ (frontv, backv) = levelVectors (frontl, backl)++ -- 2 for each end of scroll+ scrollsize = fromIntegral $ screenheight - 4++ setup :: V.Vector (V.Vector Char) -> ST RealWorld (World, Vec2 Char)+ setup v = do+ mv <- V.mapM V.thaw v+ let (w, b) = V.splitAt (scrollsize - 1) mv+ -- grow for scroll cap+ let p = V.singleton (w ! 0)+ let w' = V.concat [p,p,w,p,p]+ (,)+ <$> V.thaw w'+ <*> V.thaw b++ startingspells = flip S.insert startingSpells $+ optspells !! fst (randomR (0, length optspells - 1) (randGen r))+ optspells = S.toList maybeStartingSpells
+ portable/Pager.hs view
@@ -0,0 +1,6 @@+-- This is just a stub for systems building without unix support.++module Pager where++stdinPager :: IO (Maybe String)+stdinPager = return Nothing
scroll.cabal view
@@ -1,10 +1,10 @@ Name: scroll-Version: 1.20150313+Version: 1.20150314 Cabal-Version: >= 1.6 License: GPL-2 Maintainer: Joey Hess <id@joeyh.name> Author: Joey Hess-Stability: Experimental+Stability: Stable Copyright: 2015 Joey Hess Build-Type: Simple Category: Game@@ -16,13 +16,26 @@ Makefile doc/scroll-roguelike-game-idea .gitignore+ unix/Pager.hs+ portable/Pager.hs +Flag Unix+ Description: Use Unix capabilities to be a better pager+ Executable scroll Main-Is: Main.hs GHC-Options: -Wall -threaded -O2 Build-Depends: base >= 4.5, base < 5, vector, bytestring, mtl, containers, ncurses, data-default, random, monad-loops, IfElse, case-insensitive, optparse-applicative, text++ Hs-Source-Dirs: .+ if flag(Unix)+ Build-Depends: unix+ Hs-Source-Dirs: unix+ else+ Hs-Source-Dirs: portable+ Other-Modules: CharMap Control@@ -43,13 +56,13 @@ Level.Tutorial Monster Peruser+ Pager Player Player.Consume Player.Move Poison Poison.Enum Rand- Setup Spell Spell.Enum Status@@ -59,3 +72,4 @@ Utility.Percentage View World+ WorldSetup
+ unix/Pager.hs view
@@ -0,0 +1,25 @@+module Pager where++import System.IO+import System.Posix.IO+import System.Posix.Terminal+import Control.Applicative++-- If stdin is not connected to a tty, we're being used as a pager with+-- output piped in. The content of stdin is sucked in and returned for+-- paging, and stdin is re-opened from the terminal device.+stdinPager :: IO (Maybe String)+stdinPager = go =<< queryTerminal stdInput+ where+ go True = return Nothing+ go False = do+ stdoutisterm <- queryTerminal stdOutput+ if stdoutisterm+ then do+ term <- getTerminalName stdOutput+ oldstdin <- dup stdInput+ closeFd stdInput+ newstdin <- openFd term ReadOnly Nothing defaultFileFlags + _ <- dupTo newstdin stdInput+ Just <$> (hGetContents =<< fdToHandle oldstdin)+ else error "stdout is not a terminal"