-------------------------------------------------------------------------------- -- | -- Module : Terminal.Game -- Copyright : © 2017-2019 Francesco Ariis -- License : GPLv3 (see LICENSE file) -- -- Maintainer : Francesco Ariis <fa-ml@ariis.it> -- Stability : provisional -- Portability : portable -- -- Machinery and utilities for 2D terminal games. -- -- New? Start from 'Game'. -- -------------------------------------------------------------------------------- -- Basic col-on-black ASCII terminal, operations. -- Only module to be imported. -- todo a-t-g senza timer -- todo test that handlers are closed in case of errr [test] -- todo invert # invert (doppio) si elimina? [test] [design] -- todo schermo piccolo, si centra verticalmente? [test] -- todo metti in a-t-g [design] la formula «menu, poi fai questo, poi -- ritorni, astrai questo pattern -- todo timer in contrib o altro modulo? -- todo qptain e jimreed, implement normal movement -- todo [design] javascript backend -- todo controlla caratteri strani su windows, prova con morganw e ibispi -- todo boxratio, blitcenter e blit top left etc in ansi-terminal-game? [u:2] -- todo autoresize terminal (or get-term-size?) -- todo ranSelect :: [a] -> m a in a-t-g [design] -- todo random shuffle in atg? -- todo (da sm) sm any plans to expose more of these bits in a future -- release, making ansi-terminal-game more of a full game engine ? -- [suggestion] -- todo (da sm) music [suggestion] -- todo (da sm) I'm also wondering if I can adjust the frame rate during -- play, to speed things up -- todo (da sm) hot reload for game (see IHP web app) [suggestion] module Terminal.Game ( -- * Running FPS, Event(..), Game(..), playGame, -- * Game logic -- | Some convenient function dealing with -- Timers ('Timed') and 'Animation's. -- -- Usage of these is not mandatory: 'Game' is -- parametrised over any state @s@, you are free -- to implement game logic as you prefer. -- ** Timers/Animation -- *** Timers Timed, creaTimer, creaBoolTimer, creaTimerLoop, creaBoolTimerLoop, -- *** Animations Animation, creaAnimation, creaLoopAnimation, creaStaticAnimation, -- *** T/A interface tick, ticks, reset, lapse, fetchFrame, isExpired, getFrames, -- ** Random numbers StdGen, getStdGen, mkStdGen, getRandom, getRandomList, Random, -- * Drawing -- | To get to the gist of drawing, check the -- documentation for '%'. -- -- Blitting on screen is double-buffered and diff'd -- (at each frame, only cells with changed character -- will be redrawn). -- ** Plane Plane, Coords, Row, Column, Width, Height, blankPlane, stringPlane, stringPlaneTrans, makeTransparent, makeOpaque, paperPlane, planeSize, -- ** Draw Draw, (%), (&), (#), subPlane, mergePlanes, cell, word, box, textBox, textBoxLiquid, Color(..), ColorIntensity(..), color, bold, invert, -- *** Declarative drawing (|||), (===), (***), hcat, vcat, -- * Testing testGame, setupGame, recordGame, readRecord, narrateGame, playGameS, -- * Utility Terminal.Game.displaySize, errorPress -- * Cross platform -- $threaded ) where import System.Console.ANSI import Terminal.Game.Animation import Terminal.Game.Draw import Terminal.Game.Layer.Imperative import Terminal.Game.Layer.Object as O import Terminal.Game.Plane import Terminal.Game.Random -- $threaded -- Good practices for cross-compatibility: -- -- * choose game dimensions of no more than __24 rows__ and __80 columns__. -- This ensures compatibility with the trickiest terminals (i.e. Win32 -- console); -- -- * use __ASCII characters__ only. Again this is for Win32 console -- compatibility, until -- [this GHC bug](https://gitlab.haskell.org/ghc/ghc/issues/7593) gets -- fixed; -- -- * employ colour sparingly: as some users will play your game in a -- light-background terminal and some in a dark one, choose only colours -- that go well with either (blue, red, etc.); -- -- * some terminals/multiplexers (i.e. tmux) do not make a distinction -- between vivid/dull; do not base your game mechanics on that -- difference. -- | /Usable/ terminal display size (on Win32 console the last line is -- set aside for input). displaySize :: IO (Width, Height) displaySize :: IO (Width, Width) displaySize = IO (Width, Width) forall (m :: * -> *). MonadDisplay m => m (Width, Width) O.displaySize