roguestar-gl 0.2.2 → 0.4.0.0
raw patch · 57 files changed
+3246/−2444 lines, 57 filesdep +bytestringdep +old-timedep +priority-syncdep ~GLUTdep ~MonadRandomdep ~arrows
Dependencies added: bytestring, old-time, priority-sync, random, statistics, stm, vector
Dependency ranges changed: GLUT, MonadRandom, arrows, base, containers, filepath, mtl, rsagl
Files
- Main.hs +142/−47
- roguestar-gl.cabal +24/−15
- src/Actions.hs +389/−0
- src/Actions.lhs +0/−241
- src/Animation.hs +274/−0
- src/Animation.lhs +0/−199
- src/AnimationBuildings.hs +36/−0
- src/CommandLine.hs +12/−0
- src/CommandLine.lhs +0/−14
- src/Driver.hs +347/−0
- src/Driver.lhs +0/−237
- src/Keymaps/CommonKeymap.hs +56/−58
- src/Keymaps/Keymaps.hs +89/−0
- src/Keymaps/Keymaps.lhs +0/−77
- src/Keymaps/NumpadKeymap.hs +14/−14
- src/Keymaps/VIKeymap.hs +14/−14
- src/Limbs.hs +122/−0
- src/Limbs.lhs +0/−66
- src/Main.hs +145/−0
- src/Main.lhs +0/−114
- src/Models/Androsynth.hs +2/−4
- src/Models/Ascendant.hs +2/−4
- src/Models/Caduceator.hs +9/−13
- src/Models/Encephalon.hs +6/−9
- src/Models/Library.hs +214/−0
- src/Models/Library.lhs +0/−93
- src/Models/LibraryData.hs +82/−0
- src/Models/LibraryData.lhs +0/−29
- src/Models/MachineParts.hs +9/−11
- src/Models/Materials.hs +133/−0
- src/Models/Materials.lhs +0/−92
- src/Models/Monolith.hs +14/−0
- src/Models/PhaseWeapons.hs +34/−21
- src/Models/QuestionMark.hs +3/−6
- src/Models/Recreant.hs +5/−6
- src/Models/RecreantFactory.hs +3/−3
- src/Models/Reptilian.hs +15/−18
- src/Models/Stargate.hs +38/−0
- src/Models/Terrain.hs +129/−0
- src/Models/Terrain.lhs +0/−108
- src/Models/Tree.hs +12/−34
- src/PrintText.hs +175/−0
- src/PrintText.lhs +0/−137
- src/PrintTextData.hs +19/−0
- src/PrintTextData.lhs +0/−30
- src/ProtocolTypes.hs +70/−0
- src/ProtocolTypes.lhs +0/−72
- src/Quality.hs +23/−5
- src/RenderingControl.hs +180/−0
- src/RenderingControl.lhs +0/−434
- src/Statistics.hs +79/−0
- src/Strings.hs +35/−0
- src/Strings.lhs +0/−21
- src/Tables.hs +59/−0
- src/Tables.lhs +0/−81
- src/VisibleObject.hs +232/−0
- src/VisibleObject.lhs +0/−117
Main.hs view
@@ -1,32 +1,134 @@+{-# LANGUAGE ExistentialQuantification, FlexibleInstances, OverloadedStrings #-}+ module Main (main) where import System.Process import System.Exit+import System.Console.GetOpt import Control.Concurrent-import System.Environment import System.FilePath import System.IO import Control.Monad import Paths_roguestar_gl+import GHC.Environment+import System.Time+import qualified Data.ByteString as B+import Data.ByteString.Char8 ()+import GHC.Exts (IsString(..)) +data Args = Args {+ arg_echo_protocol :: Bool,+ arg_single_threaded :: Bool,+ arg_verbose :: Bool,+ arg_help :: Bool,+ arg_prefix :: String,+ arg_engine :: [String],+ arg_client :: [String] }++roguestar_options :: [OptDescr (Args -> Args)]+roguestar_options =+ [Option "s1" ["single-threaded"]+ (NoArg $ \a -> a { arg_single_threaded = True })+ "Run the engine and client single-threaded.",+ Option "e" ["echo-protocol"]+ (NoArg $ \a -> a { arg_echo_protocol = True })+ "Echo the client-server protocol (for debugging).",+ Option "h?" ["help"]+ (NoArg $ \a -> a { arg_help = True })+ "Print this help message.",+ Option "v" ["verbose"]+ (NoArg $ \a -> a { arg_verbose = True })+ "Print debugging information.",+ Option "p" ["prefix","path"]+ (ReqArg (\s a -> a { arg_prefix = s }) "PREFIX")+ ("Path to the directory where the roguestar-engine and " +++ "roguestar-gl binaries are kept. You might need this " +++ "to play roguestar off a remotely mounted file server " +++ "or if something is broken and you know what you're doing.")]++help_text :: String+help_text = usageInfo+ ("Roguestar: A science-fiction role playing game.\n\n" +++ "Roguestar has a client-server architecture (roguestar-gl, " +++ "and roguestar-engine). This program instantiates both " +++ "on the local host and manages all communication between " +++ "them.\n\n" +++ "Usage: roguestar [OPTIONS]")+ roguestar_options+ main :: IO ()-main = - do (should_echo_protocol,args) <- - do args <- getArgs- return ("--echo-protocol" `elem` args,- filter (/= "--echo-protocol") $ args)- n <- getNumberOfCPUCores- bin_dir <- getBinDir- let n_rts_string = if n == 1 then [] else ["-N" ++ show n]- let gl_args = ["+RTS", "-G4"] ++ n_rts_string ++ ["-RTS"] ++ args- let engine_args = ["+RTS"] ++ n_rts_string ++ ["-RTS"] ++ ["version","over","begin"]- (e_in,e_out,e_err,roguestar_engine) <- runInteractiveProcess (bin_dir `combine` "roguestar-engine") engine_args Nothing Nothing- (gl_in,gl_out,gl_err,roguestar_gl) <- runInteractiveProcess (bin_dir `combine` "roguestar-gl") gl_args Nothing Nothing- forkIO $ pump e_out $ [("",gl_in)] ++ (if should_echo_protocol then [("engine >>> gl *** ",stdout)] else [])- forkIO $ pump gl_out $ [("",e_in)] ++ (if should_echo_protocol then [("gl <<< engine *** ",stdout)] else [])- forkIO $ pump e_err $ [("roguestar-engine *** ",stderr)]- forkIO $ pump gl_err $ [("roguestar-gl *** ",stderr)]- forkIO $+main =+ do bin_dir <- getBinDir+ let default_args = Args {+ arg_echo_protocol = False,+ arg_single_threaded = False,+ arg_help = False,+ arg_prefix = bin_dir,+ arg_verbose = False,+ arg_engine = [],+ arg_client = [] }+ raw_args <- getFullArgs+ let (opts_list,_,errs) = getOpt (ReturnInOrder $ \s args -> args {+ arg_engine = arg_engine args ++ [s],+ arg_client = arg_client args ++ [s] })+ roguestar_options+ raw_args+ let args = foldr ($) default_args opts_list+ when (arg_help args) $+ do putStrLn help_text+ exitWith ExitSuccess+ when (not $ null errs) $+ do mapM_ putStrLn errs+ exitWith ExitSuccess+ let n_rts_string = if arg_single_threaded args then [] else ["-N"]+ let gl_args =+ ["+RTS", "-G4"] +++ n_rts_string +++ ["-RTS"] +++ arg_client args+ let engine_args =+ ["+RTS"] +++ n_rts_string +++ ["-RTS"] +++ arg_engine args +++ ["version","over","begin"]+ let roguestar_engine_bin = arg_prefix args `combine` "roguestar-engine"+ let roguestar_gl_bin = arg_prefix args `combine` "roguestar-gl"+ when (arg_verbose args) $+ putStrLn $ "starting process: " +++ roguestar_engine_bin ++ " " +++ unwords engine_args+ (e_in,e_out,e_err,roguestar_engine) <-+ runInteractiveProcess roguestar_engine_bin+ engine_args+ Nothing+ Nothing+ when (arg_verbose args) $+ putStrLn $ "starting process: " +++ roguestar_gl_bin +++ " " +++ unwords gl_args+ (gl_in,gl_out,gl_err,roguestar_gl) <-+ runInteractiveProcess roguestar_gl_bin gl_args Nothing Nothing+ stdout_chan <- newChan+ _ <- forkIO $ pump e_out $+ [("",DHandle gl_in)] +++ (if arg_echo_protocol args+ then [("engine >>> gl *** ",DChanTime stdout_chan)]+ else [])+ _ <- forkIO $ pump gl_out $+ [("",DHandle e_in)] +++ (if arg_echo_protocol args+ then [("engine <<< gl *** ",DChanTime stdout_chan)]+ else [])+ _ <- forkIO $ pump e_err $ if arg_verbose args+ then [("roguestar-engine *** ",DChan stdout_chan)]+ else []+ _ <- forkIO $ pump gl_err $ if arg_verbose args+ then [("roguestar-gl *** ",DChan stdout_chan)]+ else []+ _ <- forkIO $ printChan stdout stdout_chan+ _ <- forkIO $ do roguestar_engine_exit <- waitForProcess roguestar_engine case roguestar_engine_exit of ExitFailure x -> putStrLn $ "roguestar-engine terminated unexpectedly (" ++ show x ++ ")"@@ -37,36 +139,29 @@ ExitFailure x -> putStrLn $ "roguestar-gl terminated unexpectedly (" ++ show x ++ ")" _ -> return () -pump :: Handle -> [(String,Handle)] -> IO ()+data Destination = DHandle Handle | DChan (Chan B.ByteString) | DChanTime (Chan B.ByteString)++send :: Destination -> B.ByteString -> IO ()+send (DHandle h) str = B.hPutStrLn h str >> hFlush h+send (DChan c) str = writeChan c str+send (DChanTime c) str =+ do t <- getClockTime+ writeChan c $ fromString (show t) `B.append` ": " `B.append` str++setup :: Destination -> IO ()+setup (DHandle h) = hSetBuffering h NoBuffering+setup _ = return ()++pump :: Handle -> [(B.ByteString,Destination)] -> IO () pump from tos =- do mapM_ (flip hSetBuffering NoBuffering . snd) tos- hSetBuffering from NoBuffering+ do mapM_ (setup . snd) tos forever $- do l <- hGetLine from- flip mapM_ tos $ \(name,to) -> - do hPutStrLn to $ name ++ l- hFlush to--getNumberOfCPUCores :: IO Int-getNumberOfCPUCores =- do m_nop <- readNumberOfProcessors- m_cpuinfo <- scanCPUInfo- case (m_nop,m_cpuinfo) of- (Just n,_) -> do hPutStrLn stderr $ "roguestar: " ++ show n ++ - " CPU core(s) based on windows environment variable NUMBER_OF_PROCESSORS. "- return n- (_,Just n) -> do hPutStrLn stderr $ "roguestar: " ++ show n ++ " CPU core(s) based on /proc/cpuinfo. " - return n- _ -> do hPutStrLn stderr "roguestar: couldn't find number of CPU cores, assuming 1 core"- return 1+ do l <- B.hGetLine from+ flip mapM_ tos $ \(name,to) -> send to $ name `B.append` l -readNumberOfProcessors :: IO (Maybe Int)-readNumberOfProcessors = flip catch (const $ return Nothing) $ liftM Just $- do n <- getEnv "NUMBER_OF_PROCESSORS"- return $! read n+printChan :: Handle -> Chan B.ByteString -> IO ()+printChan h c = forever $+ do s <- readChan c+ B.hPutStrLn h s+ hFlush h -scanCPUInfo :: IO (Maybe Int)-scanCPUInfo = catch (liftM (Just . length . filter isProcessorLine . map words . lines) $ readFile "/proc/cpuinfo")- (const $ return Nothing)- where isProcessorLine ["processor",":",_] = True- isProcessorLine _ = False
roguestar-gl.cabal view
@@ -1,5 +1,5 @@ name: roguestar-gl-version: 0.2.2+version: 0.4.0.0 license: OtherLicense license-file: LICENSE author: Christopher Lane Hinson <lane@downstairspeople.org>@@ -9,23 +9,31 @@ synopsis: Sci-fi roguelike (turn-based, chessboard-tiled, role playing) game description: Roguestar is a science fiction themed roguelike (turn-based, chessboard-tiled, role playing) game written in Haskell. Roguestar uses- OpenGL for graphics. This is still a very early release and several- important things don't work.- .- This initial release allows you to play one of six alien races. You begin- the game stranded on an alien planet, fighting off an endless hoard of- hostile robots.+ OpenGL for graphics. This is still a very early release. . The git repository is available at <http://www.downstairspeople.org/git/roguestar-gl.git>. homepage: http://roguestar.downstairspeople.org/ -build-depends: base>3, containers, arrows, mtl, MonadRandom, GLUT, rsagl >=0.2.2 && <0.3, filepath >1.1---roguestar-engine==0.3+build-depends: base>=4&&<5,+ rsagl==0.4.0.0,+ containers>=0.3.0.0 && < 0.4,+ arrows>=0.4.1.2 && < 0.5,+ mtl>=1.1.0.2 && < 1.2,+ MonadRandom>=0.1.4 && < 1.2,+ GLUT>=2.1.2.1 && < 2.2,+ filepath>=1.1.0.3 && < 1.2,+ random>=1.0.0.2 && < 1.1,+ bytestring>=0.9.1.5 && < 0.10,+ statistics>=0.5.1.0 && < 0.6,+ vector>=0.6.0.1 && < 0.7,+ stm>=2.1.1.2 && < 2.2,+ priority-sync>=0.2.1.0 && < 0.2.2+ build-type: Simple-tested-with: GHC==6.8.2+tested-with: GHC==6.12.1 executable: roguestar-gl-main-is: Main.lhs+main-is: Main.hs hs-source-dirs: src other-modules: Quality, ProtocolTypes, VisibleObject, Strings, WordGenerator, Driver,@@ -36,11 +44,12 @@ Models.Library, Models.MachineParts, Models.LibraryData, Models.Caduceator, Models.Tree, Models.Encephalon, Models.PhaseWeapons, RenderingControl, Keymaps.BuiltinKeymaps, Keymaps.CommonKeymap, Keymaps.NumpadKeymap,- Keymaps.Keymaps, Keymaps.VIKeymap-ghc-options: -Wall -threaded -fno-warn-type-defaults -fexcess-precision+ Keymaps.Keymaps, Keymaps.VIKeymap, AnimationBuildings, Models.Monolith,+ Models.Stargate, Statistics+ghc-options: -threaded -fno-warn-type-defaults -fexcess-precision ghc-prof-options: -prof -auto-all executable: roguestar main-is: Main.hs-build-depends: process-ghc-options: -Wall -threaded+build-depends: process, old-time, bytestring+ghc-options: -threaded
+ src/Actions.hs view
@@ -0,0 +1,389 @@+{-# LANGUAGE OverloadedStrings #-}+-- |+-- A set of actions that should be bound to keystrokes.+--+module Actions+ (takeUserInputAction,+ getValidActions,+ ActionInput(..),+ select_race_action_names,+ select_base_class_action_names,+ make_what_action_names,+ executeContinueAction,+ menu_states,+ player_turn_states)+ where++import Control.Monad.Error+import Driver+import Data.List+import PrintText+import Tables+import Data.Maybe+import Globals+import RSAGL.Types+import Control.Concurrent.STM+import Quality+import Data.Char+import qualified Data.ByteString.Char8 as B++-- |+-- Input to an action.+--+data ActionInput = ActionInput {+ action_globals :: Globals,+ action_driver_object :: DriverObject,+ action_print_text_object :: PrintTextObject }++type Action = ActionInput -> ErrorT ActionValidityReason STM (STM ())++data ActionValidity = Go | Hold ActionValidityReason deriving (Read, Show)+data ActionValidityReason = Invalid | Undecided String deriving (Read,Show)++instance Error ActionValidityReason where+ noMsg = strMsg ""+ strMsg = Undecided++invalid :: ErrorT ActionValidityReason STM ()+invalid = throwError Invalid++isUndecided :: ActionValidity -> Bool+isUndecided (Hold (Undecided _)) = True+isUndecided _ = False++isGo :: ActionValidity -> Bool+isGo Go = True+isGo _ = False++{----------------------------------------------------------+ -- Support functions for Actions+ ----------------------------------------------------------}++actionValid :: ActionInput -> Action -> STM ActionValidity+actionValid action_input action = + do result <- runErrorT $ action action_input+ return $ either Hold (const Go) result++executeAction :: ActionInput -> Action -> STM ()+executeAction action_input action =+ do result <- runErrorT $ action action_input+ either (\failure -> printText+ (action_print_text_object action_input)+ UnexpectedEvent+ ("unable to execute action: " `B.append`+ B.pack (show failure)))+ (id)+ result+ return ()++executeContinueAction :: ActionInput -> STM ()+executeContinueAction action_input =+ do either (const $ return()) id =<< (runErrorT $ (snd continue_action) action_input)+ return ()++-- |+-- Constructs an action such that the action is valid only if it is listed in+-- a table from the engine. The first parameter identifies the table column to look up,+-- (table name, table id, header to look under). The second parameter is the name of the+-- action as it will be send to the engine. The third parameter is the action_param.+-- The action_param must be listed under the specified header of the specified table,+-- or the action will not be allowed to execute.+--+-- For example: selectTableAction ("people","0","name") "select-person-to-call-state" "call-with-telephone" "carl"+-- +-- \> game query state+-- answer: state select-person-to-call-state+-- \> game query people+-- begin-table people 0 name+-- john+-- wendy+-- carl+-- bob+-- end-table+-- \> game action call-with-telephone Carl+-- +-- In this case the action executes because the state is actually select-person-to-call-state and Carl+-- is actually listed under the "name" header of the "people" table.+--+-- In practice this function is used for things like the race-selection-state and the class-selection-state+-- where we select from a predifined list of possible choices, but the engine further restricts the choices.+-- Each possible choice is it's own action that will only return action_valid if it is listed in the appropriate+-- table from the engine. +--+selectTableAction :: (B.ByteString,B.ByteString,B.ByteString) -> B.ByteString -> B.ByteString -> B.ByteString -> Action+selectTableAction (the_table_name,the_table_id,the_table_header) allowed_state action_name action_param = stateGuard [allowed_state] $+ \action_input ->+ do table <- maybe (fail $ "need table " ++ B.unpack the_table_name) return =<< (lift $ getTable (action_driver_object action_input) the_table_name the_table_id)+ unless ([action_param] `elem` tableSelect table [the_table_header]) invalid+ return $ driverAction (action_driver_object action_input) [action_name, action_param]++-- |+-- Guard an action to only run during specific states.+--+stateGuard :: [B.ByteString] -> Action -> Action+stateGuard allowed_states actionM action_input =+ do is_valid_state <- liftM (maybe False (`elem` allowed_states)) $ lift $ getAnswer (action_driver_object action_input) "state"+ unless is_valid_state invalid+ actionM action_input++-- |+-- A simple action that can run whenever the player state is a member of a given list.+-- The action name is passed directly to the engine.+--+stateLinkedAction :: [B.ByteString] -> B.ByteString -> (B.ByteString,Action)+stateLinkedAction allowed_state action_name = + (action_name,+ stateGuard allowed_state $ \action_input ->+ return $ driverAction (action_driver_object action_input) [action_name])++-- |+-- An action that can always run, like "quit".+--+alwaysAction :: B.ByteString -> (ActionInput -> STM ()) -> (B.ByteString,Action)+alwaysAction action_name actionSTM =+ (action_name,+ \action_input -> return $ actionSTM action_input)++{----------------------------------------------------------+ Specific Actions+ ----------------------------------------------------------}++player_turn_states :: [B.ByteString]+player_turn_states = ["player-turn","move","attack","fire","jump","turn","clear-terrain"]++menu_states :: [B.ByteString]+menu_states = ["race-selection","class-selection","pickup","drop","wield","make","make-finished","make-what"]++selectable_menu_states :: [B.ByteString]+selectable_menu_states = if all (`elem` menu_states) states then states else error "selectable_menu_states: inconsistent with menu_states"+ where states = ["pickup","drop","wield","make"]++quit_action :: (B.ByteString,Action)+quit_action = alwaysAction "quit" $ \action_input ->+ writeTVar (global_should_quit $ action_globals action_input) True++continue_action :: (B.ByteString,Action)+continue_action = ("continue",\action_input ->+ do is_snapshot <- liftM (== Just "yes") $ lift $ getAnswer (action_driver_object action_input) "snapshot"+ unless is_snapshot invalid+ return $ driverAction (action_driver_object action_input) ["continue"])++direction_actions :: [(B.ByteString,Action)]+direction_actions = map (stateLinkedAction player_turn_states) ["n","ne","e","se","s","sw","w","nw"]++next_action :: (B.ByteString,Action)+next_action = stateLinkedAction selectable_menu_states "next"++prev_action :: (B.ByteString,Action)+prev_action = stateLinkedAction selectable_menu_states "prev"++select_menu_action :: (B.ByteString,Action)+select_menu_action = stateLinkedAction selectable_menu_states "select-menu"++normal_action :: (B.ByteString,Action)+normal_action = ("normal",+ stateGuard (menu_states ++ player_turn_states) $ \action_input -> + return $ driverAction (action_driver_object action_input) ["normal"])++move_action :: (B.ByteString,Action)+move_action = stateLinkedAction player_turn_states "move"++jump_action :: (B.ByteString,Action)+jump_action = stateLinkedAction player_turn_states "jump"++turn_action :: (B.ByteString,Action)+turn_action = stateLinkedAction player_turn_states "turn"++fire_action :: (B.ByteString,Action)+fire_action = stateLinkedAction player_turn_states "fire"++attack_action :: (B.ByteString,Action)+attack_action = stateLinkedAction player_turn_states "attack"++clear_terrain_action :: (B.ByteString,Action)+clear_terrain_action = stateLinkedAction player_turn_states "clear-terrain"++reroll_action :: (B.ByteString,Action)+reroll_action = stateLinkedAction ["class-selection"] "reroll"++pickup_action :: (B.ByteString,Action)+pickup_action = stateLinkedAction player_turn_states "pickup"++drop_action :: (B.ByteString,Action)+drop_action = stateLinkedAction player_turn_states "drop"++wield_action :: (B.ByteString,Action)+wield_action = stateLinkedAction player_turn_states "wield"++unwield_action :: (B.ByteString,Action)+unwield_action = stateLinkedAction player_turn_states "unwield"++activate_action :: (B.ByteString,Action)+activate_action = stateLinkedAction player_turn_states "activate"++make_begin_action :: (B.ByteString,Action)+make_begin_action = stateLinkedAction player_turn_states "make-begin"++make_what_action_names :: [B.ByteString]+make_what_action_names = ["pistol","carbine","rifle","fleuret","sabre"]++makeWhatAction :: B.ByteString -> (B.ByteString,Action)+makeWhatAction s = (s,+ stateGuard ["make-what","make","make-finished"] $ \action_input ->+ return $ driverAction (action_driver_object action_input) ["make-what",s])++make_what_actions :: [(B.ByteString,Action)]+make_what_actions = map makeWhatAction make_what_action_names++make_end_action :: (B.ByteString,Action)+make_end_action = stateLinkedAction ["make-finished"] "make-end"++selectRaceAction :: B.ByteString -> (B.ByteString,Action)+selectRaceAction s = + (s,selectTableAction ("player-races","0","name") "race-selection" "select-race" s)++selectBaseClassAction :: B.ByteString -> (B.ByteString,Action)+selectBaseClassAction s = + (s,selectTableAction ("base-classes","0","class") "class-selection" "select-class" s)++zoomSize :: RSdouble -> RSdouble+zoomSize x | x < 3 = 0.2+zoomSize x | x < 10 = 1.0+zoomSize _ | otherwise = 5.0++zoomIn :: RSdouble -> RSdouble+zoomIn x = x - (zoomSize $ x - zoomSize x)++zoomOut :: RSdouble -> RSdouble+zoomOut x = x + (zoomSize x)++zoom_in_action :: (B.ByteString,Action)+zoom_in_action = alwaysAction "zoom-in" $ \action_input ->+ do writeTVar (global_planar_camera_distance $ action_globals action_input) .+ (max 1.0 . zoomIn) =<< readTVar (global_planar_camera_distance $+ action_globals action_input)+ return ()++zoom_out_action :: (B.ByteString,Action)+zoom_out_action = alwaysAction "zoom-out" $ \action_input ->+ do writeTVar (global_planar_camera_distance $ action_globals action_input) .+ (min 25.0 . zoomOut) =<< readTVar (global_planar_camera_distance $+ action_globals action_input)+ return ()++sky_on_action :: (B.ByteString,Action)+sky_on_action = alwaysAction "sky-on" $ \action_input ->+ do writeTVar (global_sky_on $ action_globals action_input) True+ return ()++sky_off_action :: (B.ByteString,Action)+sky_off_action = alwaysAction "sky-off" $ \action_input ->+ do writeTVar (global_sky_on $ action_globals action_input) False+ return ()++setQualityAction :: Quality -> (B.ByteString,Action)+setQualityAction q =+ alwaysAction (B.pack $ map toLower $ "quality-" ++ show q) $+ \action_input ->+ do writeTVar (global_quality_setting $ action_globals action_input) q+ return ()++quality_bad :: (B.ByteString,Action)+quality_bad = setQualityAction Bad++quality_poor :: (B.ByteString,Action)+quality_poor = setQualityAction Poor++quality_good :: (B.ByteString,Action)+quality_good = setQualityAction Good++quality_super :: (B.ByteString,Action)+quality_super = setQualityAction Super++{----------------------------------------------------------+ Lists of Known Actions+ ----------------------------------------------------------}++select_race_action_names :: [B.ByteString]+select_race_action_names = [--"anachronid",+ "androsynth",+ "ascendant",+ "caduceator",+ "encephalon",+ --"goliath",+ --"hellion",+ --"kraken",+ --"myrmidon",+ --"perennial",+ "recreant",+ "reptilian"]++select_race_actions :: [(B.ByteString,Action)]+select_race_actions = map selectRaceAction select_race_action_names++select_base_class_action_names :: [B.ByteString]+select_base_class_action_names = ["barbarian",+ "consular",+ "engineer",+ "forceadept",+ "marine",+ "ninja",+ "pirate",+ "scout",+ "shepherd",+ "thief",+ "warrior"]++select_base_class_actions :: [(B.ByteString,Action)]+select_base_class_actions = map selectBaseClassAction select_base_class_action_names++-- | List of every convievable action.+all_actions :: [(B.ByteString,Action)]+all_actions = [continue_action,quit_action,reroll_action,+ pickup_action,drop_action,wield_action,unwield_action,+ next_action,prev_action,normal_action,select_menu_action,+ zoom_in_action,zoom_out_action,sky_on_action,sky_off_action,+ quality_bad,quality_poor,quality_good,quality_super] +++ select_race_actions ++ + select_base_class_actions +++ direction_actions +++ make_what_actions +++ [move_action,turn_action,fire_action,jump_action,attack_action,clear_terrain_action,activate_action,+ make_begin_action,make_end_action]++-- | Find an action with the given name.+lookupAction :: B.ByteString -> (B.ByteString,Action)+lookupAction x = (x,fromMaybe (error $ "tried to operate on an unknown action named " ++ B.unpack x) $ lookup x all_actions)++-- |+-- Answer a complete list of all actions that can be run at this time.+-- Accepts an optional list of action names to choose from; if Nothing,+-- uses all concievable actions as that list.+--+getValidActions :: ActionInput -> Maybe [B.ByteString] -> STM [B.ByteString]+getValidActions action_input actions_list =+ do valid_action_pairs <- mapM (\a -> liftM ((,) a) $ actionValid action_input $ snd a) $ maybe all_actions (map lookupAction) actions_list+ return $ case () of+ () | any (isUndecided . snd) valid_action_pairs -> []+ () | otherwise -> map (fst . fst) $ filter (isGo . snd) valid_action_pairs++-- |+-- Takes a list of action names and executes the first action in the list that is valid+-- in the current context, based on each action's action_valid entry.+-- Returns True if an action was taken, False otherwise.+--+takeUserInputAction :: ActionInput -> [B.ByteString] -> STM Bool+takeUserInputAction _ [] = return False+takeUserInputAction action_input action_names =+ do valid_actions <- getValidActions action_input (Just action_names)+ let action = map lookupAction valid_actions+ case length action of+ 0 -> return False+ 1 -> do executeAction action_input $ snd $ head action+ return True+ _ -> do driverSendError (action_driver_object action_input)+ ("Action bindings warning: multiple valid action for binding: " `B.append`+ (B.concat $ intersperse ", " $ map fst action))+ executeAction action_input $ snd $ head action+ return True+
− src/Actions.lhs
@@ -1,241 +0,0 @@--\section{Actions}--\begin{code}-module Actions- (takeUserInputAction,- getValidActions,- ActionInput(..),- select_race_action_names,- select_base_class_action_names,- executeContinueAction)- where--import System.Exit-import Control.Monad.Error-import Driver-import Data.List-import PrintText-import Tables-import Data.Maybe-import System.IO--data ActionInput = ActionInput {- action_driver_object :: DriverObject,- action_print_text_object :: PrintTextObject }--type Action = ActionInput -> ErrorT String IO (IO ())--actionValid :: ActionInput -> Action -> IO Bool-actionValid action_input action = - do result <- runErrorT $ action action_input- return $ either (const False) (const True) result--executeAction :: ActionInput -> Action -> IO ()-executeAction action_input action =- do result <- runErrorT $ action action_input- either (\s -> printText (action_print_text_object action_input) UnexpectedEvent ("unable to execute action: " ++ s))- (id)- result- return ()--executeContinueAction :: ActionInput -> IO ()-executeContinueAction action_input =- do either (const $ return()) id =<< (runErrorT $ (snd continue_action) action_input)- return ()--quit_action :: (String,Action)-quit_action = ("quit",- \_ -> return $ exitWith ExitSuccess)---- |--- Constructs an action such that the action is valid only if it is listed in--- a table from the engine. The first parameter identifies the table column to look up,--- (table name, table id, header to look under). The second parameter is the name of the--- action as it will be send to the engine. The third parameter is the action_param.--- The action_param must be listed under the specified header of the specified table,--- or the action will not be allowed to execute.------ For example: selectTableAction ("people","0","name") "select-person-to-call-state" "call-with-telephone" "carl"--- --- \> game query state--- answer: state select-person-to-call-state--- \> game query people--- begin-table people 0 name--- john--- wendy--- carl--- bob--- end-table--- \> game action call-with-telephone Carl--- --- In this case the action executes because the state is actually select-person-to-call-state and Carl--- is actually listed under the "name" header of the "people" table.------ In practice this function is used for things like the race-selection-state and the class-selection-state--- where we select from a predifined list of possible choices, but the engine further restricts the choices.--- Each possible choice is it's own action that will only return action_valid if it is listed in the appropriate--- table from the engine. ----selectTableAction :: (String,String,String) -> String -> String -> String -> Action-selectTableAction (the_table_name,the_table_id,the_table_header) allowed_state action_name action_param = - \action_input ->- do state <- maybe (fail "") return =<< (lift $ driverGetAnswer (action_driver_object action_input) "state")- guard $ state == allowed_state- table <- maybe (fail "") return =<< (lift $ driverGetTable (action_driver_object action_input) the_table_name the_table_id)- guard $ [action_param] `elem` tableSelect table [the_table_header]- return $ driverAction (action_driver_object action_input) [action_name, action_param]---- |--- An action that depends on the state flag of the game engine and an arbitrary constant parameter.--- For example, actions that operate directionally are parameterized--- on the eight directions (n,s,w,e,nw,ne,sw,se).------ i.e., parameterizedAction "player-turn" "move" "nw" becomes --- ("move-nw",driverAction _ ["move","nw"])----parameterizedAction :: String -> String -> String -> (String,Action)-parameterizedAction allowed_state action_name parameter =- (action_name ++ "-" ++ parameter,- stateGuard allowed_state $ \action_input ->- return $ driverAction (action_driver_object action_input) [action_name,parameter])---- |--- Guard an action to only run during a specific state.----stateGuard :: String -> Action -> Action-stateGuard allowed_state actionM action_input =- do guard =<< (liftM (== Just allowed_state) $ lift $ driverGetAnswer (action_driver_object action_input) "state")- actionM action_input---- |--- An action that depends just on the state flag of the game engine.----stateLinkedAction :: String -> String -> (String,Action)-stateLinkedAction allowed_state action_name = - (action_name,- stateGuard allowed_state $ \action_input ->- return $ driverAction (action_driver_object action_input) [action_name])--continue_action :: (String,Action)-continue_action = ("continue",\action_input ->- do guard =<< (liftM (== Just "yes") $ lift $ driverGetAnswer (action_driver_object action_input) "snapshot")- return $ driverAction (action_driver_object action_input) ["continue"])--moveAction :: String -> (String,Action)-moveAction = parameterizedAction "player-turn" "move"--turnAction :: String -> (String,Action)-turnAction = parameterizedAction "player-turn" "turn"--fireAction :: String -> (String,Action)-fireAction = parameterizedAction "player-turn" "fire"--reroll_action :: (String,Action)-reroll_action = stateLinkedAction "class-selection" "reroll"--pickup_action :: (String,Action)-pickup_action = stateLinkedAction "player-turn" "pickup"--drop_action :: (String,Action)-drop_action = stateLinkedAction "player-turn" "drop"--wield_action :: (String,Action)-wield_action = stateLinkedAction "player-turn" "wield"--unwield_action :: (String,Action)-unwield_action = stateLinkedAction "player-turn" "unwield"--selectRaceAction :: String -> (String,Action)-selectRaceAction s = - (s,selectTableAction ("player-races","0","name") "race-selection" "select-race" s)--selectBaseClassAction :: String -> (String,Action)-selectBaseClassAction s = - (s,selectTableAction ("base-classes","0","class") "class-selection" "select-class" s)--select_race_action_names :: [String]-select_race_action_names = [--"anachronid",- "androsynth",- "ascendant",- "caduceator",- "encephalon",- --"goliath",- --"hellion",- --"kraken",- --"myrmidon",- --"perennial",- "recreant",- "reptilian"]--select_race_actions :: [(String,Action)]-select_race_actions = map selectRaceAction select_race_action_names--select_base_class_action_names :: [String]-select_base_class_action_names = ["barbarian",- "consular",- "engineer",- "forceadept",- "marine",- "ninja",- "pirate",- "scout",- "shepherd",- "thief",- "warrior"]--select_base_class_actions :: [(String,Action)]-select_base_class_actions = map selectBaseClassAction select_base_class_action_names--eight_directions :: [String]-eight_directions = ["n","ne","nw","e","w","se","sw","s"]--move_actions :: [(String,Action)]-move_actions = map moveAction eight_directions--turn_actions :: [(String,Action)]-turn_actions = map turnAction eight_directions--fire_actions :: [(String,Action)]-fire_actions = map fireAction eight_directions--all_actions :: [(String,Action)]-all_actions = [continue_action,quit_action,reroll_action,pickup_action,drop_action,wield_action,unwield_action] ++- select_race_actions ++ - select_base_class_actions ++- move_actions ++- turn_actions ++- fire_actions--lookupAction :: String -> (String,Action)-lookupAction x = (x,fromMaybe (error $ "tried to operate on an unknown action named " ++ x) $ lookup x all_actions)---- |--- Answer a complete list of all actions that can be run at this time.--- Accepts an optional list of action names to choose from; if Nothing,--- uses all concievable actions as that list.----getValidActions :: ActionInput -> Maybe [String] -> IO [String]-getValidActions action_input actions_list = - do valid_action_pairs <- filterM (actionValid action_input . snd) $ - maybe all_actions (map lookupAction) actions_list- return $ map fst valid_action_pairs---- |--- Takes a list of action names and executes the first action in the list that is valid--- in the current context, based on each action's action_valid entry.--- Returns True if an action was taken, False otherwise.----takeUserInputAction :: ActionInput -> [String] -> IO Bool-takeUserInputAction _ [] = return False-takeUserInputAction action_input action_names =- do valid_actions <- getValidActions action_input (Just action_names)- let action = map lookupAction valid_actions- case length action of- 0 -> return False- 1 -> do executeAction action_input $ snd $ head action- return True- _ -> do hPutStrLn stderr ("Action bindings warning: multiple valid action for binding: " ++ (concat $ intersperse ", " $ map fst action))- executeAction action_input $ snd $ head action- return True-\end{code}
+ src/Animation.hs view
@@ -0,0 +1,274 @@+{-# OPTIONS_GHC -fno-warn-orphans #-}+{-# LANGUAGE GeneralizedNewtypeDeriving,+ Arrows,+ MultiParamTypeClasses,+ FlexibleInstances,+ TypeFamilies,+ ExistentialQuantification,+ Rank2Types,+ OverloadedStrings,+ UndecidableInstances #-}++module Animation+ (RSwitch,+ AnimationState,+ RoguestarAnimationObject,+ newRoguestarAnimationObject,+ runRoguestarAnimationObject,+ driverGetAnswerA,+ driverGetTableA,+ printTextA,+ printTextOnce,+ debugA,+ debugOnce,+ donesA,+ printMenuItemA,+ printMenuA,+ clearPrintTextA,+ clearPrintTextOnce,+ libraryA,+ libraryPointAtCamera,+ blockContinue,+ requestPrintTextMode,+ readGlobal,+ randomA)+ where++import RSAGL.Math+import RSAGL.FRP+import RSAGL.Scene hiding (std_scene_layer_hud,std_scene_layer_cockpit,std_scene_layer_local,std_scene_layer_infinite)+import RSAGL.Animation+import RSAGL.Auxiliary.RecombinantState+import Control.Monad.State+import Control.Arrow+import Control.Arrow.Operations+import Driver+import Tables+import System.IO+import System.Random+import PrintText+import Models.Library+import Models.LibraryData+import Data.Maybe+import Keymaps.Keymaps+import Keymaps.CommonKeymap+import Actions+import Data.List+import Data.Ord+import Strings+import Globals+import Control.Concurrent.STM+import qualified Data.ByteString as B++data AnimationState = AnimationState {+ animstate_scene_accumulator :: SceneAccumulator IO,+ animstate_globals :: Globals,+ animstate_driver_object :: FrozenDriver,+ animstate_print_text_object :: PrintTextObject,+ animstate_library :: Library,+ animstate_block_continue :: Bool,+ animstate_print_text_mode :: PrintTextMode,+ animstate_suspended_stm_action :: STM () }++instance CoordinateSystemClass AnimationState where+ getCoordinateSystem = getCoordinateSystem . animstate_scene_accumulator+ storeCoordinateSystem cs as = as { + animstate_scene_accumulator = storeCoordinateSystem cs $ animstate_scene_accumulator as }++instance ScenicAccumulator AnimationState IO where+ accumulateScene sl so as = as { + animstate_scene_accumulator = accumulateScene sl so $ animstate_scene_accumulator as }++instance RecombinantState AnimationState where+ type SubState AnimationState = AnimationState+ clone old = old { animstate_scene_accumulator = clone $ animstate_scene_accumulator old }+ recombine old new = old { + animstate_scene_accumulator = recombine (animstate_scene_accumulator old) (animstate_scene_accumulator new),+ animstate_block_continue = animstate_block_continue old || animstate_block_continue new,+ animstate_print_text_mode = animstate_print_text_mode old `mergePrintTextModes` animstate_print_text_mode new }++-- | The RogueStar switch type.+type RSwitch k t i o m = SimpleSwitch k t AnimationState i o m++instance (CoordinateSystemClass csc,StateOf m ~ csc) => AffineTransformable (FRP e m j p) where+ transform m actionA = proc x -> transformA actionA -< (Affine $ transform m,x)++newtype RoguestarAnimationObject = RoguestarAnimationObject (FRPProgram AnimationState () SceneLayerInfo)++newRoguestarAnimationObject :: (forall e. FRP e (FRP1 AnimationState () SceneLayerInfo) () SceneLayerInfo) -> IO RoguestarAnimationObject+newRoguestarAnimationObject rs_anim =+ liftM RoguestarAnimationObject $ newFRP1Program rs_anim++runRoguestarAnimationObject :: Library ->+ Globals ->+ DriverObject ->+ PrintTextObject ->+ RoguestarAnimationObject ->+ IO Scene+runRoguestarAnimationObject lib globals driver_object print_text_object+ (RoguestarAnimationObject rso) =+ do frozen_driver_object <- atomically $ freezeDriver driver_object+ let anim_state = AnimationState {+ animstate_globals = globals,+ animstate_scene_accumulator = null_scene_accumulator,+ animstate_driver_object = frozen_driver_object,+ animstate_print_text_object = print_text_object,+ animstate_library = lib,+ animstate_block_continue = False,+ animstate_print_text_mode = Limited,+ animstate_suspended_stm_action = return () }+ (result_scene_layer_info,result_animstate) <-+ updateFRPProgram Nothing ((),anim_state) rso+ atomically $+ do when (not $ animstate_block_continue result_animstate) $+ executeContinueAction $+ ActionInput globals driver_object print_text_object+ setPrintTextMode print_text_object $+ animstate_print_text_mode result_animstate+ animstate_suspended_stm_action result_animstate+ assembleScene result_scene_layer_info $+ animstate_scene_accumulator result_animstate++-- | Request an answer from the engine. This will return 'Nothing' until the answer arrives, which may never happen.+driverGetAnswerA :: (StateOf m ~ AnimationState) => FRP e m B.ByteString (Maybe B.ByteString)+driverGetAnswerA = proc query ->+ do driver_object <- arr animstate_driver_object <<< fetch -< ()+ ioAction (\(driver_object_,query_) ->+ atomically $ getAnswer driver_object_ query_) -<+ (driver_object,query)++-- | Request a data table from the engine. This will return 'Nothing' until the entire table arrives, which may never happen.+driverGetTableA :: (StateOf m ~ AnimationState) => FRP e m (B.ByteString,B.ByteString) (Maybe RoguestarTable)+driverGetTableA = proc query ->+ do driver_object <- arr animstate_driver_object <<< fetch -< ()+ ioAction (\(driver_object_,(the_table_name,the_table_id)) ->+ atomically $+ getTable driver_object_ the_table_name the_table_id) -<+ (driver_object,query)++-- | Store an IO action and run it at the end of the frame.+suspendedSTMAction :: (FRPModel m, StateOf m ~ AnimationState) =>+ (i -> STM ()) -> FRP e m i ()+suspendedSTMAction action = proc i ->+ do s <- fetch -< ()+ store -< s { animstate_suspended_stm_action =+ animstate_suspended_stm_action s >> action i }++-- | Print a line of text to the game console. This will print exactly once.+-- Accepts 'Nothing' and prints once immediately when a value is supplied.+printTextOnce :: (FRPModel m, StateOf m ~ AnimationState) => FRP e m (Maybe (TextType,B.ByteString)) ()+printTextOnce = onceA printTextA++printTextA :: (FRPModel m, StateOf m ~ AnimationState) =>+ FRP e m (Maybe (TextType,B.ByteString)) ()+printTextA = proc pt_data ->+ do print_text_object <- arr animstate_print_text_object <<< fetch -< ()+ suspendedSTMAction (\(print_text_object,x) -> case x of+ Nothing -> return ()+ Just (pt_type,pt_string) ->+ printText print_text_object pt_type pt_string)+ -< (print_text_object,pt_data)++-- | Number of dones. (A done is a message from the engine that an change has occured in the game world.)+donesA :: (StateOf m ~ AnimationState) => FRP e m () Integer+donesA = proc () ->+ do driver_object <- arr animstate_driver_object <<< fetch -< ()+ ioAction (atomically . driverDones) -< driver_object++-- | Print a debugging message to 'stderr'. This will print on every frame of animation.+debugA :: (StateOf m ~ AnimationState) => FRP e m (Maybe B.ByteString) ()+debugA = ioAction (maybe (return ()) (B.hPutStrLn stderr))++-- | Print a debugging message to 'stderr'. This will print exactly once.+debugOnce :: (FRPModel m, StateOf m ~ AnimationState) => FRP e m (Maybe B.ByteString) ()+debugOnce = onceA debugA++-- | Get a list of keystrokes that correspond to the specified action, that are valid on the current frame of animation.+-- This can be used to display a menu that correctly indicates what keystroke to press for a given action.+actionNameToKeysA :: (StateOf m ~ AnimationState) => B.ByteString -> FRP e m () [B.ByteString]+actionNameToKeysA action_name = proc () ->+ do animstate <- fetch -< ()+ let action_input = ActionInput (animstate_globals animstate)+ (thawDriver $ animstate_driver_object animstate)+ (animstate_print_text_object animstate)+ ioAction id -< atomically (actionNameToKeys action_input+ common_keymap+ action_name)++-- | Print a menu using 'printMenuItemA'+printMenuA :: (FRPModel m, StateOf m ~ AnimationState) => [B.ByteString] -> FRP e m () ()+printMenuA = foldr (>>>) (arr id) . map printMenuItemA++-- | Print a single menu item including it's keystroke.+printMenuItemA :: (FRPModel m, StateOf m ~ AnimationState) => B.ByteString -> FRP e m () ()+printMenuItemA action_name = proc () ->+ do keys <- actionNameToKeysA action_name -< ()+ printTextA -< fmap (\s -> (Query,s `B.append` " - " `B.append` hrstring action_name)) $ listToMaybe $ sortBy (comparing B.length) keys++-- | Clear all printed text once. This begins a new clean segment of printed text.+clearPrintTextOnce :: (FRPModel m, StateOf m ~ AnimationState) => FRP e m () ()+clearPrintTextOnce = onceA clearPrintTextA <<< arr (const $ Just ())++clearPrintTextA :: (FRPModel m, StateOf m ~ AnimationState) => FRP e m (Maybe ()) ()+clearPrintTextA = proc i ->+ do print_text_object <- arr (animstate_print_text_object) <<< fetch -< ()+ suspendedSTMAction id -<+ when (isJust i) $ clearOutputBuffer print_text_object++-- | Do an action exactly once.+onceA :: (FRPModel m) => (forall n. (FRPModel n, StateOf n ~ StateOf m) => FRP e n (Maybe j) p) -> FRP e m (Maybe j) p+onceA actionA = frp1Context onceA_+ where onceA_ = proc j ->+ do p <- actionA -< j+ switchTerminate -< (if isJust j then (Just $ arr (const p)) else Nothing,p)++-- | Display a library model.+libraryA :: (StateOf m ~ AnimationState,LibraryModelSource lm) =>+ FRP e m (SceneLayer,lm) ()+libraryA = proc (layer,lm) ->+ do q <- readGlobal global_quality_setting -< ()+ lib <- arr animstate_library <<< fetch -< ()+ accumulateSceneA -< (layer,+ sceneObject $ lookupModel lib (toLibraryModel lm) q)++-- | Display a library model that remains oriented toward the camera.+libraryPointAtCamera :: (StateOf m ~ AnimationState,LibraryModelSource lm) =>+ FRP e m (SceneLayer,lm) ()+libraryPointAtCamera = proc (layer,lm) ->+ do q <- readGlobal global_quality_setting -< ()+ lib <- arr animstate_library <<< fetch -< ()+ pointAtCameraA -< (layer,lookupModel lib (toLibraryModel lm) q)++-- | Prevent the engine from auto-continuing. When the engine is in a snapshot state, +-- the client will automatically ask it to step forward either to the next snapshot or+-- the player's turn. This delays the continue action until some animation or+-- text finishes printing.+blockContinue :: (StateOf m ~ AnimationState) => FRP e m Bool ()+blockContinue = proc b ->+ do animstate <- fetch -< ()+ store -< animstate { animstate_block_continue = animstate_block_continue animstate || b }++-- | Change the 'PrintTextMode'. If multiple calls are made to 'requestPrintTextMode', then+-- 'Disabled' takes precedence over all others, while 'Unlimited' takes precedence over 'Limited'.+requestPrintTextMode :: (StateOf m ~ AnimationState) => FRP e m PrintTextMode ()+requestPrintTextMode = proc s ->+ do animstate <- fetch -< ()+ store -< animstate { animstate_print_text_mode = animstate_print_text_mode animstate `mergePrintTextModes` s }++mergePrintTextModes :: PrintTextMode -> PrintTextMode -> PrintTextMode+mergePrintTextModes _ Disabled = Disabled+mergePrintTextModes Disabled _ = Disabled+mergePrintTextModes Limited Unlimited = Unlimited+mergePrintTextModes Unlimited Limited = Unlimited+mergePrintTextModes m _ = m++-- | Read a global variable.+readGlobal :: (StateOf m ~ AnimationState) => (Globals -> TVar g) -> FRP e m () g+readGlobal f = proc () ->+ do globals <- arr animstate_globals <<< fetch -< ()+ ioAction (\globals_ -> atomically $ readTVar $ f globals_) -< globals++-- | Get a bounded random value, as 'randomRIO'. A new value is pulled for each frame of animation.+randomA :: (Random a) => FRP e m (a,a) a+randomA = ioAction randomRIO+
− src/Animation.lhs
@@ -1,199 +0,0 @@-\section{The Roguestar Animation Arrow}--\begin{code}-{-# LANGUAGE GeneralizedNewtypeDeriving, Arrows #-}--module Animation- (RSAnimA,- RSAnimA1,- RSAnimAX,- RoguestarAnimationObject,- newRoguestarAnimationObject,- runRoguestarAnimationObject,- driverGetAnswerA,- driverGetTableA,- printTextA,- printTextOnce,- debugA,- debugOnce,- printMenuItemA,- printMenuA,- clearPrintTextA,- clearPrintTextOnce,- libraryA,- libraryPointAtCamera,- blockContinue,- requestPrintTextMode)- where--import RSAGL.FRP-import RSAGL.CoordinateSystems-import RSAGL.Scene-import RSAGL.AnimationExtras-import Control.Monad.State-import Control.Arrow-import Control.Arrow.Transformer hiding (lift)-import Control.Arrow.Transformer as Arrow-import Control.Arrow.Transformer.State as StateArrow-import Control.Arrow.Operations-import Driver-import Tables-import Control.Concurrent.MVar-import RSAGL.Time-import System.IO-import PrintText-import Models.Library-import Models.LibraryData-import Quality-import Data.Maybe-import Keymaps.Keymaps-import Keymaps.CommonKeymap-import Actions-import Data.List-import Data.Ord-import Strings-import Actions--data AnimationState = AnimationState {- animstate_scene_accumulator :: SceneAccumulator,- animstate_driver_object :: DriverObject,- animstate_print_text_object :: PrintTextObject,- animstate_library :: Library,- animstate_block_continue :: Bool,- animstate_print_text_mode :: PrintTextMode }--instance CoordinateSystemClass AnimationState where- getCoordinateSystem = getCoordinateSystem . animstate_scene_accumulator- storeCoordinateSystem cs as = as { - animstate_scene_accumulator = storeCoordinateSystem cs $ animstate_scene_accumulator as }--instance ScenicAccumulator AnimationState where- accumulateScene sl so as = as { - animstate_scene_accumulator = accumulateScene sl so $ animstate_scene_accumulator as }--newtype IOGuard a = IOGuard { runIOGuard :: IO a } deriving (Functor,Monad)--type RSAnimAX k t i o j p = FRPX k t i o (StateArrow AnimationState (Kleisli IOGuard)) j p-type RSAnimA t i o j p = RSAnimAX Threaded t i o j p-type RSAnimA1 i o j p = RSAnimAX () () i o j p--type RSAnimA_ j p = StateArrow AnimationState (Kleisli IOGuard) j p--newtype RoguestarAnimationObject = RoguestarAnimationObject {- rso_arrow :: MVar (FRPProgram (StateArrow AnimationState (Kleisli IOGuard)) () Camera) }--newRoguestarAnimationObject :: RSAnimA1 () Camera () Camera -> IO RoguestarAnimationObject-newRoguestarAnimationObject rs_anim = - liftM RoguestarAnimationObject $ newMVar $ newFRP1Program rs_anim--runRoguestarAnimationObject :: Library -> DriverObject -> PrintTextObject -> RoguestarAnimationObject -> IO Scene-runRoguestarAnimationObject lib driver_object print_text_object rso =- do old_rso_program <- takeMVar $ rso_arrow rso- t <- getTime- ((result_camera,new_rso_program),result_animstate) <- runIOGuard $ (runKleisli $ StateArrow.runState $ updateFRPProgram old_rso_program) (((),t),- AnimationState {- animstate_scene_accumulator = null_scene_accumulator,- animstate_driver_object = driver_object,- animstate_print_text_object = print_text_object,- animstate_library = lib,- animstate_block_continue = False,- animstate_print_text_mode = Limited })- putMVar (rso_arrow rso) new_rso_program- when (not $ animstate_block_continue result_animstate) $ executeContinueAction $ ActionInput driver_object print_text_object- setPrintTextMode print_text_object $ animstate_print_text_mode result_animstate- assembleScene result_camera $ animstate_scene_accumulator result_animstate--ioA :: (j -> IO p) -> RSAnimAX any t i o j p-ioA action = Arrow.lift $ ioA_ action--ioA_ :: (j -> IO p) -> RSAnimA_ j p-ioA_ action = proc j -> Arrow.lift $ Kleisli (\x -> IOGuard $ action x) -< j--driverGetAnswerA :: RSAnimAX any t i o String (Maybe String)-driverGetAnswerA = proc query ->- do driver_object <- arr animstate_driver_object <<< fetch -< ()- ioA (\(driver_object_,query_) -> driverGetAnswer driver_object_ query_) -< (driver_object,query)--driverGetTableA :: RSAnimAX any t i o (String,String) (Maybe RoguestarTable)-driverGetTableA = proc query ->- do driver_object <- arr animstate_driver_object <<< fetch -< ()- ioA (\(driver_object_,(the_table_name,the_table_id)) -> - driverGetTable driver_object_ the_table_name the_table_id) -< (driver_object,query)--printTextA :: RSAnimAX any t i o (Maybe (TextType,String)) ()-printTextA = Arrow.lift printTextA_--printTextOnce :: RSAnimAX any t i o (Maybe (TextType,String)) ()-printTextOnce = onceA printTextA_ --printTextA_ :: RSAnimA_ (Maybe (TextType,String)) ()-printTextA_ = proc pt_data ->- do print_text_object <- arr animstate_print_text_object <<< fetch -< ()- ioA_ (\(print_text_object,x) -> case x of- Nothing -> return ()- Just (pt_type,pt_string) -> printText print_text_object pt_type pt_string) -< (print_text_object,pt_data)--debugA :: RSAnimAX any t i o (Maybe String) ()-debugA = Arrow.lift debugA_--debugA_ :: RSAnimA_ (Maybe String) ()-debugA_ = Arrow.lift $ Kleisli $ maybe (return ()) (IOGuard . hPutStrLn stderr)--debugOnce :: RSAnimAX any t i o (Maybe String) ()-debugOnce = onceA debugA_--actionNameToKeysA :: String -> RSAnimAX any t i o () [String]-actionNameToKeysA action_name = Arrow.lift $ proc () ->- do animstate <- fetch -< ()- let action_input = ActionInput (animstate_driver_object animstate)- (animstate_print_text_object animstate)- app -< (Arrow.lift $ Kleisli $ const $ IOGuard $ actionNameToKeys action_input common_keymap action_name,())--printMenuA :: [String] -> RSAnimAX any t i o () ()-printMenuA = foldr (>>>) (arr id) . map printMenuItemA--printMenuItemA :: String -> RSAnimAX any t i o () ()-printMenuItemA action_name = proc () ->- do keys <- actionNameToKeysA action_name -< ()- printTextA -< fmap (\s -> (Query,s ++ " - " ++ hrstring action_name)) $ listToMaybe $ sortBy (comparing length) keys--clearPrintTextA :: RSAnimAX any t i o () ()-clearPrintTextA = Arrow.lift clearPrintText_ <<< arr (const $ Just ())--clearPrintTextOnce :: RSAnimAX any t i o () ()-clearPrintTextOnce = onceA clearPrintText_ <<< arr (const $ Just ())--clearPrintText_ :: RSAnimA_ (Maybe ()) ()-clearPrintText_ = proc i ->- do print_text_object <- arr (animstate_print_text_object) <<< fetch -< ()- app -< maybe (arr $ const (),()) (const (Arrow.lift $ Kleisli $ const $ IOGuard $ clearOutputBuffer print_text_object,())) i--onceA :: StateArrow AnimationState (Kleisli IOGuard) (Maybe j) p -> RSAnimAX any t i o (Maybe j) p-onceA actionA = frp1Context onceA_- where onceA_ = proc j -> - do p <- Arrow.lift actionA -< j- switchTerminate -< (if isJust j then (Just $ arr (const p)) else Nothing,p)--libraryA :: RSAnimAX any t i o (SceneLayer,LibraryModel) ()-libraryA = proc (layer,lm) ->- do lib <- arr animstate_library <<< fetch -< ()- accumulateSceneA -< (layer,sceneObject $ lookupModel lib lm Good)--libraryPointAtCamera :: RSAnimAX any t i o (SceneLayer,LibraryModel) ()-libraryPointAtCamera = proc (layer,lm) ->- do lib <- arr animstate_library <<< fetch -< ()- pointAtCameraA -< (layer,lookupModel lib lm Good)--blockContinue :: RSAnimAX any t i o Bool ()-blockContinue = Arrow.lift $ proc b ->- do animstate <- fetch -< ()- store -< animstate { animstate_block_continue = animstate_block_continue animstate || b }--requestPrintTextMode :: RSAnimAX any t i o PrintTextMode ()-requestPrintTextMode = Arrow.lift $ proc s ->- do animstate <- fetch -< ()- store -< animstate { animstate_print_text_mode = case (animstate_print_text_mode animstate,s) of- (_,Disabled) -> Disabled- (Limited,Unlimited) -> Unlimited- (m,_) -> m }-\end{code}
+ src/AnimationBuildings.hs view
@@ -0,0 +1,36 @@+{-# LANGUAGE Arrows, OverloadedStrings, TypeFamilies, FlexibleContexts #-}++module AnimationBuildings+ (buildingAvatar)+ where++import RSAGL.FRP+import Animation+import VisibleObject+import Models.LibraryData+import Control.Arrow+import Scene++type BuildingAvatarSwitch m = AvatarSwitch () () m+type BuildingAvatar e m = FRP e (BuildingAvatarSwitch m) () ()++buildingAvatar :: (FRPModel m) => BuildingAvatar e m+buildingAvatar = proc () ->+ do objectTypeGuard (== "building") -< ()+ m_building_type <- objectDetailsLookup ThisObject "building-type" -< ()+ switchContinue -< (fmap switchTo m_building_type,())+ returnA -< ()+ where switchTo "monolith" = simpleBuildingAvatar Monolith+ switchTo "portal" = simpleBuildingAvatar Portal+ switchTo _ = questionMarkAvatar >>> arr (const ())++simpleBuildingAvatar :: (FRPModel m, LibraryModelSource lm) =>+ lm -> BuildingAvatar e m+simpleBuildingAvatar phase_weapon_model = proc () ->+ do visibleObjectHeader -< ()+ m_orientation <- objectIdealOrientation ThisObject -< ()+ whenJust (transformA libraryA) -< fmap+ (\o -> (o,(scene_layer_local,phase_weapon_model))) m_orientation+ returnA -< ()++
+ src/CommandLine.hs view
@@ -0,0 +1,12 @@+module CommandLine(CommandLineOptions, keymap_name, parseCommandLine) where++import Keymaps.Keymaps++data CommandLineOptions = CommandLineOptions {+ keymap_name :: Maybe KeymapName+}++-- Extremely minimal implementation for now: switch to System.Console.GetOpt or just use a config. file?+parseCommandLine :: [String] -> CommandLineOptions+parseCommandLine [the_keymap_name] = CommandLineOptions $ Just the_keymap_name+parseCommandLine _ = CommandLineOptions Nothing
− src/CommandLine.lhs
@@ -1,14 +0,0 @@-\begin{code}-module CommandLine(CommandLineOptions, keymap_name, parseCommandLine) where--import Keymaps.Keymaps--data CommandLineOptions = CommandLineOptions {- keymap_name :: Maybe KeymapName-}---- Extremely minimal implementation for now: switch to System.Console.GetOpt or just use a config. file?-parseCommandLine :: [String] -> CommandLineOptions-parseCommandLine [the_keymap_name] = CommandLineOptions $ Just the_keymap_name-parseCommandLine _ = CommandLineOptions Nothing-\end{code}
+ src/Driver.hs view
@@ -0,0 +1,347 @@+{-# LANGUAGE OverloadedStrings, PatternGuards #-}+-- | Interacts with Protocol module of roguestar-engine.+module Driver+ (DriverObject,RoguestarEngineState,FrozenDriver,+ freezeDriver,thawDriver,+ DriverClass(..),+ driverSendError,+ driverNoop,+ driverDones,+ newDriverObject,+ driverAction)+ where++import Control.Concurrent.STM+import Control.Concurrent+import Data.List as List+import Data.Map as Map+import Data.Set as Set+import Data.Maybe+import System.IO+import Tables+import RSAGL.FRP.Time+import Control.Applicative+import Control.Monad.Reader+import qualified Data.ByteString.Char8 as B++-- | Contains detailed information about ongoing interaction with the engine.+data DriverObject = DriverObject {+ -- | Unparsed incomming information from the engine. These lines are+ -- stored in reverse order from how they were recieved.+ driver_incomming :: TVar [B.ByteString],+ -- | Unsent outgoing information to the driver. Either a destructive+ -- action or a lossy query.+ driver_outgoing :: TVar (Either B.ByteString [B.ByteString]),+ -- | Unsent outgoing error messages. Stored in reverse order.+ driver_errors :: TVar [B.ByteString],+ -- | Lines already sent to the engine. This is cleared whenever we send+ -- a "game action" line. We retain this because it's wasteful to repeat+ -- queries when nothing has been committed back to the engine.+ driver_cached_lines :: TVar (Set.Set [B.ByteString]),+ -- | Parsed information about the game state. This is cleared whenever+ -- we send a "game action" line.+ driver_engine_state :: TVar RoguestarEngineState,+ -- | Count of the number of times we've recieved "done" from the engine.+ driver_dones :: TVar Integer,+ -- | Count of the number of destructive actions we've sent to the+ -- engine.+ driver_actions :: TVar Integer,+ -- | Current time, updated constantly by the read/write/error loops.+ driver_time :: TVar Time }++-- | A frozen 'DriverObject'. It has a static view of the world as seen at the+-- time that the Driver is fozen, however, the original 'DriverObject' continues+-- to be updated when this 'FrozenDriver' is used.+data FrozenDriver = FrozenDriver {+ _frozen_driver :: DriverObject,+ _frozen_state :: RoguestarEngineState,+ _frozen_actions :: Integer,+ _frozen_dones :: Integer }++-- | Contains all of the information that is known about roguestar-engine at a+-- specific moment in time.+data RoguestarEngineState = RoguestarEngineState {+ -- index by (table_name, table_id)+ restate_tables :: Map (B.ByteString,B.ByteString) RoguestarTable,+ restate_answers :: Map B.ByteString B.ByteString }++-- | Initialize a DriverObject. Call only once in any given process.+newDriverObject :: IO DriverObject+newDriverObject =+ do driver_object <- initialDriverData+ _ <- forkIO $ forever $ driverReadLoop driver_object+ _ <- forkIO $ forever $ driverWriteLoop driver_object+ _ <- forkIO $ forever $ driverErrorLoop driver_object+ return driver_object++initialDriverData :: IO DriverObject+initialDriverData = DriverObject <$>+ newTVarIO [] <*>+ newTVarIO (Right []) <*>+ newTVarIO [] <*>+ newTVarIO Set.empty <*>+ newTVarIO (RoguestarEngineState Map.empty Map.empty) <*>+ newTVarIO 0 <*>+ newTVarIO 0 <*>+ (newTVarIO =<< getTime)++updateTime :: DriverObject -> Time -> STM ()+updateTime driver_object t =+ do old_time <- readTVar (driver_time driver_object)+ when (t > old_time) $+ writeTVar (driver_time driver_object) t++-- | Just write to the engine.+driverWriteLoop :: DriverObject -> IO ()+driverWriteLoop driver_object =+ do hFlush stdout+ (mapM_ B.putStrLn =<<) $ atomically $+ do writes <- readTVar (driver_outgoing driver_object)+ writeTVar (driver_outgoing driver_object) $ Right []+ case writes of+ Left destructive ->+ do writeTVar (driver_cached_lines driver_object) Set.empty+ writeTVar (driver_actions driver_object) . succ =<<+ readTVar (driver_actions driver_object)+ return [destructive]+ Right queries ->+ do when (List.null queries) retry+ return $ reverse queries++driverErrorLoop :: DriverObject -> IO ()+driverErrorLoop driver_object =+ do hFlush stderr+ (mapM_ (B.hPutStrLn stderr) =<<) $ atomically $+ do errs <- readTVar (driver_errors driver_object)+ when (List.null errs) retry+ writeTVar (driver_errors driver_object) []+ return $ reverse errs++-- | Just read from the engine. Whenever 'driverRead' reads an "over", it+-- automatically fires off 'interpretText' to parse the newly read information.+driverReadLoop :: DriverObject -> IO ()+driverReadLoop driver_object =+ do str <- B.getLine+ t <- getTime+ atomically $+ do updateTime driver_object t+ readses <- readTVar (driver_incomming driver_object)+ writeTVar (driver_incomming driver_object) $ str : readses+ when (str == "over") $ interpretText driver_object++-- | A class for DriverObjects.+class DriverClass a where+ -- | Retrieves an answer from the engine. An answer is a single string.+ getAnswer :: a -> B.ByteString -> STM (Maybe B.ByteString)+ -- | Retrieves a table from the engine. These tables make up a kind of+ -- simple relational database.+ getTable :: a -> B.ByteString -> B.ByteString -> STM (Maybe RoguestarTable)++instance DriverClass DriverObject where+ getAnswer driver_object query =+ do driverQuery driver_object [query]+ restate <- readTVar (driver_engine_state driver_object)+ getAnswer restate query+ getTable driver_object the_table_name the_table_id =+ do driverQuery driver_object [the_table_name,the_table_id]+ restate <- readTVar (driver_engine_state driver_object)+ getTable restate the_table_name the_table_id++instance DriverClass RoguestarEngineState where+ getAnswer restate query = return $+ Map.lookup query $ restate_answers restate+ getTable restate the_table_name the_table_id = return $+ (Map.lookup (the_table_name,the_table_id)) $ restate_tables restate++instance DriverClass FrozenDriver where+ getAnswer (FrozenDriver driver_object restate acts _) query =+ do result <- getAnswer restate query+ when (isNothing result) $+ do current_actions <- readTVar (driver_actions driver_object)+ when (current_actions == acts) $+ getAnswer driver_object query >> return ()+ return result+ getTable (FrozenDriver driver_object restate acts _)+ the_table_name the_table_id =+ do result <- getTable restate the_table_name the_table_id+ when (isNothing result) $+ do current_actions <- readTVar (driver_actions driver_object)+ when (current_actions == acts) $+ getTable driver_object the_table_name the_table_id >>+ return ()+ return result++-- | Transmits a read-only query against the state of the engine.+driverSendLossy :: DriverObject -> [B.ByteString] -> STM ()+driverSendLossy driver_object query =+ do cached_lines <- readTVar (driver_cached_lines driver_object)+ when (not $ query `Set.member` cached_lines) $+ do writes <- either (const retry) return =<<+ readTVar (driver_outgoing driver_object)+ writeTVar (driver_outgoing driver_object) $+ Right $ B.unwords query : writes+ writeTVar (driver_cached_lines driver_object) $+ Set.insert query cached_lines++-- | Commit a destructive action to the engine.+driverSendDestructive :: DriverObject ->+ [B.ByteString] ->+ STM ()+driverSendDestructive driver_object strs =+ do _ <- either (const retry) return =<<+ readTVar (driver_outgoing driver_object)+ writeTVar (driver_engine_state driver_object) $+ RoguestarEngineState Map.empty Map.empty+ writeTVar (driver_outgoing driver_object) $ Left $+ B.unwords strs++-- | Transmits a read-only query against the state of the engine.+driverSendError :: DriverObject -> B.ByteString -> STM ()+driverSendError driver_object err =+ writeTVar (driver_errors driver_object) . (err:) =<<+ readTVar (driver_errors driver_object)++-- | Get an immutable driver. This driver still echoes requests+-- to it's parent driver.+freezeDriver :: DriverObject -> STM FrozenDriver+freezeDriver driver_object =+ do restate <- readTVar (driver_engine_state driver_object)+ actions <- readTVar (driver_actions driver_object)+ dones <- readTVar (driver_dones driver_object)+ return $ FrozenDriver driver_object restate actions dones++-- | Not strictly necessary, you can use the original.+thawDriver :: FrozenDriver -> DriverObject+thawDriver (FrozenDriver driver_object _ _ _) = driver_object++-- | Number of times 'done' has been received from the engine.+driverDones :: FrozenDriver -> STM Integer+driverDones (FrozenDriver _ _ _ dones) = return dones++-- | Issue a \"driver query\" to the engine.+driverQuery :: DriverObject -> [B.ByteString] -> STM ()+driverQuery driver_object query = driverSendLossy driver_object $+ "game":"query":query++-- | Commit a \"driver action\" to the engine.+driverAction :: DriverObject -> [B.ByteString] -> STM ()+driverAction driver_object query = driverSendDestructive driver_object $+ "game":"action":query++-- | Issue a no-op to the engine..+driverNoop :: DriverObject -> STM ()+driverNoop driver_object = driverSendDestructive driver_object ["noop"]++{-------------------------------------------------------------------------------+ - This is the parser.+ - ----------------------------------------------------------------------------}++data DriverInterpretationState = DINeutral+ | DIScanningTable RoguestarTable+ | DIError+ deriving (Eq,Show)++-- | Parse 'driver_engine_input_lines'.+interpretText :: DriverObject -> STM ()+interpretText driver_object =+ do final_state <- foldM (interpretLine driver_object) DINeutral =<<+ liftM reverse (readTVar $ driver_incomming driver_object)+ when (final_state /= DINeutral) $+ do driverSendError driver_object+ "interpretText concluded in a non-neutral state, which was:"+ driverSendError driver_object $ B.pack $ show final_state+ writeTVar (driver_incomming driver_object) []++-- | 'interpretLine' is a simple line-by-line parser invoked using 'foldM'.+interpretLine :: DriverObject ->+ DriverInterpretationState ->+ B.ByteString ->+ STM DriverInterpretationState+interpretLine _ DIError _ = return DIError++-- Ignore empty lines.+interpretLine _ di_state str | List.null (B.words str) = do return di_state++-- Report errors. \"protocol-error\" means that engine believes that we screwed+-- up. \"error\" means that the engine admits it screwed up.+interpretLine driver_object _ str | (head $ B.words str) `elem`+ ["protocol-error:", "error:"] =+ do driverSendError driver_object str+ return DIError++-- Engine acknowledges completed database update.+interpretLine driver_object DINeutral "done" =+ do writeTVar (driver_engine_state driver_object) $+ RoguestarEngineState Map.empty Map.empty+ writeTVar (driver_dones driver_object) . succ =<<+ readTVar (driver_dones driver_object)+ return DINeutral++interpretLine driver_object di_state "done" =+ do driverSendError driver_object $+ "Driver raised protocol error: unexpected \"done\" in " `B.append`+ (B.pack $ show di_state) `B.append` " state."+ return DIError++-- Engine is finished answering a query or action.+-- (But it may continue to spew answers to other queries.)+interpretLine _ DINeutral "over" = return DINeutral++interpretLine driver_object (DIScanningTable {}) "over" =+ do driverSendError driver_object $+ "Driver raised protocol error: 'over' issued while reading a data table"+ return DIError++-- Engine is answering a question.+interpretLine driver_object DINeutral str+ | ["answer:",key,value] <- B.words str =+ do engine_state <- readTVar (driver_engine_state driver_object)+ writeTVar (driver_engine_state driver_object) $+ engine_state {+ restate_answers = Map.insert key value+ (restate_answers engine_state) }+ return DINeutral++-- Engine is opening a new table.+interpretLine driver_object DINeutral str+ | ("begin-table":tname:tid:theaders) <- B.words str+ , not (List.null theaders) =+ do t <- readTVar (driver_time driver_object)+ return $ DIScanningTable $ RoguestarTable {+ table_created = t,+ table_name = tname,+ table_id = tid,+ table_header = theaders,+ table_data = [] }++interpretLine driver_object _ str | (head $ B.words str) == "begin-table" =+ do driverSendError driver_object+ "Driver raised protocol error: incomplete begin-table header"+ return DIError++-- Engine is closing a table.+interpretLine driver_object (DIScanningTable table) str+ | (head $ B.words str) == "end-table" =+ do engine_state <- readTVar (driver_engine_state driver_object)+ writeTVar (driver_engine_state driver_object) $+ engine_state { restate_tables =+ Map.insert (table_name table, table_id table)+ (table { table_data = reverse $ table_data table }) $+ restate_tables engine_state }+ return DINeutral++-- Inside an open table, read a single row of that table.+interpretLine driver_object (DIScanningTable table) str =+ let table_row = B.words str+ in (if length table_row == (length $ table_header table)+ then return $ DIScanningTable (table {+ table_data = table_row : table_data table })+ else do driverSendError driver_object+ "Driver raised protocol error: malformed table row"+ return DIError )++-- If we don't know what else to do, just print to stderr.+interpretLine driver_object _ str =+ do driverSendError driver_object str+ return DINeutral+
− src/Driver.lhs
@@ -1,237 +0,0 @@-\section{Driver}--\begin{code}-module Driver- (DriverObject,- driverNoop,- newDriverObject,- driverRead,- driverGetAnswer,- driverGetTable,- driverAction)- where--import Data.Maybe-import Control.Monad-import Data.IORef-import Data.List-import System.IO-import Tables-import RSAGL.Time--data RoguestarEngineState = RoguestarEngineState { - restate_tables :: [RoguestarTable], - restate_answers :: [(String,String)] }--data DriverData = DriverData {- driver_engine_input_lines :: [String],- driver_engine_input_line_fragment :: String,- driver_engine_output_lines :: [String],- driver_engine_state :: RoguestarEngineState,- driver_dones :: Integer }--newtype DriverObject = DriverObject (IORef DriverData)--newDriverObject :: IO DriverObject-newDriverObject = liftM DriverObject $ newIORef initial_driver_data--initial_driver_data :: DriverData-initial_driver_data = DriverData {- driver_engine_input_lines = [],- driver_engine_input_line_fragment = [],- driver_engine_output_lines = [],- driver_engine_state = RoguestarEngineState [] [],- driver_dones = 0 }--driverGet :: DriverObject -> (DriverData -> a) -> IO a-driverGet (DriverObject ioref) f = liftM f $ readIORef ioref--modifyDriver :: DriverObject -> (DriverData -> DriverData) -> IO () -modifyDriver (DriverObject ioref) f = modifyIORef ioref f-\end{code}--\begin{code}-driverNoop :: DriverObject -> IO ()-driverNoop driver_object = driverWrite driver_object "noop\n"-\end{code}--\subsection{Getting Protocol Answers}--\texttt{driverGetAnswer} retrieves an answer from the engine. An answer is a single string.--\begin{code}-driverGetAnswer :: DriverObject -> String -> IO (Maybe String)-driverGetAnswer driver_object query =- do driverQuery driver_object [query]- driverGet driver_object (lookup query . restate_answers . driver_engine_state)-\end{code}--\subsection{Getting Protocol Tables}--\testtt{driverGetTable} retrieves a table from the engine. These tables make up a kind of simple relational database.--\begin{code}-driverGetTable :: DriverObject -> String -> String -> IO (Maybe RoguestarTable)-driverGetTable driver_object the_table_name the_table_id =- do driverQuery driver_object [the_table_name,the_table_id]- driverGet driver_object (find (\x -> table_name x == the_table_name && table_id x == the_table_id) . restate_tables . driver_engine_state)-\end{code}--\texttt{driverQuery} transmits a read-only query against the state of the engine.--\begin{code}-driverQuery :: DriverObject -> [String] -> IO ()-driverQuery driver_object query = driverWrite driver_object $ "game query " ++ unwords query ++ "\n"-\end{code}--\begin{code}-driverAction :: DriverObject -> [String] -> IO ()-driverAction driver_object strs = - do modifyDriver driver_object $ \driver -> driver { driver_engine_state = RoguestarEngineState [] []}- driverWrite driver_object $ "game action " ++ unwords strs ++ "\n"-\end{code}--\texttt{driverWrite} writes the specified command to standard output, automatically interleaving calls to read.-\texttt{driverWrite} will never write the same string twice between calls to \texttt{driverReset}.--\begin{code}-driverWrite :: DriverObject -> String -> IO ()-driverWrite driver_object str = - do already_sent <- driverGet driver_object (elem str . driver_engine_output_lines)- unless already_sent $- do modifyDriver driver_object $ \driver -> driver { driver_engine_output_lines=str:driver_engine_output_lines driver }- driverWrite_ driver_object str- driverRead driver_object--driverWrite_ :: DriverObject -> String -> IO ()-driverWrite_ driver_object "" = - do hFlush stdout- driverRead driver_object-driverWrite_ driver_object str = - do driverRead driver_object- putChar $ head str- --hPutChar stderr $ head str -- uncomment to see everything we write in stderr- driverWrite_ driver_object $ tail str-\end{code}--\texttt{maybeRead} reads one character if it is available.--\begin{code}-maybeRead :: IO (Maybe Char)-maybeRead = do ready <- hReady stdin- case ready of- False -> return Nothing- True -> do char <- getChar- return $ Just char-\end{code}--\texttt{driverRead} reads, parses, and stores data from the engine.--\begin{code}-driverRead :: DriverObject -> IO ()-driverRead driver_object = - do maybe_next_char <- maybeRead- case maybe_next_char of- Nothing -> return ()- Just '\n' -> do modifyDriver driver_object- (\driver -> driver { driver_engine_input_lines= - (reverse $ driver_engine_input_line_fragment driver) :- driver_engine_input_lines driver,- driver_engine_input_line_fragment=""});- driverUpdate driver_object- Just next_char -> modifyDriver driver_object - (\driver -> driver { driver_engine_input_line_fragment=(next_char : - driver_engine_input_line_fragment driver) })- when (isJust maybe_next_char) $ do --hPutChar stderr $ fromJust maybe_next_char -- uncomment to see everything we read in stderr- driverRead driver_object-\end{code}--\texttt{driverUpdate} parses stored information in the driver, generating answers and tables.--\begin{code}-driverUpdate :: DriverObject -> IO ()-driverUpdate driver_object =- do head_line <- driverGet driver_object (head . driver_engine_input_lines)- when (head_line == "over") $ interpretText driver_object--data DriverInterpretationState = DINeutral- | DIScanningTable RoguestarTable- | DIError- deriving (Eq,Show)--interpretText :: DriverObject -> IO ()-interpretText driver_object = - do final_state <- foldM (interpretLine driver_object) DINeutral =<< driverGet driver_object (reverse . driver_engine_input_lines)- when (final_state /= DINeutral) $ do hPutStr stderr "interpretText concluded in a non-neutral state, which was:"- hPutStr stderr (show final_state)- modifyDriver driver_object $ \driver -> initial_driver_data { - driver_dones = driver_dones driver,- driver_engine_state = driver_engine_state driver }- modifyDriver driver_object $ \driver -> driver { driver_engine_input_lines = [] }--modifyEngineState :: DriverObject -> (RoguestarEngineState -> RoguestarEngineState) -> IO ()-modifyEngineState driver_object f = modifyDriver driver_object $ \driver -> driver { driver_engine_state = f $ driver_engine_state driver } --interpretLine :: DriverObject -> DriverInterpretationState -> String -> IO DriverInterpretationState-interpretLine _ DIError _ = return DIError--interpretLine _ di_state "" = do return di_state -- ignore empty lines--interpretLine _ _ str | (head $ words str) == "protocol-error:" = - do hPutStr stderr str- return DIError--interpretLine _ _ str | (head $ words str) == "error:" = - do hPutStr stderr str- return DIError--interpretLine driver_object DINeutral "done" =- do modifyDriver driver_object $ \driver -> driver { driver_engine_state = RoguestarEngineState [] [],- driver_engine_output_lines = [],- driver_dones = driver_dones driver + 1 }- return DINeutral--interpretLine _ di_state "done" = - do hPutStr stderr ("client-side protocol error: unexpected \"done\" in " ++ (show di_state) ++ " state.")- return DIError--interpretLine _ DINeutral "over" = return DINeutral--interpretLine _ (DIScanningTable {}) "over" = - do hPutStr stderr "client-side protocol error: 'over' issued while reading a data table"- return DIError--interpretLine driver_object DINeutral str | (head $ words str) == "answer:" && (length $ words str) == 3 =- do modifyEngineState driver_object (\engine_state -> engine_state { restate_answers = (words str !! 1,words str !! 2):restate_answers engine_state })- return DINeutral--interpretLine _ DINeutral str | (head $ words str) == "begin-table" && (length $ words str) > 3 = - do t <- getTime- let table_start_data = words str- return $ DIScanningTable $ RoguestarTable {- table_created = t,- table_name = table_start_data !! 1,- table_id = table_start_data !! 2,- table_header = drop 3 table_start_data,- table_data = []}--interpretLine _ _ str | (head $ words str) == "begin-table" =- do hPutStr stderr "client-side protocol error: incomplete begin-table header"- return DIError--interpretLine driver_object (DIScanningTable table) str | (head $ words str) == "end-table" =- do modifyEngineState driver_object (\engine_state -> engine_state { restate_tables = (table { table_data=reverse $ table_data table}):restate_tables engine_state })- return DINeutral--interpretLine _ (DIScanningTable table) str = - let table_row = words str- in (if length table_row == (length $ table_header table)- then return $ DIScanningTable (table { table_data = table_row : table_data table })- else do hPutStr stderr "client-side protocol error: malformed table row"- return DIError )--interpretLine _ _ str = - do hPutStr stderr str- return DINeutral--\end{code}
src/Keymaps/CommonKeymap.hs view
@@ -1,59 +1,42 @@+{-# LANGUAGE OverloadedStrings #-}+ module Keymaps.CommonKeymap- (commonMovementKeymap,MovementKeymap(..),common_keymap)+ (common_keymap) where -import Data.Char- import Keymaps.Keymaps -data MovementKeymap = MovementKeymap {- mk_n, mk_s, mk_e, mk_w, mk_nw, mk_ne, mk_sw, mk_se :: String }--commonMovementKeymap :: MovementKeymap -> Keymap-commonMovementKeymap mk = [- (mk_n mk,"move-n"),- (mk_s mk,"move-s"),- (mk_e mk,"move-e"),- (mk_w mk,"move-w"),- (mk_nw mk,"move-nw"),- (mk_ne mk,"move-ne"),- (mk_sw mk,"move-sw"),- (mk_se mk,"move-se"),- (">t" ++ mk_n mk,"turn-n"),- (">t" ++ mk_s mk,"turn-s"),- (">t" ++ mk_e mk,"turn-e"),- (">t" ++ mk_w mk,"turn-w"),- (">t" ++ mk_nw mk,"turn-nw"),- (">t" ++ mk_ne mk,"turn-ne"),- (">t" ++ mk_sw mk,"turn-sw"),- (">t" ++ mk_se mk,"turn-se"),- (">f" ++ mk_n mk,"fire-n"),- (">f" ++ mk_s mk,"fire-s"),- (">f" ++ mk_e mk,"fire-e"),- (">f" ++ mk_w mk,"fire-w"),- (">f" ++ mk_nw mk,"fire-nw"),- (">f" ++ mk_ne mk,"fire-ne"),- (">f" ++ mk_sw mk,"fire-sw"),- (">f" ++ mk_se mk,"fire-se")]- common_keymap :: Keymap common_keymap = [- (":move-n","move-n"),- (":move-s","move-s"),- (":move-w","move-w"),- (":move-e","move-e"),- (":move-nw","move-nw"),- (":move-ne","move-ne"),- (":move-sw","move-sw"),- (":move-se","move-se"),- (":turn-n","turn-n"),- (":turn-s","turn-s"),- (":turn-w","turn-w"),- (":turn-e","turn-e"),- (":turn-nw","turn-nw"),- (":turn-ne","turn-ne"),- (":turn-sw","turn-sw"),- (":turn-se","turn-se"),+ (":north","n"),+ (":northeast","ne"),+ (":east","e"),+ (":southeast","sw"),+ (":south","s"),+ (":southwest","sw"),+ (":west","w"),+ (":northwest","nw"),+ (":escape","escape"),+ (":next","next"),+ (":prev","prev"),+ ("\n","select-menu"),+ ("\r","select-menu"),+ (">:::KeyDown","next"),+ (">:::KeyUp","prev"),+ ("\ESC","normal"),+ (":move","move"),+ ("t","turn"),+ (":turn","turn"),+ ("J","jump"),+ (":jump","jump"),+ ("F","attack"),+ (":attack","attack"),+ ("f","fire"),+ (":fire","fire"),+ ("c","clear-terrain"),+ (":clear-terrain","clear-terrain"),+ ("a","activate"),+ (":activate","activate"), ("x","anachronid"), (":anachronid","anachronid"), ("a","androsynth"),@@ -102,7 +85,26 @@ (":thief","thief"), ("w","warrior"), (":warrior","warrior"),+ ("m","make-begin"),+ ("p","pistol"),+ (":pistol","pistol"),+ ("c","carbine"),+ (":carbine","carbine"),+ ("r","rifle"),+ (":rifle","rifle"),+ ("f","fleuret"),+ (":fleuret","fleuret"),+ ("s","sabre"),+ (":sabre","sabre"),+ ("\n","make-end"),+ ("\r","make-end"), ("#quit","quit"),+ ("#sky-on","sky-on"),+ ("#sky-off","sky-off"),+ ("#quality-1","quality-bad"),+ ("#quality-2","quality-poor"),+ ("#quality-3","quality-good"),+ ("#quality-4","quality-super"), (",","pickup"), (":pickup","pickup"), ("d","drop"),@@ -111,12 +113,8 @@ (":wield","wield"), ("-","unwield"), (":unwield","unwield"),- (":fire-n","fire-n"),- (":fire-s","fire-s"),- (":fire-w","fire-w"),- (":fire-e","fire-e"),- (":fire-new","fire-nw"),- (":fire-ne","fire-ne"),- (":fire-sw","fire-sw"),- (":fire-se","fire-sw"),- (":continue","continue")]+ (":continue","continue"),+ ("]","zoom-in"),+ (":zoom-in","zoom-in"),+ ("[","zoom-out"),+ (":zoom-out","zoom-out")]
+ src/Keymaps/Keymaps.hs view
@@ -0,0 +1,89 @@+-- | 'Keymap's are simple mappings from a sequence of keystrokes to the+-- names of various commands.+{-# LANGUAGE OverloadedStrings #-}++module Keymaps.Keymaps+ (Keymap,+ KeymapName,+ fixKeymap,+ filterKeySequence,+ keysToActionNames,+ actionNameToKeys)+ where++import Actions+import Data.List+import Control.Monad+import Control.Concurrent.STM+import qualified Data.ByteString.Char8 as B++type Keymap = [(B.ByteString,B.ByteString)]+type KeymapName = String++-- 'fixKeymap' processes a keymap to make it usable, by:+-- * removing erroneous whitespace+-- * placing \\n and \\r at the end of multi-key keystrokes that should be+-- activated via the enter key.+-- * passing keystrokes that begin with the escape character '>' as raw+-- keystrokes.++fixKeymap :: Keymap -> Keymap+fixKeymap = concatMap $ \(keystrokes,action_name) ->+ case () of+ () | B.null keystrokes -> []+ () | B.length keystrokes == 1 -> [(keystrokes,action_name)]+ () | B.head keystrokes == '>' ->+ [(B.drop 1 keystrokes,action_name)]+ () -> let fixed_keystrokes =+ B.concat $ intersperse "-" $ B.words keystrokes+ in [(fixed_keystrokes `B.append` "\r",action_name),+ (keystrokes `B.append` "\n",action_name)]++-- | 'validKeyMap' reduces a 'Keymap' to one that contains only those actions+-- that are valid at the instant of the atomic transaction.+validKeyMap :: ActionInput -> Keymap -> STM [(B.ByteString,B.ByteString)]+validKeyMap action_input raw_keymap =+ do valid_actions <- getValidActions action_input Nothing+ return $ filter (\x -> snd x `elem` valid_actions) raw_keymap++-- | 'filterKeySequence' transforms a key sequence, providing services such as:+-- * blanking the input buffer is there is no possible completion+-- * performing tab complation+filterKeySequence :: ActionInput -> Keymap -> B.ByteString -> STM B.ByteString+filterKeySequence _ _ key_sequence | (length $ B.words key_sequence) /= 1 = return ""+filterKeySequence action_input keymap key_sequence =+ do valid_key_map <- validKeyMap action_input keymap+ let is_tab_key_completion = B.last key_sequence == '\t'+ let stripped_key_sequence = B.unwords $ B.words key_sequence+ let possible_completions =+ filter (\x -> elem stripped_key_sequence $ B.inits x) $+ map fst $ valid_key_map+ return $ case length possible_completions of+ -- if there are no possible completions, clear the buffer+ 0 -> ""+ -- if the trailing input isn't a tab, chill+ _ | not is_tab_key_completion -> stripped_key_sequence+ -- if there's only one completion, get it, but strip+ -- the trailing return in case they didn't want+ -- to exec that completion.+ 1 -> if key_sequence /= head possible_completions+ then B.init $ head possible_completions+ else key_sequence+ -- get the best completion we can find+ _ -> maximumBy (\x y -> compare (B.length x) (B.length y)) $+ foldr1 intersect $ map B.inits possible_completions++-- | 'keysToActionNames' gets a list of the names of all action that could be+-- executed by the given key sequence at this instant. A null result+-- indicates that the key sequence isn't a valid command. A result with more+-- than one element indicates a collision in the key map, which is an error.++keysToActionNames :: ActionInput -> Keymap -> B.ByteString -> STM [B.ByteString]+keysToActionNames action_input keymap key_sequence =+ liftM (map snd . filter (\x -> fst x == key_sequence)) $ validKeyMap action_input keymap ++-- 'actionNameToKeys' provides reverse lookup versus keysToActionNames.+actionNameToKeys :: ActionInput -> Keymap -> B.ByteString -> STM [B.ByteString]+actionNameToKeys action_input keymap action_name =+ liftM (map fst . filter (\x -> snd x == action_name)) $ validKeyMap action_input keymap+
− src/Keymaps/Keymaps.lhs
@@ -1,77 +0,0 @@-\section{Keymaps}--\texttt{Keymap}s are simple mappings from a sequence of keystrokes to the names of various commands.--\begin{code}-module Keymaps.Keymaps- (Keymap,- KeymapName,- fixKeymap,- filterKeySequence,- keysToActionNames,- actionNameToKeys)- where--import Actions-import Data.List-import Control.Monad--type Keymap = [(String,String)]-type KeymapName = String-\end{code}--\texttt{fixKeymap} processes a keymap to make it usable. If you type in a multi-character command and it executes before you press enter,-you forgot to use \texttt{fixKeymap}.--\begin{code}-fixKeymap :: Keymap -> Keymap-fixKeymap = concatMap $ \(x,y) -> - case x of- [c] -> [([c],y)]- ('>':keystrokes) | (not $ null keystrokes) -> [(keystrokes,y)]- command -> let keystrokes = concat $ intersperse "-" $ words command in [(keystrokes ++ "\r",y),(keystrokes ++ "\n",y)]-\end{code}--\texttt{validKeyMap} reduces a \texttt{Keymap} to one that contains only those actions that are valid at this instant.--\begin{code}-validKeyMap :: ActionInput -> Keymap -> IO [(String,String)]-validKeyMap action_input raw_keymap =- do valid_actions <- getValidActions action_input Nothing- return $ filter (\x -> snd x `elem` valid_actions) raw_keymap-\end{code}--\texttt{filterKeySequence} transforms a key sequence into either the empty string, if it isn't the prefix of any valid action,-or completes the key sequence if it is the prefix of exactly one valid action.--\begin{code}-filterKeySequence :: ActionInput -> Keymap -> String -> IO String-filterKeySequence _ _ key_sequence | (length $ words key_sequence) == 0 = return ""-filterKeySequence action_input keymap key_sequence =- do valid_key_map <- validKeyMap action_input keymap- let possible_completions = filter (\x -> elem key_sequence $ inits x) $ map fst $ valid_key_map- return $ case length possible_completions of- 0 -> ""- 1 -> if key_sequence /= head possible_completions- then init $ head possible_completions- else key_sequence- _ -> maximumBy (\x -> \y -> compare (length x) (length y)) $ foldr1 intersect $ map inits possible_completions-\end{code}--\texttt{keysToActionNames} gets a list of the names of all action that could be exected by the given key sequence at this instant.-A result of zero indicates that the key sequence isn't a valid command. A result with more than one element indicates-a collision in the key map, which is an error.--\begin{code}-keysToActionNames :: ActionInput -> Keymap -> String -> IO [String]-keysToActionNames action_input keymap key_sequence = - liftM (map snd . filter (\x -> fst x == key_sequence)) $ validKeyMap action_input keymap -\end{code}--\texttt{actionNameToKeys} provides reverse lookup versus keysToActionNames.--\begin{code}-actionNameToKeys :: ActionInput -> Keymap -> String -> IO [String]-actionNameToKeys action_input keymap action_name = - liftM (map fst . filter (\x -> snd x == action_name)) $ validKeyMap action_input keymap-\end{code}
src/Keymaps/NumpadKeymap.hs view
@@ -1,21 +1,21 @@+{-# LANGUAGE OverloadedStrings #-}+ module Keymaps.NumpadKeymap- (numpad_movement_keymap,numpad_keymap)+ (numpad_keymap) where import Keymaps.CommonKeymap import Keymaps.Keymaps -numpad_movement_keymap :: MovementKeymap-numpad_movement_keymap = MovementKeymap {- mk_n = "8",- mk_s = "2",- mk_w = "4",- mk_e = "6",- mk_nw = "7",- mk_ne = "9",- mk_sw = "1",- mk_se = "3" }- numpad_keymap :: Keymap-numpad_keymap =- commonMovementKeymap numpad_movement_keymap ++ common_keymap+numpad_keymap = common_keymap +++ [("8","n"),+ ("2","s"),+ ("4","w"),+ ("6","e"),+ ("7","nw"),+ ("9","ne"),+ ("1","sw"),+ ("3","se"),+ ("8","prev"),+ ("2","next")]
src/Keymaps/VIKeymap.hs view
@@ -1,21 +1,21 @@+{-# LANGUAGE OverloadedStrings #-}+ module Keymaps.VIKeymap- (vi_movement_keymap,vi_keymap)+ (vi_keymap) where import Keymaps.Keymaps import Keymaps.CommonKeymap -vi_movement_keymap :: MovementKeymap-vi_movement_keymap = MovementKeymap {- mk_n = "k",- mk_s = "j",- mk_w = "h",- mk_e = "l",- mk_nw = "y",- mk_ne = "u",- mk_sw = "b",- mk_se = "n" }- vi_keymap :: Keymap-vi_keymap =- commonMovementKeymap vi_movement_keymap ++ common_keymap+vi_keymap = common_keymap +++ [("k","n"),+ ("j","s"),+ ("h","w"),+ ("l","e"),+ ("y","nw"),+ ("u","ne"),+ ("b","sw"),+ ("n","se"),+ ("j","next"),+ ("k","prev")]
+ src/Limbs.hs view
@@ -0,0 +1,122 @@+{-# LANGUAGE Arrows, OverloadedStrings, TypeFamilies #-}++module Limbs+ (bothArms,+ bothLegs)+ where++import VisibleObject+import Animation+import RSAGL.Animation+import RSAGL.Math+import Models.LibraryData+import Control.Arrow+import RSAGL.Scene+import RSAGL.FRP+import EventUtils+import RSAGL.Types++-- | Animate an arbitrary articulated joint.+libraryJointAnimation :: (FRPModel m, StateOf m ~ AnimationState) => RSdouble -> LibraryModel -> LibraryModel -> FRP e m Joint ()+libraryJointAnimation maximum_length upper lower = proc joint_info ->+ jointAnimation (proc () -> transformA libraryA -< (Affine $ scale' (maximum_length/2),(std_scene_layer_local,upper)))+ (proc () -> transformA libraryA -< (Affine $ scale' (maximum_length/2),(std_scene_layer_local,lower))) -< + joint_info++-- | Animate an articulated joint holding constant the bend vector and arm length.+arm :: (FRPModel m, StateOf m ~ AnimationState) => LibraryModel -> LibraryModel -> Vector3D -> RSdouble -> FRP e m (Point3D,Point3D) Joint+arm arm_upper arm_lower bend_vector maximum_length = proc (shoulder_point,hand_point) ->+ do let joint_info = joint bend_vector shoulder_point maximum_length hand_point+ libraryJointAnimation maximum_length arm_upper arm_lower -< joint_info + returnA -< joint_info++-- | Animate a right arm. This animation is aware of what tool the current creature (based on thread ID) is holding and raises the arm forward+-- while holding any tool.+rightArm :: (FRPModel m, StateOf m ~ AnimationState, ThreadIDOf m ~ Maybe Integer) => LibraryModel -> LibraryModel -> Vector3D -> Point3D -> RSdouble -> Point3D -> FRP e m () Joint+rightArm arm_upper arm_lower bend_vector shoulder_anchor maximum_length hand_rest = proc () ->+ do m_time_recent_attack <- recentAttack ThisObject -< ()+ t_now <- threadTime -< ()+ m_tool_type <- objectDetailsLookup (WieldedTool ThisObject) "tool-type" -< () + is_wielding <- isWielding ThisObject -< ()+ hand_point <- approachA 0.1 (perSecond 1.0) -< case m_time_recent_attack of+ Just t | t_now < t `add` fromSeconds 0.5 && m_tool_type == Just "sword" -> translate (Vector3D maximum_length 0 0) shoulder_anchor+ Just t | t_now < t `add` fromSeconds 0.3 && m_tool_type == Nothing -> translate (Vector3D 0 0 $ maximum_length / 4) shoulder_anchor+ Just t | t_now < t `add` fromSeconds 1.0 && m_tool_type == Nothing -> translate (Vector3D 0 0 maximum_length) shoulder_anchor+ _ | is_wielding -> translate (Vector3D 0 0 maximum_length) shoulder_anchor+ _ | otherwise -> hand_rest+ arm arm_upper arm_lower bend_vector maximum_length -< (shoulder_anchor,hand_point)++-- | Animate a left arm, which is always held at the side.+leftArm :: (FRPModel m, StateOf m ~ AnimationState, ThreadIDOf m ~ Maybe Integer) => LibraryModel -> LibraryModel -> Vector3D -> Point3D -> RSdouble -> Point3D -> FRP e m () Joint+leftArm arm_upper arm_lower bend_vector shoulder_anchor maximum_length hand_rest = + proc () -> arm arm_upper arm_lower bend_vector maximum_length -< (shoulder_anchor,hand_rest)++-- | Animate two arms. The parameters describe the right arm, and are swapped across the yz plane to produce left arm parameters.+bothArms :: (FRPModel m,+ StateOf m ~ AnimationState,+ ThreadIDOf m ~ Maybe Integer,+ LibraryModelSource lm1,+ LibraryModelSource lm2) =>+ lm1 ->+ lm2 ->+ Vector3D ->+ Point3D ->+ RSdouble ->+ Point3D ->+ FRP e m () (Joint,Joint)+bothArms arm_upper+ arm_lower+ bend_vector+ shoulder_anchor+ maximum_length+ hand_rest = proc () ->+ do left_joint <- leftArm (toLibraryModel arm_upper)+ (toLibraryModel arm_lower)+ (swapX bend_vector)+ (swapX shoulder_anchor)+ maximum_length+ (swapX hand_rest) -< ()+ right_joint <- rightArm (toLibraryModel arm_upper)+ (toLibraryModel arm_lower)+ bend_vector+ shoulder_anchor+ maximum_length+ hand_rest -< ()+ returnA -< (left_joint,right_joint)++-- | Animate legs, which automatically know how to take steps when moved.+bothLegs :: (FRPModel m,+ StateOf m ~ AnimationState,+ LibraryModelSource lm1,+ LibraryModelSource lm2) =>+ lm1 ->+ lm2 ->+ Vector3D ->+ Point3D ->+ RSdouble ->+ Point3D ->+ FRP e m () ()+bothLegs leg_upper+ leg_lower+ bend_vector+ hip_anchor+ maximum_length+ foot_rest = proc () ->+ do legs [leg bend_vector+ hip_anchor+ maximum_length+ foot_rest+ (libraryJointAnimation maximum_length+ (toLibraryModel leg_upper)+ (toLibraryModel leg_lower)),+ leg bend_vector+ (swapX hip_anchor)+ maximum_length+ (swapX foot_rest)+ (libraryJointAnimation maximum_length+ (toLibraryModel leg_upper)+ (toLibraryModel leg_lower))]+ -< ()++swapX :: (AffineTransformable a) => a -> a+swapX = scale (Vector3D (-1.0) 1.0 1.0)
− src/Limbs.lhs
@@ -1,66 +0,0 @@-\section{Limbs}--\begin{code}-{-# LANGUAGE Arrows #-}--module Limbs- (rightArm,- leftArm,- bothArms,- bothLegs)- where-import VisibleObject-import Animation-import RSAGL.Joint-import RSAGL.Affine-import Models.LibraryData-import Control.Arrow-import RSAGL.Vector-import RSAGL.CoordinateSystems-import RSAGL.Scene-import RSAGL.InverseKinematics-import Data.Maybe-import RSAGL.Time--libraryJointAnimation :: Double -> LibraryModel -> LibraryModel -> RSAnimAX any t i o Joint ()-libraryJointAnimation maximum_length upper lower = proc joint_info ->- jointAnimation (proc () -> transformA libraryA -< (Affine $ scale' (maximum_length/2),(Local,upper)))- (proc () -> transformA libraryA -< (Affine $ scale' (maximum_length/2),(Local,lower))) -< joint_info--arm :: LibraryModel -> LibraryModel -> Vector3D -> Double -> RSAnimAX any t i o (Point3D,Point3D) Joint-arm arm_upper arm_lower bend_vector maximum_length = proc (shoulder_point,hand_point) ->- do let joint_info = joint bend_vector shoulder_point maximum_length hand_point- libraryJointAnimation maximum_length arm_upper arm_lower -< joint_info - returnA -< joint_info--rightArm :: LibraryModel -> LibraryModel -> Vector3D -> Point3D -> Double -> Point3D -> RSAnimA (Maybe Integer) i o () Joint-rightArm arm_upper arm_lower bend_vector shoulder_anchor maximum_length hand_rest = proc () ->- do is_wielding <- arr isJust <<< wieldedTool -< ()- hand_point <- approachA 0.1 (perSecond 1.0) -< if is_wielding- then translate (Vector3D 0 0 maximum_length) shoulder_anchor- else hand_rest- arm arm_upper arm_lower bend_vector maximum_length -< (shoulder_anchor,hand_point)--leftArm :: LibraryModel -> LibraryModel -> Vector3D -> Point3D -> Double -> Point3D -> RSAnimA (Maybe Integer) i o () Joint-leftArm arm_upper arm_lower bend_vector shoulder_anchor maximum_length hand_rest = - proc () -> arm arm_upper arm_lower bend_vector maximum_length -< (shoulder_anchor,hand_rest)-\end{code}--\texttt{bothArms} and \texttt{bothLegs} render two limbs by swapping the description for the right limb.--\begin{code}-bothArms :: LibraryModel -> LibraryModel -> Vector3D -> Point3D -> Double -> Point3D -> RSAnimA (Maybe Integer) i o () (Joint,Joint)-bothArms arm_upper arm_lower bend_vector shoulder_anchor maximum_length hand_rest = proc () ->- do left_joint <- leftArm arm_upper arm_lower (swapX bend_vector) (swapX shoulder_anchor) maximum_length (swapX hand_rest) -< ()- right_joint <- rightArm arm_upper arm_lower bend_vector shoulder_anchor maximum_length hand_rest -< ()- returnA -< (left_joint,right_joint)--bothLegs :: LibraryModel -> LibraryModel -> Vector3D -> Point3D -> Double -> Point3D -> RSAnimA t i o () ()-bothLegs leg_upper leg_lower bend_vector hip_anchor maximum_length foot_rest = proc () ->- do legs [leg bend_vector hip_anchor maximum_length foot_rest (libraryJointAnimation maximum_length leg_upper leg_lower),- leg bend_vector (swapX hip_anchor) maximum_length (swapX foot_rest) (libraryJointAnimation maximum_length leg_upper leg_lower)]- -< ()--swapX :: (AffineTransformable a) => a -> a-swapX = scale (Vector3D (-1.0) 1.0 1.0)-\end{code}
+ src/Main.hs view
@@ -0,0 +1,145 @@+{-# LANGUAGE Arrows #-}++module Main+ (main)+ where++import System.IO+import PrintText+import Data.Maybe+import Graphics.UI.GLUT+import Control.Monad+import Actions+import Keymaps.Keymaps+import CommandLine+import Keymaps.BuiltinKeymaps+import RenderingControl+import Driver+import Animation+import RSAGL.Scene+import Models.Library+import System.Timeout+import System.Exit+import Globals+import Control.Concurrent.STM+import Control.Concurrent+import Statistics++roguestar_client_version :: String+roguestar_client_version = "0.3"++default_window_size :: Size+default_window_size = Size 800 600++display_mode :: [DisplayMode]+display_mode = [RGBAMode,+ WithDepthBuffer,+ DoubleBuffered]++timer_callback_millis :: Int+timer_callback_millis = 30++main :: IO ()+main =+ do (_, command_line) <- getArgsAndInitialize+ let command_line_options = parseCommandLine command_line+ let keymap = findKeymapOrDefault $ keymap_name command_line_options+ scene_var <- newTVarIO Nothing+ globals <- defaultGlobals+ driver_object <- newDriverObject+ print_text_object <- newPrintTextObject+ animation_object <- newRoguestarAnimationObject mainAnimationLoop+ lib <- newLibrary+ initialWindowSize $= default_window_size+ initialDisplayMode $= display_mode+ window <- createWindow $ "RogueStar GL " ++ roguestar_client_version+ reshapeCallback $= Just roguestarReshapeCallback+ display_statistics <- newStatistics "rendering"+ displayCallback $= roguestarDisplayCallback display_statistics scene_var print_text_object+ perWindowKeyRepeat $= PerWindowKeyRepeatOff+ keyboardMouseCallback $= (Just $ keyCallback print_text_object)+ addTimerCallback timer_callback_millis (roguestarTimerCallback scene_var globals driver_object print_text_object keymap window)+ scene_statistics <- newStatistics "scene"+ sceneLoop scene_statistics scene_var lib globals driver_object print_text_object animation_object+ mainLoop++watchQuit :: Globals -> IO ()+watchQuit g =+ do q <- atomically $ readTVar $ global_should_quit g+ when q $ exitWith ExitSuccess++sceneLoop :: Statistics -> TVar (Maybe Scene) -> Library -> Globals -> DriverObject -> PrintTextObject -> RoguestarAnimationObject -> IO ()+sceneLoop stats scene_var lib globals driver_object print_text_object animation_object = liftM (const ()) $ forkIO $ forever $+ do atomically $+ do b <- liftM isJust $ readTVar scene_var+ when b retry+ result <- timeout 20000000 $+ do scene <- runStatistics stats $ runRoguestarAnimationObject lib globals driver_object print_text_object animation_object + atomically $ writeTVar scene_var $ Just scene+ if isNothing result+ then do hPutStrLn stderr "roguestar-gl: aborting due to stalled simulation run (timed out after 20 seconds)"+ exitWith $ ExitFailure 1+ else return ()++roguestarReshapeCallback :: Size -> IO ()+roguestarReshapeCallback (Size width height) = + do matrixMode $= Projection+ loadIdentity+ viewport $= (Position 0 0,Size width height)++roguestarDisplayCallback :: Statistics -> TVar (Maybe Scene) -> PrintTextObject -> IO ()+roguestarDisplayCallback stats scene_var print_text_object =+ do scene <- atomically $+ do result <- maybe retry return =<< readTVar scene_var+ writeTVar scene_var Nothing+ return result+ result <- timeout 20000000 $+ do color (Color4 0 0 0 0 :: Color4 GLfloat)+ clear [ColorBuffer]+ (Size width height) <- get windowSize+ runStatistics stats $+ do sceneToOpenGL (fromIntegral width / fromIntegral height) (0.1,80.0) scene+ renderText print_text_object+ swapBuffers+ if isNothing result+ then do hPutStrLn stderr "roguestar-gl: aborting due to stalled display callback (timed out after 20 seconds)"+ exitWith $ ExitFailure 1+ else return ()++roguestarTimerCallback :: TVar (Maybe Scene) -> Globals -> DriverObject -> PrintTextObject -> Keymap -> Window -> IO ()+roguestarTimerCallback scene_var globals driver_object print_text_object keymap window =+ do watchQuit globals+ result <- timeout 20000000 $+ do addTimerCallback timer_callback_millis $ roguestarTimerCallback scene_var globals driver_object print_text_object keymap window+ postRedisplay $ Just window+ maybeExecuteKeymappedAction globals driver_object print_text_object keymap+ if isNothing result+ then do hPutStrLn stderr "roguestar-gl: aborting due to stalled timer callback (timed out after 20 seconds)"+ exitWith $ ExitFailure 1+ else return ()++maybeExecuteKeymappedAction :: Globals ->+ DriverObject ->+ PrintTextObject ->+ Keymap ->+ IO ()+maybeExecuteKeymappedAction globals driver_object print_text_object keymap =+ do let action_input = ActionInput globals+ driver_object+ print_text_object+ pullInputBuffer print_text_object+ buffer_contents <- getInputBuffer print_text_object+ (id =<<) $ atomically $+ do synced_with_engine <- liftM (not . null) $+ getValidActions action_input Nothing+ worked <- if synced_with_engine+ then takeUserInputAction action_input =<<+ keysToActionNames action_input keymap buffer_contents+ else return False+ filtered <- filterKeySequence action_input+ keymap+ buffer_contents+ return $ if worked+ then clearInputBuffer print_text_object+ else setInputBuffer print_text_object filtered+
− src/Main.lhs
@@ -1,114 +0,0 @@-\section{Main}--\begin{code}-{-# LANGUAGE Arrows #-}--module Main- (main)- where--import System.IO-import PrintText---import Quality-import Data.Maybe-import Data.List-import Graphics.UI.GLUT-import Control.Monad-import Actions-import Keymaps.Keymaps-import CommandLine-import Keymaps.BuiltinKeymaps-import RenderingControl-import Driver---import Models.LibraryData---import Models.Library---import Camera-import Animation-import RSAGL.Scene-import Models.Library-import System.Timeout-import System.Exit--roguestar_client_version :: String-roguestar_client_version = "0.2.2"--default_window_size :: Size-default_window_size = Size 800 600--display_mode :: [DisplayMode]-display_mode = [RGBAMode,- WithDepthBuffer,- DoubleBuffered]--timer_callback_millis :: Int-timer_callback_millis = 30----qualityFromArgs :: [String] -> Quality---qualityFromArgs [] = Good---qualityFromArgs ("quality-bad":_) = Bad---qualityFromArgs ("quality-poor":_) = Poor---qualityFromArgs ("quality-good":_) = Good---qualityFromArgs ("quality-super":_) = Super---qualityFromArgs args = qualityFromArgs $ tail args--main :: IO ()-main =- do (_, command_line) <- getArgsAndInitialize- let command_line_options = parseCommandLine command_line- let keymap = findKeymapOrDefault $ keymap_name command_line_options- driver_object <- newDriverObject- print_text_object <- newPrintTextObject- animation_object <- newRoguestarAnimationObject mainAnimationLoop- lib <- newLibrary- initialWindowSize $= default_window_size- initialDisplayMode $= display_mode- window <- createWindow $ "RogueStar GL " ++ roguestar_client_version- reshapeCallback $= Just roguestarReshapeCallback- displayCallback $= roguestarDisplayCallback lib driver_object print_text_object animation_object- keyboardMouseCallback $= (Just $ keyCallback print_text_object)- addTimerCallback timer_callback_millis (roguestarTimerCallback driver_object print_text_object keymap window)- mainLoop--roguestarReshapeCallback :: Size -> IO ()-roguestarReshapeCallback (Size width height) = - do matrixMode $= Projection- loadIdentity- viewport $= (Position 0 0,Size width height)--roguestarDisplayCallback :: Library -> DriverObject -> PrintTextObject -> RoguestarAnimationObject -> IO ()-roguestarDisplayCallback lib driver_object print_text_object animation_object =- do result <- timeout 20000000 $- do color (Color4 0 0 0 0 :: Color4 Float)- clear [ColorBuffer]- scene <- runRoguestarAnimationObject lib driver_object print_text_object animation_object - (Size width height) <- get windowSize- sceneToOpenGL (fromIntegral width / fromIntegral height) (0.1,80.0) scene- renderText print_text_object - swapBuffers- if isNothing result- then do hPutStrLn stderr "roguestar-gl: aborting due to stalled display callback (timed out after 20 seconds)"- exitWith $ ExitFailure 1- else return ()--roguestarTimerCallback :: DriverObject -> PrintTextObject -> Keymap -> Window -> IO ()-roguestarTimerCallback driver_object print_text_object keymap window =- do result <- timeout 20000000 $- do addTimerCallback timer_callback_millis $ roguestarTimerCallback driver_object print_text_object keymap window- driverRead driver_object- postRedisplay $ Just window- maybeExecuteKeymappedAction driver_object print_text_object keymap- if isNothing result- then do hPutStrLn stderr "roguestar-gl: aborting due to stalled timer callback (timed out after 20 seconds)"- exitWith $ ExitFailure 1- else return ()--maybeExecuteKeymappedAction :: DriverObject -> PrintTextObject -> Keymap -> IO ()-maybeExecuteKeymappedAction driver_object print_text_object keymap =- do let action_input = ActionInput driver_object print_text_object- buffer_contents <- getInputBuffer print_text_object- actions <- keysToActionNames action_input keymap buffer_contents- worked <- takeUserInputAction action_input actions- if worked- then clearInputBuffer print_text_object- else setInputBuffer print_text_object =<< filterKeySequence action_input keymap buffer_contents-\end{code}
src/Models/Androsynth.hs view
@@ -2,12 +2,10 @@ (androsynth) where -import RSAGL.Affine-import RSAGL.Vector-import RSAGL.Model+import RSAGL.Math+import RSAGL.Modeling import Quality import Models.Materials-import RSAGL.ModelingExtras androsynth_head :: Quality -> Modeling () androsynth_head _ = model $
src/Models/Ascendant.hs view
@@ -2,11 +2,9 @@ (ascendant_glow) where -import RSAGL.Model-import RSAGL.ModelingExtras-import RSAGL.Vector+import RSAGL.Modeling+import RSAGL.Math import Quality-import RSAGL.Affine ascendant_glow :: Quality -> Modeling () ascendant_glow _ =
src/Models/Caduceator.hs view
@@ -4,21 +4,17 @@ caduceator_arm_lower) where -import RSAGL.Vector-import RSAGL.CurveExtras-import RSAGL.Model+import RSAGL.Math+import RSAGL.Math.CurveExtras+import RSAGL.Modeling import Quality import Models.Materials-import RSAGL.Affine-import RSAGL.Interpolation-import RSAGL.ModelingExtras-import RSAGL.Angle caduceator :: Quality -> Modeling () caduceator _ = model $ do model $ do tube $ linearInterpolation- [(0.001,Point3D 0 0 (-5)),+ [(0,Point3D 0 0 (-5)), (0.5, Point3D 0 2 (-3)), (0.5, Point3D 0 2.5 (-2)), (0.5, Point3D 0 2.5 (-1)),@@ -30,7 +26,7 @@ (0.5, Point3D 0 7 3.5), (0.5, Point3D 0 7.5 4 ), (0.75, Point3D 0 7.25 5 ),- (0.001,Point3D 0 7 7 )]+ (0,Point3D 0 7 7 )] deform $ \(Point3D x y z) -> let x' = flip lerpMap y [(0,4),@@ -52,22 +48,22 @@ caduceator_arm_upper :: Quality -> Modeling () caduceator_arm_upper _ = rotate (Vector3D 1 0 0) (fromDegrees 90) $ model $ do model $ sor $ linearInterpolation $- points2d [(0.0001,0),+ points2d [(0,0), (1.0,1.0), (0.5,10.0),- (0.0001,10.5)]+ (0,10.5)] caduceator_skin affine $ scale' (1/10) caduceator_arm_lower :: Quality -> Modeling () caduceator_arm_lower _ = rotate (Vector3D 1 0 0) (fromDegrees 90) $ model $ do model $ sor $ linearInterpolation $- points2d [(0.0001,-0.5),+ points2d [(0,-0.5), (0.5,0.0), (0.25,9.0), (1.25,10.0), (1.0,10.25), (0.75,9.75),- (0.0001,9)]+ (0,9)] caduceator_skin affine $ scale' (1/10)
src/Models/Encephalon.hs view
@@ -3,18 +3,16 @@ where import Quality-import RSAGL.Vector-import RSAGL.CurveExtras-import RSAGL.Model-import RSAGL.ModelingExtras+import RSAGL.Math+import RSAGL.Math.CurveExtras+import RSAGL.Modeling import Models.Materials-import RSAGL.Affine encephalon_head :: Quality -> Modeling () encephalon_head _ = model $ do sor $ linearInterpolation $ points2d $ reverse- [(0.001,9),+ [(0,9), (0.5,9), (1,9), (1.5,9),@@ -25,8 +23,7 @@ (3,3)] deform dfn encephalon_skin- where dfn (Point3D x y z) | y > (abs x ** 0.4) + 7.5 = Point3D x ((abs x ** 0.4) + 7.5) z - dfn pt = pt+ where dfn (Point3D x y z) = Point3D x (min (abs x ** 4 + 7.5) y) z encephalon_eye :: Quality -> Modeling () encephalon_eye _ = model $@@ -43,7 +40,7 @@ (8,1), (8,0.5), (7,0),- (0.001,0)]+ (0,0)] alliance_metal encephalon :: Quality -> Modeling ()
+ src/Models/Library.hs view
@@ -0,0 +1,214 @@+{-# LANGUAGE OverloadedStrings #-}++module Models.Library+ (Library,+ newLibrary,+ lookupModel)+ where++import Quality+import Data.Maybe+import Data.Map as Map+import RSAGL.Modeling hiding (model)+import Control.Concurrent.STM+import Control.Monad+import GHC.Conc+import PrioritySync.PrioritySync+import System.IO+import Control.Concurrent.MVar++import Models.LibraryData++import Models.Terrain+import Models.QuestionMark+import Models.Tree+import Models.Encephalon+import Models.Recreant+import Models.Androsynth+import Models.Ascendant+import Models.Caduceator+import Models.Reptilian+import Models.PhaseWeapons+import Models.MachineParts+import Models.Sky+import Models.CyborgType4+import Models.EnergyThings+import Models.EnergySwords+import Models.Spheres+import Models.Monolith+import Models.Stargate++-- |+-- Get the modeling data for a named library model.+--+toModel :: LibraryModel -> Quality -> Modeling ()+toModel (TerrainTile s) = terrainTile s+toModel (SimpleModel LeafyBlob) = const $ leafy_blob+toModel (SimpleModel TreeBranch) = const $ tree_branch+toModel (SkySphere sky_info) = const $ makeSky sky_info+toModel (SunDisc sun_info) = const $ makeSun sun_info+toModel (SimpleModel QuestionMark) = const $ question_mark+toModel NullModel = const $ return ()+toModel (SimpleModel Encephalon) = encephalon+toModel (SimpleModel Recreant) = recreant+toModel (SimpleModel Androsynth) = androsynth+toModel (SimpleModel Caduceator) = caduceator+toModel (SimpleModel Reptilian) = reptilian+toModel (SimpleModel AscendantGlow) = ascendant_glow+toModel (SimpleModel PhasePistol) = phase_pistol+toModel (SimpleModel Phaser) = phaser+toModel (SimpleModel PhaseRifle) = phase_rifle+toModel (SimpleModel GasSphere) = gasSphere+toModel (SimpleModel MetalSphere) = metalSphere+toModel (SimpleModel ChromaliteSphere) = chromaliteSphere+toModel (SimpleModel MachineArmLower) = machine_arm_lower+toModel (SimpleModel MachineArmUpper) = machine_arm_upper+toModel (SimpleModel CaduceatorArmLower) = caduceator_arm_lower+toModel (SimpleModel CaduceatorArmUpper) = caduceator_arm_upper+toModel (SimpleModel ReptilianLegLower) = reptilian_leg_lower+toModel (SimpleModel ReptilianLegUpper) = reptilian_leg_upper+toModel (SimpleModel ReptilianArmLower) = reptilian_arm_lower+toModel (SimpleModel ReptilianArmUpper) = reptilian_arm_upper+toModel (SimpleModel ThinLimb) = thin_limb+toModel (SimpleModel CyborgType4Dome) = cyborg_type_4_dome+toModel (SimpleModel CyborgType4Base) = cyborg_type_4_base+toModel (SimpleModel CyborgType4HyperspaceDisc) = cyborg_type_4_hyperspace_disc+toModel (SimpleModel CyborgType4HyperspaceRotor) =+ cyborg_type_4_hyperspace_rotor+toModel (SimpleModel CyborgType4HyperspaceStabilizer) =+ cyborg_type_4_hyperspace_stabilizer+toModel (EnergyThing EnergyCylinder c) = energyCylinder c+toModel (EnergyThing EnergySword c) = energySword c 3+toModel (SimpleModel Monolith) = monolith+toModel (SimpleModel Portal) = portal++-- |+-- Models that should be displayed at lower quality.+--+downgraded_models :: [LibraryModel]+downgraded_models =+ Prelude.map toLibraryModel+ [LeafyBlob,TreeBranch,+ GasSphere,MetalSphere,ChromaliteSphere,MachineArmLower,+ MachineArmUpper,CaduceatorArmLower,CaduceatorArmUpper,+ ReptilianLegLower,ReptilianLegUpper,ReptilianArmLower,+ ReptilianArmUpper, ThinLimb]++-- |+-- Sometimes we want to constrain the quality of some models.+--+forceQuality :: LibraryModel -> Quality -> Quality+-- ambient box, LOD doesn't affect appearance+forceQuality (SimpleModel CyborgType4HyperspaceRotor) = const Bad+-- terrain just isn't that interesting+forceQuality (TerrainTile _) = min Poor+forceQuality x | x `elem` downgraded_models = \q -> case q of+ Bad -> Bad+ Poor -> Bad+ Good -> Poor+ Super -> Poor+forceQuality _ = id++-- |+-- Models to build at start time.+--+essential_library_models :: [LibraryModel]+essential_library_models =+ Prelude.map TerrainTile known_terrain_types +++ Prelude.map SimpleModel [minBound..maxBound] +++ [EnergyThing x y | x <- [minBound..maxBound],+ y <- [minBound..maxBound]]++modelPriority :: Quality -> LibraryModel -> Integer+modelPriority Bad model | model `elem` essential_library_models = 0+modelPriority q (SkySphere {}) = 10 * qualityPriority q+modelPriority q _ = qualityPriority q++qualityPriority :: Quality -> Integer+qualityPriority Bad = 1+qualityPriority Poor = 2+qualityPriority Good = 4+qualityPriority Super = 8++-- |+-- A library of named models. Models are generated on demand, but models+-- known in all_library_models are generated at worst quality when the library+-- is first initialized.+--+data Library = Library {+ library_cache :: TVar (Map.Map (Quality,LibraryModel)+ (TVar (Maybe BakedModel))),+ library_wrapper :: Quality -> LibraryModel -> IO () -> IO () }++-- |+-- Create a new library.+-- Only one Library should be needed per process instance.+--+newLibrary :: IO Library+newLibrary =+ do task_pool <- newTaskPool fast_queue_configuration+ (max 1 $ numCapabilities - 2)+ ()+ startQueue task_pool+ cache <- newTVarIO Map.empty+ loading_stdout_mvar <- newMVar ()+ let lib = Library cache $ \q m actionIO ->+ do _ <- dispatch (schedule task_pool $ modelPriority q m)+ actionIO+ return ()+ forM_ essential_library_models $ \x -> lookupModel lib x Bad+ let waitFor x = atomically $+ do b <- isLoaded lib x+ when (not b) retry+ forM_ essential_library_models $ \x -> forkIO $+ do waitFor x+ modifyMVar_ loading_stdout_mvar $ const $+ do hPutStrLn stderr $ "Pregenerated model: " ++ show x+ hPutStrLn stderr . modelInfo =<< lookupModel lib x Bad+ return ()+ forM_ essential_library_models $ \x -> waitFor x+ return lib++-- |+-- Get a library model. If the model is not available at the requested quality+-- level, a poorer model will be provided instead, and a background thread will+-- launch to begin generating the model at the requested level of detail.+--+lookupModel :: Library -> LibraryModel -> Quality -> IO IntermediateModel+lookupModel lib model q_ = (id =<<) $ atomically $+ do let q = forceQuality model q_+ cache <- readTVar $ library_cache lib+ let m_var = Map.lookup (q,model) cache+ case m_var of+ Just var ->+ do m_result <- liftM (fmap toIntermediateModel) $ readTVar var+ case m_result of+ Just result -> return $ return result+ Nothing | model == NullModel ->+ return $ atomically $ liftM toIntermediateModel $+ maybe retry return =<< readTVar var+ Nothing | q == Bad -> return $+ lookupModel lib NullModel Bad+ Nothing -> return $+ lookupModel lib model (pred q)+ Nothing ->+ do target <- newTVar Nothing+ writeTVar (library_cache lib) $+ Map.insert (q,model) target cache+ return $+ do library_wrapper lib q model $+ bakeAndStore target model q+ lookupModel lib model q++isLoaded :: Library -> LibraryModel -> STM Bool+isLoaded lib model =+ do cache <- readTVar $ library_cache lib+ let m_var = Map.lookup (Bad,model) cache+ maybe (return False) (liftM isJust . readTVar) m_var++bakeAndStore :: TVar (Maybe BakedModel) -> LibraryModel -> Quality -> IO ()+bakeAndStore target model q =+ do result <- bakeModel $ buildIntermediateModel (qualityToVertices q)+ (toModel model q)+ atomically $ writeTVar target $ Just result+
− src/Models/Library.lhs
@@ -1,93 +0,0 @@-\section{The Model Library}--\begin{code}-module Models.Library- (Library,- newLibrary,- lookupModel)- where- -import Quality-import Models.LibraryData-import Models.Terrain-import Models.QuestionMark-import Data.Map as Map-import RSAGL.Model-import RSAGL.QualityControl-import Control.Concurrent-import RSAGL.Bottleneck-import Control.Monad-import System.IO-import Models.Encephalon-import Models.Recreant-import Models.Androsynth-import Models.Ascendant-import Models.Caduceator-import Models.Reptilian-import Models.PhaseWeapons-import Models.MachineParts--toModel :: LibraryModel -> Quality -> Modeling ()-toModel (TerrainTile s) = terrainTile s-toModel QuestionMark = const $ question_mark-toModel NullModel = const $ return ()-toModel Encephalon = encephalon-toModel Recreant = recreant-toModel Androsynth = androsynth-toModel Caduceator = caduceator-toModel Reptilian = reptilian-toModel AscendantGlow = ascendant_glow-toModel PhasePistol = phase_pistol-toModel MachineArmLower = machine_arm_lower-toModel MachineArmUpper = machine_arm_upper-toModel CaduceatorArmLower = caduceator_arm_lower-toModel CaduceatorArmUpper = caduceator_arm_upper-toModel ReptilianLegLower = reptilian_leg_lower-toModel ReptilianLegUpper = reptilian_leg_upper-toModel ReptilianArmLower = reptilian_arm_lower-toModel ReptilianArmUpper = reptilian_arm_upper-toModel ThinLimb = thin_limb--all_library_models :: [LibraryModel]-all_library_models =- Prelude.map TerrainTile known_terrain_types ++- [QuestionMark, NullModel,- Encephalon,- Recreant,- Androsynth,- Caduceator,- Reptilian,- AscendantGlow,- PhasePistol,- MachineArmLower,- MachineArmUpper,- CaduceatorArmLower,- CaduceatorArmUpper,- ReptilianLegLower,- ReptilianLegUpper,- ReptilianArmLower,- ReptilianArmUpper,- ThinLimb]--data Library = Library - Bottleneck- (MVar (Map LibraryModel (QualityCache Quality IntermediateModel)))--newLibrary :: IO Library-newLibrary = - do lib <- liftM2 Library newBottleneck (newMVar Map.empty)- mapM_ (\x -> do hPutStrLn stderr ("Preloading model: " ++ show x)- lookupModel lib x Poor) all_library_models- return lib--lookupModel :: Library -> LibraryModel -> Quality -> IO IntermediateModel-lookupModel (Library bottleneck lib) lm q =- do lib_map <- takeMVar lib- m_qo <- return $ Map.lookup lm lib_map- case m_qo of- Just qo -> do putMVar lib lib_map- getQuality qo q- Nothing -> do qo <- newQuality bottleneck parIntermediateModel (\q' -> toIntermediateModel (qualityToVertices q') (toModel lm q')) [Bad,Poor,Good,Super]- putMVar lib $ insert lm qo lib_map- getQuality qo q-\end{code}
+ src/Models/LibraryData.hs view
@@ -0,0 +1,82 @@+module Models.LibraryData+ (LibraryModel(..),+ SimpleModel(..),+ EnergyColor(..),+ EnergyThing(..),+ LibraryModelSource(..))+ where++import Models.Sky+import qualified Data.ByteString.Char8 as B++data EnergyColor = Blue | Yellow | Red | Green+ deriving (Eq,Ord,Show,Enum,Bounded)++data SimpleModel =+ LeafyBlob+ | TreeBranch+ | QuestionMark+ -- Creature Bodies+ | Encephalon+ | Recreant+ | Androsynth+ | AscendantGlow+ | Caduceator+ | Reptilian+ -- Tools+ | PhasePistol+ | Phaser+ | PhaseRifle+ | GasSphere+ | MetalSphere+ | ChromaliteSphere+ -- Arms and Legs+ | MachineArmLower+ | MachineArmUpper+ | CaduceatorArmLower+ | CaduceatorArmUpper+ | ReptilianLegUpper+ | ReptilianLegLower+ | ReptilianArmUpper+ | ReptilianArmLower+ | ThinLimb+ -- Space Ship Parts+ | CyborgType4Dome+ | CyborgType4Base+ | CyborgType4HyperspaceDisc+ | CyborgType4HyperspaceRotor+ | CyborgType4HyperspaceStabilizer+ -- Buildings+ | Monolith+ | Portal+ deriving (Eq,Ord,Show,Enum,Bounded)++data EnergyThing =+ EnergySword+ | EnergyCylinder+ deriving (Eq,Ord,Show,Enum,Bounded)++data LibraryModel =+ -- Terrain+ TerrainTile B.ByteString+ -- Astronomical Phenomena+ | SkySphere SkyInfo+ | SunDisc SunInfo+ -- The Null Model+ | NullModel+ -- SimpleModels (zero-parameter models)+ | SimpleModel SimpleModel+ -- Energy things+ | EnergyThing EnergyThing EnergyColor+ deriving (Eq,Ord,Show)++-- | Things that are also LibraryModels.+class LibraryModelSource a where+ toLibraryModel :: a -> LibraryModel++instance LibraryModelSource LibraryModel where+ toLibraryModel = id++instance LibraryModelSource SimpleModel where+ toLibraryModel = SimpleModel+
− src/Models/LibraryData.lhs
@@ -1,29 +0,0 @@-\section{Library Models}--\begin{code}-module Models.LibraryData- (LibraryModel(..))- where--data LibraryModel = - TerrainTile String- | QuestionMark- | NullModel- | Encephalon- | Recreant- | Androsynth- | AscendantGlow- | Caduceator- | Reptilian- | PhasePistol- | MachineArmLower- | MachineArmUpper- | CaduceatorArmLower- | CaduceatorArmUpper- | ReptilianLegUpper- | ReptilianLegLower- | ReptilianArmUpper- | ReptilianArmLower- | ThinLimb- deriving (Eq,Ord,Show)-\end{code}
src/Models/MachineParts.hs view
@@ -5,33 +5,31 @@ where import Quality-import RSAGL.Model-import RSAGL.CurveExtras-import RSAGL.Affine-import RSAGL.Vector-import RSAGL.Angle+import RSAGL.Modeling+import RSAGL.Math.CurveExtras+import RSAGL.Math import Models.Materials machine_arm_lower :: Quality -> Modeling () machine_arm_lower _ = scale' (1/4) $ rotate (Vector3D 1 0 0) (fromDegrees 90) $ model $- do sor $ linearInterpolation $- points2d [(0.001,4.5),+ do sor $ linearInterpolation $ reverse $ + points2d [(0.0,4.5), (0.25,4.5), (0.25,3.5), (0.35,3), (0.35,0.5),- (0.001,0.3)]+ (0.0,0.3)] alliance_metal machine_arm_upper :: Quality -> Modeling () machine_arm_upper _ = scale' (1/4) $ rotate (Vector3D 1 0 0) (fromDegrees 90) $ model $- do sor $ linearInterpolation $- points2d [(0.001,4.5),+ do sor $ linearInterpolation $ reverse $+ points2d [(0.0,4.5), (0.5,4.5), (0.75,3.5), (0.5,3), (0.5,1),- (0.001,-0.5)]+ (0.0,-0.5)] alliance_metal thin_limb :: Quality -> Modeling ()
+ src/Models/Materials.hs view
@@ -0,0 +1,133 @@+module Models.Materials+ (+ -- | Materials by Faction+ treaty_metal,+ treaty_glow,+ treaty_energy_field,+ alliance_metal,+ concordance_metal,+ concordance_dark_glass,+ concordance_bright_glass,+ cyborg_metal,+ cyborg_glow,+ -- | Material by Species+ caduceator_skin,+ encephalon_skin,+ reptilian_skin,+ reptilian_pigment,+ reptilian_specular,+ -- | Material by Energy Type+ energyColor,+ energyMaterial)+ where++import RSAGL.Modeling+import Models.LibraryData++{---------------------------------------------------------+ - Materials by Faction+ - -------------------------------------------------------}++-- Treaty Organization Materials (cyan-teal solid colors, yellow energy colors)++treaty_metal :: MaterialM attr ()+treaty_metal = material $+ do pigment $ pure camouflage_green+ specular 1 $ pure $ viridian++treaty_glow :: MaterialM attr ()+treaty_glow = material $+ do pigment $ pure black+ emissive $ pure brass++treaty_energy_field :: MaterialM attr ()+treaty_energy_field = material $+ do emissive $ pure brass+ specular 1 $ pure saffron++-- Alliance Materials (yellow-gold solid colors, orange energy colors)++alliance_metal :: Modeling ()+alliance_metal = material $+ do pigment $ pure $ scaleRGB 0.6 gold+ specular 7 $ pure gold++-- Concordance Materials (violet solid colors, blue energy colors)++concordance_metal :: Modeling ()+concordance_metal = material $+ do pigment $ pure slate_gray+ specular 4 $ pure lilac++concordance_dark_glass :: Modeling ()+concordance_dark_glass = material $+ do pigment $ pure black+ specular 8 $ pure eggplant++concordance_bright_glass :: Modeling ()+concordance_bright_glass = material $+ do pigment $ pure black+ emissive $ pure puce+ specular 8 $ pure eggplant++-- Pirates (green solid colors, red energy colors)++-- Utopiate (red solid colors, cyan-teal energy colors)++-- Whispers (black solid colors, white energy colors)++-- Monster (orange solid colors, violet energy colors)++-- Cyborg Materials (white solid colors, green energy colors)++cyborg_metal :: MaterialM attr ()+cyborg_metal = metallic $ pure wheat++cyborg_glow :: MaterialM attr ()+cyborg_glow = + do pigment $ pure blackbody+ emissive $ pure $ scaleRGB 1.0 green++{-------------------------------------------------------+ - Materials by Species+ - -----------------------------------------------------}++-- Caduceator Skins++caduceator_skin :: Modeling ()+caduceator_skin = material $ pigment $ pattern (cloudy 75 0.01) [(0.0,pure red),(0.5,pure safety_orange),(1.0,pure black)]++-- Reptilian Skins++reptilian_pigment :: ColorFunction RGB+reptilian_pigment = pattern (cloudy 75 0.1) [(0.0,pure lavender),(1.0,pure saffron)]++reptilian_specular :: ColorFunction RGB+reptilian_specular = pattern (cloudy 75 0.1) [(0.0,pure firebrick),(1.0,pure chartreuse)]++reptilian_skin :: Modeling ()+reptilian_skin = material $+ do pigment $ reptilian_pigment+ specular 5.0 $ reptilian_specular++-- Encephalon Skins++encephalon_skin :: Modeling ()+encephalon_skin = material $ pigment $ pattern (cloudy 32 0.1) [(0.0,pure sepia),(1.0,pure amethyst)]++{--------------------------------------------------------+ - Material by Energy Type+ - ------------------------------------------------------}++energyColor :: EnergyColor -> RGB+energyColor Blue = cobalt+energyColor Yellow = saffron+energyColor Red = red+energyColor Green = shamrock++energyMaterial :: EnergyColor -> Modeling ()+energyMaterial c = material $+ do pigment $ pure $ scaleRGB 0.33 $ energyColor c+ specular 1.0 $ pure $ scaleRGB 0.33 $ energyColor c+ emissive $ pure $ scaleRGB 0.33 $ energyColor c+
− src/Models/Materials.lhs
@@ -1,92 +0,0 @@-\section{Materials}--\begin{code}-module Models.Materials- (alliance_metal,- concordance_metal,- concordance_dark_glass,- concordance_bright_glass,- caduceator_skin,- encephalon_skin,- recreant_metal,- reptilian_skin,- reptilian_pigment,- reptilian_specular)- where--import RSAGL.Model-import RSAGL.ModelingExtras-\end{code}--\subsection{Materials by Alliance}--\subsubsection{Alliance Materials}--\begin{code}-alliance_metal :: Modeling ()-alliance_metal = material $- do pigment $ pure $ scaleRGB 0.6 gold- specular 75 $ pure $ scaleRGB 1.0 gold-\end{code}--\subsubsection{Condorance Materials}--\begin{code}-concordance_metal :: Modeling ()-concordance_metal = material $- do pigment $ pure slate_gray- specular 45 $ pure lilac-\end{code}--\begin{code}-concordance_dark_glass :: Modeling ()-concordance_dark_glass = material $- do pigment $ pure black- specular 85 $ pure eggplant-\end{code}--\begin{code}-concordance_bright_glass :: Modeling ()-concordance_bright_glass = material $- do pigment $ pure black- emissive $ pure puce- specular 85 $ pure eggplant-\end{code}--\section{Materials by Species}--\subsubsection{Encephalon Materials}--\begin{code}-caduceator_skin :: Modeling ()-caduceator_skin = material $ pigment $ pattern (cloudy 75 0.01) [(0.0,pure red),(0.5,pure safety_orange),(1.0,pure black)]-\end{code}--\begin{code}-reptilian_pigment :: ColorFunction RGB-reptilian_pigment = pattern (cloudy 75 0.1) [(0.0,pure lavender),(1.0,pure saffron)]--reptilian_specular :: ColorFunction RGB-reptilian_specular = pattern (cloudy 75 0.1) [(0.0,pure firebrick),(1.0,pure chartreuse)]--reptilian_skin :: Modeling ()-reptilian_skin = material $- do pigment $ reptilian_pigment- specular 5.0 $ reptilian_specular- -\end{code}--\begin{code}-encephalon_skin :: Modeling ()-encephalon_skin = material $ pigment $ pattern (cloudy 32 0.1) [(0.0,pure sepia),(1.0,pure amethyst)]-\end{code}--\subsubsection{Recreant Materials}--\begin{code}-recreant_metal :: Modeling ()-recreant_metal = material $- do pigment $ pure camouflage_green - specular 25 $ pure white-\end{code}-
+ src/Models/Monolith.hs view
@@ -0,0 +1,14 @@+module Models.Monolith+ (monolith)+ where++import RSAGL.Math+import RSAGL.Modeling+import Quality++monolith :: Quality -> Modeling ()+monolith _ =+ do box (Point3D (-1/2) 0 (-1/8)) (Point3D (1/2) (9/4) (1/8))+ material $+ do pigment $ pure blackbody+ specular 100 $ pure white
src/Models/PhaseWeapons.hs view
@@ -1,20 +1,23 @@ module Models.PhaseWeapons- (phase_pistol)+ (phase_pistol,+ phaser,+ phase_rifle) where import Quality import Models.Materials-import RSAGL.Vector-import RSAGL.Model-import RSAGL.Affine-import RSAGL.CurveExtras-import RSAGL.Angle+import RSAGL.Math+import RSAGL.Modeling+import RSAGL.Math.CurveExtras+import Control.Monad -phase_weapon_emitter :: Quality -> Modeling ()-phase_weapon_emitter _ = model $+-- | A phase weapon emitter. Includes a specification of a phase guide length.+phaseWeaponEmitter :: Integer -> Quality -> Modeling ()+phaseWeaponEmitter guide_length _ = model $ do sor $ linearInterpolation $ points2d $ reverse - [(0.001,0),+ [(0,0),+ (1,0), (4,1), (7,2), (9,3),@@ -23,15 +26,16 @@ (7,-6), (4,-9), (2,-10),- (0.001,-10)]- deform dfn+ (0,-10)]+ let guide_length_y = fromInteger guide_length * 3 + 6+ when (guide_length > 0) $ openCone (Point3D 0 0 8,1) (Point3D 0 guide_length_y 8,0)+ _ <- flip mapM [2..guide_length] $ \i ->+ do let y = fromInteger i * 3+ translate (Vector3D 0 y 0) $ torus 8 (lerpBetween (0,y,guide_length_y) (1,0))+ deform dfn_squish affine $ rotateX (fromDegrees 90)- where dfn_squish p@(Point3D _ _ z) | z > 0 = scale (Vector3D 1 1 0.1) p- dfn_squish p = scale (Vector3D 1 1 0.25) p- dfn_smoothe_dish p@(Point3D _ y _) | y > 0 = scale (Vector3D 1 (u/10) 1) p- where u = distanceBetween origin_point_3d $ scale (Vector3D 1 0 1) p- dfn_smoothe_dish p = p- dfn = dfn_smoothe_dish . dfn_squish+ where dfn_squish p@(Point3D _ _ z) | z > 0 = scale (Vector3D 1 1 0.1) p -- bottom of emitter is flattened+ dfn_squish p = scale (Vector3D 1 1 0.25) p -- top of emitter is more round phase_weapon_grip :: Quality -> Modeling () phase_weapon_grip _ = model $@@ -52,10 +56,19 @@ dfn_flat_sides (Point3D x y z) = Point3D ((signum x *) $ sqrt (abs x/3)) y z dfn_slope_backwards pt@(Point3D _ y _) = translate (Vector3D 0 0 (-y/3)) pt dfn = dfn_slope_backwards . dfn_flat_back . dfn_flat_sides . dfn_gripped_front- -phase_pistol :: Quality -> Modeling ()-phase_pistol q = model $++phaseWeapon :: Integer -> Quality -> Modeling ()+phaseWeapon guide_length q = model $ scale' (1/100) $- do translate (Vector3D 0 5 7) $ phase_weapon_emitter q+ do translate (Vector3D 0 5 7) $ phaseWeaponEmitter guide_length q translate (Vector3D 0 5 0) $ phase_weapon_grip q concordance_metal++phase_pistol :: Quality -> Modeling ()+phase_pistol = phaseWeapon 0++phaser :: Quality -> Modeling ()+phaser = phaseWeapon 1++phase_rifle :: Quality -> Modeling ()+phase_rifle = phaseWeapon 2
src/Models/QuestionMark.hs view
@@ -2,12 +2,9 @@ (question_mark) where -import RSAGL.Model-import RSAGL.ModelingExtras-import RSAGL.Affine-import RSAGL.Vector-import RSAGL.CurveExtras-import RSAGL.Curve+import RSAGL.Modeling+import RSAGL.Math+import RSAGL.Math.CurveExtras question_mark_material :: Modeling () question_mark_material = material $
src/Models/Recreant.hs view
@@ -3,10 +3,9 @@ where import Quality-import RSAGL.Model-import RSAGL.CurveExtras-import RSAGL.Vector-import RSAGL.Affine+import RSAGL.Modeling+import RSAGL.Math.CurveExtras+import RSAGL.Math import Models.Materials recreant_antenna_small :: Quality -> Modeling ()@@ -31,11 +30,11 @@ recreant_body _ = do sor $ linearInterpolation $ points2d $ reverse- [(0.001,3),+ [(0,3), (4,3), (6,2), (7,1),- (0.001,0)]+ (0,0)] recreant :: Quality -> Modeling () recreant q = scale' 0.05 $
src/Models/RecreantFactory.hs view
@@ -3,8 +3,8 @@ where import Quality-import RSAGL.Model-import RSAGL.Vector+import RSAGL.Modeling+import RSAGL.Math import Models.Materials recreant_factory :: Quality -> Modeling ()@@ -17,4 +17,4 @@ sphere (Point3D 0.4 0 (-0.4)) 0.1 sphere (Point3D 0.4 0 0.4) 0.1 sphere (Point3D (-0.4) 0 0.4) 0.1- recreant_metal+ alliance_metal
src/Models/Reptilian.hs view
@@ -6,20 +6,17 @@ reptilian_arm_lower) where -import RSAGL.Vector-import RSAGL.Model+import RSAGL.Math+import RSAGL.Modeling import Quality import Models.Materials-import RSAGL.ModelingExtras-import RSAGL.CurveExtras-import RSAGL.Affine-import RSAGL.Angle+import RSAGL.Math.CurveExtras reptilian :: Quality -> Modeling () reptilian _ = model $ do model $ do tube $ linearInterpolation- [(0.001,Point3D 0 0 (-6)),+ [(0 ,Point3D 0 0 (-6)), (1 ,Point3D 0 5 (-4)), (1.5 ,Point3D 0 5.5 (-3)), (2.4 ,Point3D 0 6 (-2)),@@ -31,7 +28,7 @@ (1 ,Point3D 0 11 1), (1 ,Point3D 0 11 2), (0.5 ,Point3D 0 10 2.5),- (0.001,Point3D 0 9 3)]+ (0 ,Point3D 0 9 3)] model $ do triangle (Point3D 0 7 (-2)) (Point3D (-1) 10 (-8)) (Point3D 1 10 (-8)) triangle (Point3D 0 7 (-2)) (Point3D (-0.25) 9 (-9)) (Point3D (-3.5) 9.5 (-8))@@ -42,20 +39,20 @@ deform $ \(Point3D x y z) -> Point3D x (y+x^2/100) (z+x^2/25+sin (x*10)/4) reptilian_skin model $- do sphere (Point3D 0.5 11 2) 0.25- sphere (Point3D (-0.5) 11 2) 0.25+ do sphere (Point3D 0.55 11 2) 0.5+ sphere (Point3D (-0.55) 11 2) 0.5 material $ pigment $ pure black affine $ scale' (1/20) reptilian_leg_upper :: Quality -> Modeling () reptilian_leg_upper _ = rotate (Vector3D 1 0 0) (fromDegrees 90) $ model $ do model $ sor $ linearInterpolation $- points2d [(0.0001,0),+ points2d [(0 ,0), (1.9,1), (1.6,5), (1,7.5), (0.75,10.6),- (0.0001,10.8)]+ (0 ,10.8)] material $ do pigment $ pattern (gradient origin_point_3d (Vector3D 0 10 0)) [(0.0,reptilian_pigment),(1.0,pure burgundy)] specular 5.0 $ pattern (gradient origin_point_3d (Vector3D 0 10 0)) [(0.0,reptilian_specular),(1.0,pure crimson)]@@ -64,12 +61,12 @@ reptilian_leg_lower :: Quality -> Modeling () reptilian_leg_lower _ = rotate (Vector3D 1 0 0) (fromDegrees 90) $ model $ do sor $ linearInterpolation $- points2d [(0.0001,-0.15),+ points2d [(0 ,-0.15), (0.5,-0.1), (1.0,0.0), (0.5,9.0), (0.5,10.0),- (0.0001,10.1)]+ (0 ,10.1)] openCone (Point3D 0 9.5 0,0.5) (Point3D 0 10 7,0.0001) openCone (Point3D 0 9.5 0,0.5) (Point3D 5 10 5,0.0001) openCone (Point3D 0 9.5 0,0.5) (Point3D (-5) 10 5,0.0001)@@ -81,13 +78,13 @@ reptilian_arm_upper :: Quality -> Modeling () reptilian_arm_upper _ = rotate (Vector3D 1 0 0) (fromDegrees 90) $ model $ do sor $ linearInterpolation $- points2d [(0.0001,0.0),+ points2d [(0 ,0.0), (1.0,0.1), (0.5,1.0), (0.25,5.0), (0.5,9.0), (1.0,9.9),- (0.0001,0.0)]+ (0 ,0.0)] material $ do pigment $ pure burgundy specular 5.0 $ pure crimson@@ -96,12 +93,12 @@ reptilian_arm_lower :: Quality -> Modeling () reptilian_arm_lower _ = rotate (Vector3D 1 0 0) (fromDegrees 90) $ model $ do sor $ linearInterpolation $- points2d [(0.0001,-1.0),+ points2d [(0 ,-1.0), (1.0,0.0), (0.25,5.0), (0.5,9.0), (0.5,9.9),- (0.0001,0.0)]+ (0 ,0.0)] material $ do pigment $ pure burgundy specular 5.0 $ pure crimson
+ src/Models/Stargate.hs view
@@ -0,0 +1,38 @@+module Models.Stargate+ (portal)+ where++import RSAGL.Math+import RSAGL.Modeling+import Models.Materials+import Quality++portal :: Quality -> Modeling ()+portal q =+ do model $+ do model $+ do box (Point3D (-0.6) 0 (-0.05)) (Point3D (-0.5) 1.618 0.05)+ box (Point3D 0.6 0 (-0.05)) (Point3D 0.5 1.618 0.05)+ box (Point3D (-1.1) 1.568 (-0.05))+ (Point3D (1.1) 1.668 0.05)+ minorFixedQuality q+ let cone =+ do openCone (Point3D 0 (1.618/6) 0,0.4)+ (Point3D 0 (1.618/3) 0,0)+ openCone (Point3D 0 (1.618/6) 0,0.4) (Point3D 0 0 0,0)+ cone_stack = scale (Vector3D 1 (7/8) 1) $+ do cone+ translate (Vector3D 0 (1.618/3) 0) cone+ translate (Vector3D 0 (2*1.618/3) 0) cone+ translate (Vector3D 0 1.618 0) cone+ translate (Vector3D 1.1 0 0) cone_stack+ translate (Vector3D (-1.1) 0 0) cone_stack+ material treaty_metal+ model $+ do quadralateral (Point3D (-0.55) 0 0)+ (Point3D (-0.55) 1.618 0)+ (Point3D 0.55 1.618 0)+ (Point3D 0.55 0 0)+ twoSided True+ material treaty_energy_field+
+ src/Models/Terrain.hs view
@@ -0,0 +1,129 @@+{-# LANGUAGE OverloadedStrings #-}++module Models.Terrain+ (known_terrain_types,+ terrainTile)+ where++import Quality+import Models.RecreantFactory+import RSAGL.Modeling+import RSAGL.Math+import RSAGL.Types+import qualified Data.ByteString.Char8 as B++-- |+-- A list of all terrain type names known to roguestar-gl.+-- But at any given moment the engine might have been extended to include other types.+known_terrain_types :: [B.ByteString]+known_terrain_types =+ ["water",+ "deepwater",+ "sand",+ "desert",+ "grass",+ "dirt",+ "forest",+ "deepforest",+ "ice",+ "lava",+ "glass",+ "rockyground",+ "rubble",+ "rockface",+ "recreantfactory"]++-- |+-- A simple 1-by-1 square patch, centered at the origin, and raised on a slope toward it's center.+-- The first parameter indicates the patch's actual height, while the second represent's the patch's+-- apparent height based on it's normal vectors.+-- The sloping effect makes it clear where one tile ends and another begins, which is practical for game play.+-- The two heights are different as it is not desireable that creature's legs should dissappear inside the landscape,+-- but at shallow angles of attack this makes the terrain tile look fake. REVISIT+--+-- This is just the shape, without any material.+--+terrainTileShape :: RSdouble -> RSdouble -> Quality -> Modeling ()+terrainTileShape physical_height aesthetic_height q = model $+ do heightField (-0.5,-0.5) (0.5,0.5) $ \(x,z) -> let y = 1 - max (abs x) (abs z) * 2 in min (max 0 $ sqrt y) (2*y)+ affine $ scale (Vector3D 1 aesthetic_height 1)+ deform $ \(SurfaceVertex3D p v) -> SurfaceVertex3D (scale (Vector3D 1 (physical_height/aesthetic_height) 1) p) v+ qualityToFixed q++-- |+-- Creates a terrain tile based on 'terrainTileShape' with appropriate characteristics and material for its type,+-- but without any special casing for unsual terrains like forest.+--+basicTerrainTile :: B.ByteString -> Quality -> Modeling ()+basicTerrainTile s q = model $+ do terrainTileShape 0.01 (terrainHeight s) q+ material $ terrainTexture s++-- |+-- Creates a terrain tile based on 'terrainTileShape'+-- Provides special casing for forest, rockface, liquids, etc.+--+terrainTile :: B.ByteString -> Quality -> Modeling ()+terrainTile "recreantfactory" q = recreant_factory q+terrainTile "rockface" q = model $+ do terrainTileShape (terrainHeight "rockface") (terrainHeight "rockface") q+ material $ terrainTexture "rockface"+terrainTile s q = basicTerrainTile s q++-- |+-- Answers the height of a type of terrain.+-- Note that, with some exceptions, all terrain is the same height, but some terrain is given+-- sharper contrast in its normal vectors than others (see 'terrainTileShape').+-- Unrecognized terrain types will appear very tall, so they can be easily noticed and corrected.+--+terrainHeight :: B.ByteString -> RSdouble+terrainHeight "water" = 0.01+terrainHeight "deepwater" = 0.005+terrainHeight "sand" = 0.1+terrainHeight "desert" = 0.1+terrainHeight "grass" = 0.13+terrainHeight "dirt" = 0.12+terrainHeight "forest" = 0.15+terrainHeight "deepforest" = 0.17+terrainHeight "ice" = 0.05+terrainHeight "lava" = 0.06+terrainHeight "glass" = 0.04+terrainHeight "rockyground" = 0.18+terrainHeight "rubble" = 0.2+terrainHeight "rockface" = 1.0+terrainHeight _ = 5.0++-- |+-- Answers the material of a type of terrain.+-- Unrecognized terrain types will appear bright magenta, so they can be easily+-- noticed and corrected.+--+terrainTexture :: B.ByteString -> MaterialM () ()+terrainTexture "water" =+ do pigment $ pure blue+ specular 100 $ pure white+terrainTexture "deepwater" =+ do pigment $ pure $ scaleRGB 0.8 blue+ specular 100 $ pure white+terrainTexture "sand" = pigment $ pure corn+terrainTexture "desert" = pigment $ pure $ lerp 0.5 (corn,white)+terrainTexture "grass" = pigment $ pure forest_green+terrainTexture "dirt" = pigment $ pure brown+terrainTexture "forest" = pigment $ pure fern_green+terrainTexture "deepforest" = pigment $ pure fern_green+terrainTexture "ice" =+ do pigment $ pure white+ specular 1 $ pure teal+terrainTexture "lava" =+ do pigment $ pure blackbody+ emissive $ pure coral+terrainTexture "glass" =+ do pigment $ pure black+ specular 1 $ pure white+terrainTexture "rockyground" = pigment $ pure slate_gray+terrainTexture "rubble" = pigment $ pure slate_gray+terrainTexture "rockface" = pigment $ pure slate_gray+terrainTexture _ =+ do pigment $ pure blackbody+ emissive $ pure magenta+
− src/Models/Terrain.lhs
@@ -1,108 +0,0 @@-\section{Terrain Tiles}--\begin{code}-module Models.Terrain- (known_terrain_types,- terrainTile)- where--import Quality-import Models.RecreantFactory-import RSAGL.Model-import RSAGL.ModelingExtras-import Models.Tree-import RSAGL.Vector-import RSAGL.Interpolation-import RSAGL.Affine-import RSAGL.Angle--known_terrain_types :: [String]-known_terrain_types =- ["water",- "deepwater",- "sand",- "desert",- "grass",- "forest",- "deepforest",- "ice",- "lava",- "glass",- "rockyground",- "rubble",- "rockface",- "recreantfactory"]--terrainTileShape :: Double -> Double -> Modeling ()-terrainTileShape squash height = model $- do regularPrism (origin_point_3d,sqrt 0.5) (Point3D 0 1 0,0.0001) 4- deform $ \(Point3D x y z) -> Point3D x (sqrt $ max 0 y) z- affine $ scale (Vector3D 1 height 1) . rotate (Vector3D 0 1 0) (fromDegrees 45)- deform $ \(SurfaceVertex3D p v) -> SurfaceVertex3D (scale (Vector3D 1 squash 1) p) v--terrainTile :: String -> Quality -> Modeling ()-terrainTile "recreantfactory" q = recreant_factory q-terrainTile "forest" q =- do basicTerrainTile "forest"- leafy_tree q-terrainTile "deepforest" q =- do basicTerrainTile "deepforest"- translate (Vector3D 0.5 0 0.5) $ leafy_tree q- translate (Vector3D (-0.5) 0 0.5) $ leafy_tree q- translate (Vector3D 0 0 (-0.5)) $ leafy_tree q-terrainTile "rockface" _ = model $- do terrainTileShape 1.0 (terrainHeight "rockface")- material $ terrainTexture "rockface"-terrainTile s _ = basicTerrainTile s--basicTerrainTile :: String -> Modeling ()-basicTerrainTile s = model $- do terrainTileShape 0.01 (terrainHeight s)- material $ terrainTexture s--terrainHeight :: String -> Double-terrainHeight "water" = 0.025-terrainHeight "deepwater" = 0.025-terrainHeight "sand" = 0.1-terrainHeight "desert" = 0.1-terrainHeight "grass" = 0.13-terrainHeight "dirt" = 0.12-terrainHeight "forest" = 0.15-terrainHeight "deepforest" = 0.17-terrainHeight "ice" = 0.05-terrainHeight "lava" = 0.06-terrainHeight "glass" = 0.04-terrainHeight "rockyground" = 0.18-terrainHeight "rubble" = 0.2-terrainHeight "rockface" = 0.5-terrainHeight _ = 2.0--terrainTexture :: String -> MaterialM () ()-terrainTexture "water" =- do pigment $ pure blue- specular 100 $ pure white-terrainTexture "deepwater" =- do pigment $ pure $ scaleRGB 0.8 blue- specular 100 $ pure white-terrainTexture "sand" = pigment $ pure corn-terrainTexture "desert" = pigment $ pure $ lerp 0.5 (corn,white)-terrainTexture "grass" = pigment $ pure forest_green-terrainTexture "dirt" = pigment $ pure brown-terrainTexture "forest" = pigment $ pure fern_green-terrainTexture "deepforest" = pigment $ pure fern_green-terrainTexture "ice" = - do pigment $ pure white- specular 1 $ pure teal-terrainTexture "lava" =- do pigment $ pure blackbody- emissive $ pure coral-terrainTexture "glass" =- do pigment $ pure black- specular 1 $ pure white-terrainTexture "rockyground" = pigment $ pure slate_gray-terrainTexture "rubble" = pigment $ pure slate_gray-terrainTexture "rockface" = pigment $ pure slate_gray-terrainTexture _ =- do pigment $ pure blackbody- emissive $ pure magenta-\end{code}
src/Models/Tree.hs view
@@ -1,39 +1,17 @@ module Models.Tree- (leafy_tree)+ (leafy_blob, tree_branch) where -import RSAGL.Model-import RSAGL.ModelingExtras-import RSAGL.Vector-import Control.Monad.Random-import Quality-import RSAGL.Affine-import RSAGL.AbstractVector-import RSAGL.Interpolation-import Control.Monad.State+import RSAGL.Modeling+import RSAGL.Math -leafy_tree :: Quality -> Modeling ()-leafy_tree Bad = evalRandT (leafyTreeBranch origin_point_3d (Vector3D 0 0.5 0) 0.1 2) (mkStdGen 45)-leafy_tree Poor = evalRandT (leafyTreeBranch origin_point_3d (Vector3D 0 0.5 0) 0.1 3) (mkStdGen 45)-leafy_tree Good = evalRandT (leafyTreeBranch origin_point_3d (Vector3D 0 0.5 0) 0.1 4) (mkStdGen 45)-leafy_tree Super = evalRandT (leafyTreeBranch origin_point_3d (Vector3D 0 0.5 0) 0.1 5) (mkStdGen 45)+leafy_blob :: ModelingM () ()+leafy_blob = model $+ do sphere (Point3D 0 0 0) 1.0+ material $ pigment $ pure forest_green -leafyTreeBranch :: Point3D -> Vector3D -> Double -> Int -> RandT StdGen (ModelingM ()) ()-leafyTreeBranch point vector thickness recursion | recursion <= 0 = - do b <- getRandom- when b $ lift $ model $- do sphere point (vectorLength vector + thickness)- material $ pigment $ pure forest_green-leafyTreeBranch point vector thickness recursion =- do lift $ model $- do closedCone (point,thickness) (translate vector point,thickness/2)- material $ pigment $ pure dark_brown- us <- liftM (take recursion) $ getRandomRs (0.0,1.0)- mapM leafyTreeBranchFrom us- leafyTreeBranchFrom 1.0- where leafyTreeBranchFrom :: Double -> RandT StdGen (ModelingM ()) ()- leafyTreeBranchFrom u =- do let new_vector_constraint = vectorLength vector / 1.5- (x:y:z:_) <- getRandomRs (-new_vector_constraint,new_vector_constraint)- t <- getRandomR (thickness/3,thickness/2)- leafyTreeBranch (lerp u (point,translate vector point)) (vectorScaleTo new_vector_constraint $ vector `add` (Vector3D x y z)) t (recursion - 1)+tree_branch :: ModelingM () ()+tree_branch = model $+ do closedCone (Point3D 0 0 0, 1.0) (Point3D 0 1 0, 0.5)+ material $ pigment $ pure dark_brown+
+ src/PrintText.hs view
@@ -0,0 +1,175 @@+{-# LANGUAGE OverloadedStrings #-}++-- | A 'PrintTextObject' renders the text console at the bottom of the screen.++module PrintText+ (newPrintTextObject,+ printText,+ PrintTextObject,+ renderText,+ PrintTextMode(..),+ TextType(..),+ getInputBuffer,+ pullInputBuffer,+ setInputBuffer,+ setPrintTextMode,+ clearOutputBuffer,+ clearInputBuffer,+ keyCallback)+ where++import Control.Concurrent.STM+import Graphics.UI.GLUT as GLUT+import PrintTextData+import Control.Monad+import Control.Concurrent.Chan+import qualified Data.ByteString.Char8 as B++data PrintTextData = PrintTextData {+ text_output_buffer :: [(TextType,B.ByteString)],+ text_input_buffer :: B.ByteString,+ text_output_mode :: PrintTextMode }++data PrintTextObject = PrintTextObject (TVar PrintTextData) (Chan B.ByteString)++font_width_pixels :: Int+font_width_pixels = 9++font_height_pixels :: Int+font_height_pixels = 15++padding_pixels :: Int+padding_pixels = 10++font :: BitmapFont+font = Fixed9By15++newPrintTextObject :: IO PrintTextObject+newPrintTextObject =+ do pt_data <- newTVarIO $ PrintTextData {+ text_output_buffer = [],+ text_input_buffer = B.empty,+ text_output_mode = Unlimited }+ pt_chan <- newChan+ return $ PrintTextObject pt_data pt_chan++printText :: PrintTextObject -> TextType -> B.ByteString -> STM ()+printText (PrintTextObject pto _) text_type str =+ do print_text <- readTVar pto+ writeTVar pto $ print_text {+ text_output_buffer = text_output_buffer print_text +++ map ((,) text_type) (B.lines str) }++keyCallback :: PrintTextObject -> KeyboardMouseCallback+keyCallback (PrintTextObject _ chan) (Char char) Down _ _ =+ writeChan chan $ B.pack [char]+keyCallback (PrintTextObject _ chan) (SpecialKey special) Down _ _ =+ writeChan chan $ ":::" `B.append` (B.pack $ show special)+keyCallback _ _ _ _ _ = return ()++getInputBuffer :: PrintTextObject -> IO B.ByteString+getInputBuffer (PrintTextObject pto _) =+ liftM text_input_buffer $ atomically $ readTVar pto++-- | Pull one keypress into the input buffer, if it exists.+pullInputBuffer :: PrintTextObject -> IO ()+pullInputBuffer (PrintTextObject pto chan) =+ do e <- isEmptyChan chan+ when (not e) $+ do r <- readChan chan+ atomically $+ do print_text <- readTVar pto+ writeTVar pto $ print_text {+ text_input_buffer = text_input_buffer print_text+ `B.append` r }+ return ()++setInputBuffer :: PrintTextObject -> B.ByteString -> IO ()+setInputBuffer (PrintTextObject pto _) new_input_buffer = atomically $+ do print_text <- readTVar pto+ writeTVar pto $ print_text { text_input_buffer = new_input_buffer }++clearOutputBuffer :: PrintTextObject -> STM ()+clearOutputBuffer (PrintTextObject pto _) =+ do print_text <- readTVar pto+ writeTVar pto $ print_text { text_output_buffer = [] }++setPrintTextMode :: PrintTextObject -> PrintTextMode -> STM ()+setPrintTextMode (PrintTextObject pto _) pt_mode =+ do print_text <- readTVar pto+ writeTVar pto $ print_text { text_output_mode = pt_mode }++clearInputBuffer :: PrintTextObject -> IO ()+clearInputBuffer (PrintTextObject pto _) = atomically $+ do print_text <- readTVar pto+ writeTVar pto $ print_text { text_input_buffer = B.empty }++renderText :: PrintTextObject -> IO ()+renderText (PrintTextObject pto _) =+ do ptd <- atomically $ readTVar pto+ (Size width height) <- get windowSize+ save_depth_func <- get depthFunc+ save_depth_mask <- get depthMask+ save_blend <- get blend+ depthFunc $= Nothing+ depthMask $= GLUT.Disabled+ blend $= Enabled+ matrixMode $= Projection+ loadIdentity+ ortho2D 0 (fromIntegral width) 0 (fromIntegral height)+ matrixMode $= Modelview 0+ loadIdentity+ let max_characters_height =+ (fromIntegral height - 2 * fromIntegral padding_pixels) `div`+ (fromIntegral font_height_pixels)+ let max_characters_width =+ (fromIntegral width - 2 * fromIntegral padding_pixels) `div`+ (fromIntegral font_width_pixels)+ let lines_to_print =+ restrictLines max_characters_height max_characters_width $+ (case (text_output_mode ptd) of+ PrintTextData.Disabled -> []+ Limited -> reverse $ take 3 $ reverse $+ text_output_buffer ptd+ Unlimited -> (text_output_buffer ptd)) +++ (if B.length (text_input_buffer ptd) > 0 ||+ text_output_mode ptd /= PrintTextData.Disabled+ then [(Query,"> " `B.append` (text_input_buffer ptd))]+ else [])+ actual_width_pixels = font_width_pixels *+ (maximum $ map (B.length . snd) lines_to_print)+ actual_height_pixels = font_height_pixels * (length lines_to_print)+ in do color $ (Color4 0 0 0 0.92 :: Color4 GLfloat)+ (rect :: Vertex2 GLfloat -> Vertex2 GLfloat -> IO ())+ (Vertex2 (fromIntegral padding_pixels)+ (fromIntegral padding_pixels))+ (Vertex2 (fromIntegral (actual_width_pixels ++ padding_pixels))+ (fromIntegral (actual_height_pixels ++ padding_pixels)))+ currentRasterPosition $= (Vertex4 (fromIntegral padding_pixels)+ (fromIntegral padding_pixels)+ 0 1)+ mapM_ drawLine $ reverse lines_to_print+ blend $= save_blend+ depthMask $= save_depth_mask+ depthFunc $= save_depth_func++drawLine :: (TextType,B.ByteString) -> IO ()+drawLine (textType,str) = do (Vertex4 x y _ _) <- get currentRasterPosition+ color $ textTypeToColor textType+ currentRasterPosition $= (Vertex4 x y 0 1)+ renderString font $ B.unpack str+ currentRasterPosition $= (Vertex4 x (y + fromIntegral font_height_pixels) 0 1)++restrictLines :: Int -> Int -> [(TextType,B.ByteString)] -> [(TextType,B.ByteString)]+restrictLines height width text_lines = reverse $ take height $ reverse $ concatMap splitLongLines text_lines+ where splitLongLines (_,l) | B.null l = []+ splitLongLines (textType,str) = (textType,B.take width str):(splitLongLines (textType,B.drop width str))++textTypeToColor :: TextType -> Color3 GLfloat+textTypeToColor UnexpectedEvent = Color3 1.0 0.5 0.0+textTypeToColor Event = Color3 0.5 0.75 1.0+textTypeToColor Input = Color3 0.5 1.0 0.5+textTypeToColor Query = Color3 1.0 1.0 1.0+
− src/PrintText.lhs
@@ -1,137 +0,0 @@-\section{PrintText}--A \texttt{PrintTextObject} renders the text console at the bottom of the screen.--\begin{code}-module PrintText- (newPrintTextObject,- printText,- PrintTextObject,- renderText,- PrintTextMode(..),- TextType(..),- getInputBuffer,- setInputBuffer,- setPrintTextMode,- clearOutputBuffer,- clearInputBuffer,- keyCallback)- where--import Data.IORef-import Graphics.UI.GLUT as GLUT-import PrintTextData-import Control.Monad-\end{code}--\begin{code}-data PrintTextData = PrintTextData {- text_output_buffer :: [(TextType,String)],- text_input_buffer :: String,- text_output_mode :: PrintTextMode }--newtype PrintTextObject = PrintTextObject (IORef PrintTextData)--font_width_pixels :: Int-font_width_pixels = 9--font_height_pixels :: Int-font_height_pixels = 15--padding_pixels :: Int-padding_pixels = 10--font :: BitmapFont-font = Fixed9By15--newPrintTextObject :: IO PrintTextObject-newPrintTextObject = liftM PrintTextObject $ newIORef $ PrintTextData {- text_output_buffer = [],- text_input_buffer = [],- text_output_mode = Unlimited } --printText :: PrintTextObject -> TextType -> String -> IO ()-printText (PrintTextObject pto) text_type str =- modifyIORef pto $ - \print_text -> print_text { text_output_buffer = text_output_buffer print_text ++ map (\l -> (text_type,l)) (lines str) } --keyCallback :: PrintTextObject -> KeyboardMouseCallback-keyCallback _ _ Up _ _ = return ()-keyCallback _ (MouseButton {}) _ _ _ = return ()-keyCallback (PrintTextObject pto) (Char char) _ _ _ = modifyIORef pto $ - \ptd -> ptd { text_input_buffer = text_input_buffer ptd ++ [char] }-keyCallback (PrintTextObject pto) (SpecialKey special) _ _ _ = modifyIORef pto $- \ptd -> ptd { text_input_buffer = text_input_buffer ptd ++ show special }--getInputBuffer :: PrintTextObject -> IO String-getInputBuffer (PrintTextObject pto) = liftM text_input_buffer $ readIORef pto--setInputBuffer :: PrintTextObject -> String -> IO ()-setInputBuffer (PrintTextObject pto) new_input_buffer = modifyIORef pto $- \print_text -> print_text { text_input_buffer = new_input_buffer }--clearOutputBuffer :: PrintTextObject -> IO ()-clearOutputBuffer (PrintTextObject pto) = modifyIORef pto $ \ptd -> ptd { text_output_buffer = [] }--setPrintTextMode :: PrintTextObject -> PrintTextMode -> IO ()-setPrintTextMode (PrintTextObject pto) pt_mode =- do modifyIORef pto $ \print_text -> print_text { text_output_mode = pt_mode }--clearInputBuffer :: PrintTextObject -> IO ()-clearInputBuffer (PrintTextObject pto) = modifyIORef pto $ \ptd -> ptd { text_input_buffer = [] }--renderText :: PrintTextObject -> IO ()-renderText (PrintTextObject pto) = - do ptd <- readIORef pto- (Size width height) <- get windowSize- save_depth_func <- get depthFunc- save_depth_mask <- get depthMask- save_blend <- get blend- depthFunc $= Nothing- depthMask $= GLUT.Disabled- blend $= Enabled- matrixMode $= Projection- loadIdentity- ortho2D 0 (fromIntegral width) 0 (fromIntegral height)- matrixMode $= Modelview 0- loadIdentity- let max_characters_height = (fromIntegral height - 2 * fromIntegral padding_pixels) `div` (fromIntegral font_height_pixels)- let max_characters_width = (fromIntegral width - 2 * fromIntegral padding_pixels) `div` (fromIntegral font_width_pixels)- let lines_to_print = restrictLines max_characters_height max_characters_width $ - (case (text_output_mode ptd) of- PrintTextData.Disabled -> []- Limited -> reverse $ take 3 $ reverse $ text_output_buffer ptd- Unlimited -> (text_output_buffer ptd)) ++- (if length (text_input_buffer ptd) > 0 || (text_output_mode ptd) /= PrintTextData.Disabled - then [(Query,"> " ++ (text_input_buffer ptd))] - else [])- actual_width_pixels = font_width_pixels * (maximum $ map (length . snd) lines_to_print)- actual_height_pixels = font_height_pixels * (length lines_to_print)- in do color $ (Color4 0 0 0 0.92 :: Color4 GLfloat)- (rect :: Vertex2 GLfloat -> Vertex2 GLfloat -> IO ())- (Vertex2 (fromIntegral padding_pixels) (fromIntegral padding_pixels)) - (Vertex2 (fromIntegral (actual_width_pixels + padding_pixels)) (fromIntegral (actual_height_pixels + padding_pixels)))- currentRasterPosition $= (Vertex4 (fromIntegral padding_pixels) (fromIntegral padding_pixels) 0 1)- mapM_ drawLine $ reverse lines_to_print- blend $= save_blend- depthMask $= save_depth_mask- depthFunc $= save_depth_func--drawLine :: (TextType,String) -> IO ()-drawLine (textType,str) = do (Vertex4 x y _ _) <- get currentRasterPosition- color $ textTypeToColor textType- currentRasterPosition $= (Vertex4 x y 0 1)- renderString font str- currentRasterPosition $= (Vertex4 x (y + fromIntegral font_height_pixels) 0 1)--restrictLines :: Int -> Int -> [(TextType,String)] -> [(TextType,String)]-restrictLines height width text_lines = reverse $ take height $ reverse $ concatMap splitLongLines text_lines- where splitLongLines (_,[]) = []- splitLongLines (textType,str) = (textType,take width str):(splitLongLines (textType,drop width str))--textTypeToColor :: TextType -> Color3 GLfloat-textTypeToColor UnexpectedEvent = Color3 1.0 0.5 0.0-textTypeToColor Event = Color3 0.5 0.75 1.0-textTypeToColor Input = Color3 0.5 1.0 0.5-textTypeToColor Query = Color3 1.0 1.0 1.0-\end{code}
+ src/PrintTextData.hs view
@@ -0,0 +1,19 @@+module PrintTextData+ (PrintTextMode(..),+ TextType(..))+ where++-- | How much of the screen should we use for text.+data PrintTextMode = Limited -- ^ print only a few lines+ | Unlimited -- ^ fill the entire screen with text+ | Disabled -- ^ don't print any text at all+ deriving (Eq)++-- |+-- Type of any line of text printed on the screen. We print the+-- different TextTypes in different colors for readability.+--+data TextType = Event -- ^ message related to an event in the game + | UnexpectedEvent -- ^ message related to a software problem+ | Input -- ^ something the user typed in+ | Query -- ^ asking the user to type something
− src/PrintTextData.lhs
@@ -1,30 +0,0 @@-\section{PrintText Data Structures}--\begin{code}-module PrintTextData- (PrintTextMode(..),- TextType(..))- where-\end{code}-How to run printText.--Limited -- print only the most recent lines at the top of the screen-Unlimited -- fill the entire screen with text-Disabled -- don't print any text at all--\begin{code}-data PrintTextMode = Limited -- print only a few lines (or only 1)- | Unlimited -- fill the entire screen with text- | Disabled -- don't print any text at all- deriving (Eq)-\end{code}--Type of any line of text printed on the screen. We print the-different TextTypes in different colors for readability.--\begin{code}-data TextType = Event -- message related to an even in the game - | UnexpectedEvent -- - | Input -- something the user typed in- | Query-\end{code}
+ src/ProtocolTypes.hs view
@@ -0,0 +1,70 @@+{-# LANGUAGE OverloadedStrings #-}+-- | ADTs corresponding to certain data tables incomming from roguestar-engine.+module ProtocolTypes+ (ProtocolType(..),+ TerrainTile(..),+ VisibleObject(..),+ WieldedObject(..),+ tableSelectTyped)+ where++import Data.Maybe+import Tables+import RSAGL.Math.Angle+import Debug.Trace+import qualified Data.ByteString.Char8 as B++-- | 'ProtocolType' is any type that can be constructed from a row of a 'RoguestarTable'.+-- 'formatTable' is a function from bottom to the expected format of the table row.+class ProtocolType t where+ fromTable :: [TableDataFormat B.ByteString Integer] -> Maybe t+ formatTable :: t -> [TableDataFormat B.ByteString B.ByteString]++data TerrainTile = TerrainTile {+ tt_type :: B.ByteString,+ tt_xy :: (Integer,Integer) }+ deriving (Eq,Show)++instance ProtocolType TerrainTile where+ formatTable = const [TDString "terrain-type",TDNumber "x",TDNumber "y"]+ fromTable [TDString t,TDNumber x,TDNumber y] = Just $ TerrainTile t (x,y)+ fromTable _ = Nothing++data VisibleObject = VisibleObject {+ vo_unique_id :: Integer,+ vo_xy :: (Integer,Integer),+ vo_facing :: BoundAngle }++instance ProtocolType VisibleObject where+ formatTable = const $ [TDNumber "object-unique-id",TDNumber "x",TDNumber "y",TDString "facing"]+ fromTable [TDNumber unique_id,TDNumber x,TDNumber y,TDString facing] = Just $+ VisibleObject unique_id (x,y) (facingToAngle facing)+ fromTable _ = Nothing++data WieldedObject = WieldedObject {+ wo_unique_id :: Integer,+ wo_creature_id :: Integer }++instance ProtocolType WieldedObject where+ formatTable = const $ [TDNumber "uid",TDNumber "creature"]+ fromTable [TDNumber unique_id,TDNumber creature_id] = Just $+ WieldedObject unique_id creature_id+ fromTable _ = Nothing++facingToAngle :: B.ByteString -> BoundAngle+facingToAngle "south" = BoundAngle $ fromDegrees 0+facingToAngle "southeast" = BoundAngle $ fromDegrees 45+facingToAngle "east" = BoundAngle $ fromDegrees 90+facingToAngle "northeast" = BoundAngle $ fromDegrees 135+facingToAngle "north" = BoundAngle $ fromDegrees 180+facingToAngle "northwest" = BoundAngle $ fromDegrees 225+facingToAngle "west" = BoundAngle $ fromDegrees 270+facingToAngle "southwest" = BoundAngle $ fromDegrees 315+facingToAngle "here" = BoundAngle $ fromDegrees 0+facingToAngle s = trace ("facingToAngle: what is " ++ B.unpack s ++ "?") $ BoundAngle $ fromDegrees 180++tableSelectTyped :: (ProtocolType t) => RoguestarTable -> [t]+tableSelectTyped the_table = result+ where result = mapMaybe fromTable $ tableSelectFormatted the_table the_format + the_format = formatTable $ (error "tableSelectTyped: undefined" :: (a -> a)) (head result)+
− src/ProtocolTypes.lhs
@@ -1,72 +0,0 @@-\section{Protocol Types}--\begin{code}-module ProtocolTypes- (ProtocolType(..),- TerrainTile(..),- VisibleObject(..),- WieldedObject(..),- tableSelectTyped)- where--import Data.Maybe-import Tables-import RSAGL.Angle-import Debug.Trace-\end{code}--\texttt{ProtocolType} is any type that can be constructed from a row of a \texttt{RoguestarTable}.-\texttt{formatTable} is a function from bottom to the expected format of the table row.--\begin{code}-class ProtocolType t where- fromTable :: [TableDataFormat String Integer] -> Maybe t- formatTable :: t -> [TableDataFormat String String]--data TerrainTile = TerrainTile {- tt_type :: String,- tt_xy :: (Integer,Integer) }--instance ProtocolType TerrainTile where- formatTable = const [TDString "terrain-type",TDNumber "x",TDNumber "y"]- fromTable [TDString t,TDNumber x,TDNumber y] = Just $ TerrainTile t (x,y)- fromTable _ = Nothing--data VisibleObject = VisibleObject {- vo_unique_id :: Integer,- vo_xy :: (Integer,Integer),- vo_facing :: BoundAngle }--instance ProtocolType VisibleObject where- formatTable = const $ [TDNumber "object-unique-id",TDNumber "x",TDNumber "y",TDString "facing"]- fromTable [TDNumber unique_id,TDNumber x,TDNumber y,TDString facing] = Just $- VisibleObject unique_id (x,y) (facingToAngle facing)- fromTable _ = Nothing--data WieldedObject = WieldedObject {- wo_unique_id :: Integer,- wo_creature_id :: Integer }--instance ProtocolType WieldedObject where- formatTable = const $ [TDNumber "uid",TDNumber "creature"]- fromTable [TDNumber unique_id,TDNumber creature_id] = Just $- WieldedObject unique_id creature_id- fromTable _ = Nothing--facingToAngle :: String -> BoundAngle-facingToAngle "south" = BoundAngle $ fromDegrees 0-facingToAngle "southeast" = BoundAngle $ fromDegrees 45-facingToAngle "east" = BoundAngle $ fromDegrees 90-facingToAngle "northeast" = BoundAngle $ fromDegrees 135-facingToAngle "north" = BoundAngle $ fromDegrees 180-facingToAngle "northwest" = BoundAngle $ fromDegrees 225-facingToAngle "west" = BoundAngle $ fromDegrees 270-facingToAngle "southwest" = BoundAngle $ fromDegrees 315-facingToAngle "here" = BoundAngle $ fromDegrees 0-facingToAngle s = trace ("facingToAngle: what is " ++ s ++ "?") $ BoundAngle $ fromDegrees 180--tableSelectTyped :: (ProtocolType t) => RoguestarTable -> [t]-tableSelectTyped the_table = result- where result = mapMaybe fromTable $ tableSelectFormatted the_table the_format - the_format = formatTable $ (error "tableSelectTyped: undefined" :: (a -> a)) (head result)-\end{code}
src/Quality.hs view
@@ -1,9 +1,12 @@ module Quality (Quality(..),- qualityToVertices)+ qualityToVertices,+ qualityToFixed,+ minorFixedQuality) where +import RSAGL.Modeling data Quality = Bad | Poor@@ -12,8 +15,23 @@ deriving (Eq,Enum,Ord,Show) qualityToVertices :: Quality -> Integer-qualityToVertices Bad = 2^5-qualityToVertices Poor = 2^9-qualityToVertices Good = 2^13-qualityToVertices Super = 2^17+qualityToVertices Bad = 2^6+qualityToVertices Poor = 2^8+qualityToVertices Good = 2^10+qualityToVertices Super = 2^12 +-- | Sets the fixed quality of of modeled surface using 'RSAGL.Model.fixed'.+-- Good for pieces that need to mesh together but don't for some reason.+qualityToFixed :: Quality -> ModelingM attr ()+qualityToFixed Bad = fixed (2^2,2^2)+qualityToFixed Poor = fixed (2^3,2^3)+qualityToFixed Good = fixed (2^4,2^4)+qualityToFixed Super = fixed (2^5,2^5)++-- | Sets the fixed quality of of modeled surface using 'RSAGL.Model.fixed'.+-- The quality here is very low.+minorFixedQuality :: Quality -> ModelingM attr ()+minorFixedQuality Bad = fixed (2^1,2^1)+minorFixedQuality Poor = fixed (2^1,2^1)+minorFixedQuality Good = fixed (2^2,2^2)+minorFixedQuality Super = fixed (2^3,2^3)
+ src/RenderingControl.hs view
@@ -0,0 +1,180 @@+{-# LANGUAGE Arrows, OverloadedStrings, TypeFamilies #-}++module RenderingControl+ (mainAnimationLoop)+ where++import Globals+import RSAGL.FRP+import RSAGL.Math+import RSAGL.Modeling+import Animation+import Control.Arrow+import Data.Maybe+import PrintText+import Tables+import VisibleObject+import RSAGL.Animation+import qualified Data.Map as Map+import Sky+import Scene+import Data.Monoid+import AnimationTerrain+import AnimationCreatures+import AnimationBuildings+import AnimationTools+import AnimationMenus+import AnimationExtras+import AnimationEvents+import Strings+import RSAGL.Types+import qualified Data.ByteString.Char8 as B++-- | Enters the top-level animation loop.+mainAnimationLoop :: FRP e (FRP1 AnimationState () SceneLayerInfo) () SceneLayerInfo+mainAnimationLoop = proc () ->+ do m_state <- driverGetAnswerA -< "state"+ switchContinue -< (fmap (const $ mainWelcome >>> mainDispatch) m_state,())+ printTextOnce -< Just (UnexpectedEvent,"Waiting for engine...")+ returnA -< roguestarSceneLayerInfo mempty basic_camera+ where mainWelcome = proc () ->+ do printTextOnce -< Just (Event,"Welcome to Roguestar-GL.")+ returnA -< ()++-- | This is the actual to-level animation loop.+mainDispatch :: (FRPModel m) => FRP e (RSwitch Disabled () () SceneLayerInfo m) () SceneLayerInfo+mainDispatch = proc () ->+ do result <- frp1Context (mainStateHeader (const False) >>> arr (const $ roguestarSceneLayerInfo mempty basic_camera)) -< ()+ monitorPlanetName -< ()+ monitorCompassReading -< ()+ returnA -< result++-- | Forces the current RSAnim thread to switch whenever the current state does not match the specified predicate.+-- Any switch that wants to surrender control whenever the state changes should call this first.+mainStateHeader :: (FRPModel m) => (B.ByteString -> Bool) -> FRP e (RSwitch Disabled () () SceneLayerInfo m) () ()+mainStateHeader = genericStateHeader switchTo+ where switchTo blanking_state | blanking_state `elem` blanking_states = blankingDispatch blanking_state+ switchTo menu_state | menu_state `elem` menu_states = menuManager+ switchTo planar_state | planar_state `elem` planar_states = planarGameplayDispatch+ switchTo "game-over" = gameOver+ switchTo unknown_state = error $ "mainStateHeader: unrecognized state: " ++ B.unpack unknown_state++-- | Displays all menus with a black background.+menuManager :: (FRPModel m) => FRP e (RSwitch Disabled () () SceneLayerInfo m) () SceneLayerInfo+menuManager = proc () ->+ do mainStateHeader (`elem` menu_states) -< ()+ frp1Context menuDispatch -< ()++-- | Print a "Welcome to P-XXXX" message whenever we arrive on a new planet.+monitorPlanetName :: (FRPModel m, StateOf m ~ AnimationState) => FRP e m () ()+monitorPlanetName = proc () ->+ do m_planet_name <- driverGetAnswerA -< "planet-name"+ p <- changed (==) <<< sticky isJust Nothing -< m_planet_name+ printTextA -< case m_planet_name of+ _ | not p -> Nothing+ Nothing -> Nothing+ Just "nothing" -> Nothing+ Just somewhere -> Just (Event, B.concat $ ["Welcome to ", capitalize somewhere, "."])+ returnA -< ()++-- | Print a compass heading message whenever it changes.+monitorCompassReading :: (FRPModel m, StateOf m ~ AnimationState) => FRP e m () ()+monitorCompassReading = proc () ->+ do m_compass <- driverGetAnswerA -< "compass"+ p <- changed (==) <<< sticky isJust Nothing -< m_compass+ printTextA -< case m_compass of+ _ | not p -> Nothing+ Nothing -> Nothing+ Just "nothing" -> Nothing+ Just "here" -> Nothing+ Just compass -> Just (Event, B.concat $ ["Compass reading: ", hrstring compass, "."])+ returnA -< ()+++-- | Print the game over message and retain control of the rendering loop forever.+gameOver :: (FRPModel m) => FRP e (RSwitch Disabled () () SceneLayerInfo m) () SceneLayerInfo+gameOver = proc () ->+ do printTextOnce -< Just (Event,"Game Over")+ returnA -< roguestarSceneLayerInfo mempty basic_camera++-- | List of all states that require the display of the planar environment (terrain, characters, tools, and sky)+planar_states :: [B.ByteString]+planar_states = ["player-turn","move","turn","jump","attack","fire","clear-terrain"] ++ recognized_events++-- | Captures all planar visuals: terrain, characters, tools, and sky.+planarGameplayDispatch :: (FRPModel m) => FRP e (RSwitch Disabled () () SceneLayerInfo m) () SceneLayerInfo+planarGameplayDispatch = proc () ->+ do -- setup/get infos+ mainStateHeader (`elem` planar_states) -< () + clearPrintTextOnce -< ()+ frp1Context eventMessager -< ()+ -- terrain threads+ frpContext (allowAnonymous forbidDuplicates) [(Nothing,terrainThreadLauncher)] -< ()+ -- building threads+ frpContext (allowAnonymous forbidDuplicates) [(Nothing,visibleObjectThreadLauncher buildingAvatar)] -< ()+ -- creature threads+ ctos <- arr (catMaybes . map (uncurry $ liftA2 (,))) <<< + frpContext (allowAnonymous forbidDuplicates) + [(Nothing,visibleObjectThreadLauncher creatureAvatar)] -< ()+ -- tool threads+ frpContext (allowAnonymous forbidDuplicates) [(Nothing,visibleObjectThreadLauncher toolAvatar)] -< + ToolThreadInput {+ tti_wield_points = Map.fromList $ map (\(uid,cto) -> (uid,cto_wield_point cto)) ctos }+ -- camera/lighting stuff, including sky sphere + sky_info <- getSkyInfo -< ()+ sky -< sky_info+ m_lookat <- whenJust (approachA 1.0 (perSecond 3.0)) <<< sticky isJust Nothing <<<+ arr (fmap (\(x,y) -> Point3D (realToFrac x) 0.25 (negate $ realToFrac y))) <<< centerCoordinates -< ()+ camera_distance <- approachA 5.0 (perSecond 5.0) <<< readGlobal global_planar_camera_distance -< ()+ let (planar_camera,lookat) = maybe (basic_camera,origin_point_3d) (\x -> (planarCamera camera_distance x,x)) m_lookat+ artificial_light_intensity <- arr lighting_artificial <<< lightingConfiguration -< sky_info+ sky_on <- readGlobal global_sky_on -< ()+ accumulateSceneA -< (scene_layer_local, lightSource $ case () of+ () | artificial_light_intensity > 0.05 && sky_on ->+ mapLightSource (mapBoth $ scaleRGB artificial_light_intensity) $ PointLight {+ lightsource_position = camera_position planar_camera,+ lightsource_radius = measure (camera_position planar_camera) lookat,+ lightsource_color = gray 0.8,+ lightsource_ambient = gray 0.2 }+ () | artificial_light_intensity > 0.05 -> PointLight {+ lightsource_position = camera_position planar_camera,+ lightsource_radius = measure (camera_position planar_camera) lookat,+ lightsource_color = gray 0.5,+ lightsource_ambient = gray 0.5 }+ () | otherwise -> NoLight)+ returnA -< roguestarSceneLayerInfo (skyAbsorbtionFilter sky_info) planar_camera++-- | Sets up a Camera, based on a camera distance parameter (probably tied to the global_planar_camera_distance variable)+-- and the look-at point.+planarCamera :: RSdouble -> Point3D -> Camera+planarCamera camera_distance look_at = PerspectiveCamera {+ camera_position = translate (vectorScaleTo camera_distance $ Vector3D 0 (7*(camera_distance/10)**2) camera_distance) look_at,+ camera_lookat = translate (Vector3D 0 (1/camera_distance) 0) look_at,+ camera_up = Vector3D 0 1 0,+ camera_fov = fromDegrees $ 40 + 30 / camera_distance }++-- | Retrieve the look-at point from the engine.+centerCoordinates :: (FRPModel m, StateOf m ~ AnimationState) => FRP e m () (Maybe (Integer,Integer))+centerCoordinates = proc () ->+ do m_center_coordinates_table <- sticky isJust Nothing <<< driverGetTableA -< ("center-coordinates","0")+ returnA -< do center_coordinates_table <- m_center_coordinates_table+ x <- tableLookupInteger center_coordinates_table ("axis","coordinate") "x" -- Maybe monad+ y <- tableLookupInteger center_coordinates_table ("axis","coordinate") "y" + return (x,y)++-- | A list of the states that require the screen to be blanked (made black), interrupting the planar visuals.+blanking_states :: [B.ByteString]+blanking_states = ["teleport-event"]++-- | Display the blanked screen and print any blanking events.+blankingDispatch :: (FRPModel m) => B.ByteString -> FRP e (RSwitch Disabled () () SceneLayerInfo m) () SceneLayerInfo+blankingDispatch "teleport-event" = proc () ->+ do mainStateHeader (`elem` blanking_states) -< () + clearPrintTextOnce -< ()+ printTextOnce -< Just (Event,"Whoosh!")+ blockContinue <<< arr ((< 0.5) . toSeconds) <<< threadTime -< ()+ returnA -< roguestarSceneLayerInfo mempty basic_camera+blankingDispatch blanking_state = proc () ->+ do debugOnce -< Just $ "blankingDispatch: unrecognized blanking_state `" `B.append` blanking_state `B.append` "`"+ returnA -< roguestarSceneLayerInfo mempty basic_camera+
− src/RenderingControl.lhs
@@ -1,434 +0,0 @@-\section{The Top-Level Animation Loop}--\begin{code}-{-# LANGUAGE Arrows #-}--module RenderingControl- (mainAnimationLoop)- where--import Data.List-import RSAGL.FRP-import RSAGL.Edge-import RSAGL.Scene-import RSAGL.Vector-import Animation-import RSAGL.Angle-import Control.Arrow-import Data.Maybe-import PrintText-import Tables-import Models.LibraryData-import RSAGL.CoordinateSystems-import RSAGL.Affine-import RSAGL.ModelingExtras-import RSAGL.Interpolation-import ProtocolTypes-import RSAGL.Time-import RSAGL.AbstractVector-import VisibleObject-import RSAGL.InverseKinematics-import RSAGL.AnimationExtras-import Actions-import Strings-import Control.Applicative-import qualified Data.Map as Map-import Limbs-import RSAGL.Joint-import RSAGL.AbstractVector-\end{code}--\begin{code}-mainAnimationLoop :: RSAnimA1 () Camera () Camera-mainAnimationLoop = proc () ->- do m_state <- driverGetAnswerA -< "state"- switchContinue -< (fmap (const $ mainWelcome >>> mainDispatch) m_state,())- printTextOnce -< Just (UnexpectedEvent,"Waiting for engine...")- returnA -< basic_camera- where mainWelcome = proc () ->- do printTextOnce -< Just (Event,"Welcome to Roguestar-GL.")- returnA -< ()--basic_camera :: Camera-basic_camera = PerspectiveCamera {- camera_position = Point3D 0 0 0,- camera_lookat = Point3D 0 0 1,- camera_up = Vector3D 0 1 0,- camera_fov = fromDegrees 60 }-\end{code}--\subsection{The Main Dispatch}--\texttt{mainDispatch} transfers control of the main thread to a-subprogram based on the state of the engine. For example, menu-related-states dispatch to \texttt{menuDispatch} which manages the menu gui.--\texttt{mainHeader} guards against changes in the engine state.-Whenever the state changes, the header switches to \texttt{mainDispatch}.--\begin{code}-mainStateHeader :: (String -> Bool) -> RSAnimA1 () Camera () ()-mainStateHeader = genericStateHeader switchTo- where switchTo menu_state | menu_state `elem` menu_states = menuManager- switchTo planar_state | planar_state `elem` planar_states = planarGameplayDispatch- switchTo "game-over" = gameOver- switchTo unknown_state = error $ "mainStateHeader: unrecognized state: " ++ unknown_state--genericStateHeader :: (String -> RSAnimA1 i o i o) -> (String -> Bool) -> RSAnimA1 i o i ()-genericStateHeader switchTo f = proc i ->- do m_state <- driverGetAnswerA -< "state"- switchContinue -< (if fmap f m_state == Just True then Nothing else fmap switchTo m_state,i)- returnA -< ()--mainDispatch :: RSAnimA1 () Camera () Camera-mainDispatch = mainStateHeader (const False) >>> arr (const basic_camera)-\end{code}--\subsection{The Menu Dispatch}--\begin{code}-menu_states :: [String]-menu_states = ["race-selection",- "class-selection"]-menuManager :: RSAnimA1 () Camera () Camera-menuManager = proc () ->- do mainStateHeader (`elem` menu_states) -< ()- frp1Context menuDispatch -< ()--menuStateHeader :: (String -> Bool) -> RSAnimA1 () Camera () Camera-menuStateHeader f = genericStateHeader switchTo f >>> arr (const basic_camera)- where switchTo "race-selection" = menuRaceSelection- switchTo "class-selection" = menuClassSelection- switchTo unknown_state = error $ "menuStateHeader: unrecognized state: " ++ unknown_state--menuDispatch :: RSAnimA1 () Camera () Camera-menuDispatch = menuStateHeader (const False) >>> arr (const basic_camera)--menuRaceSelection :: RSAnimA1 () Camera () Camera-menuRaceSelection = proc s -> - do menuStateHeader (== "race-selection") -< s- requestPrintTextMode -< Unlimited- clearPrintTextA -< ()- printMenuA select_race_action_names -< ()- printTextA -< Just (Query,"Select a Race:")- returnA -< basic_camera--menuClassSelection :: RSAnimA1 () Camera () Camera-menuClassSelection = proc () -> - do menuStateHeader (== "class-selection") -< ()- changed <- edgep <<< sticky isJust Nothing <<<arr (fmap table_created) <<< driverGetTableA -< ("player-stats","0")- switchContinue -< (if changed then Just menuClassSelection else Nothing,())- requestPrintTextMode -< Unlimited- clearPrintTextA -< ()- printCharacterStats 0 -< ()- printMenuA select_base_class_action_names -< ()- printMenuItemA "reroll" -< ()- printTextA -< Just (Query,"Select a Class:")- returnA -< basic_camera--printCharacterStats :: Integer -> RSAnimAX any t i o () ()-printCharacterStats unique_id = proc () ->- do m_player_stats <- driverGetTableA -< ("player-stats",show unique_id)- print1CharacterStat -< (m_player_stats,"str")- print1CharacterStat -< (m_player_stats,"dex")- print1CharacterStat -< (m_player_stats,"con")- print1CharacterStat -< (m_player_stats,"int")- print1CharacterStat -< (m_player_stats,"per")- print1CharacterStat -< (m_player_stats,"cha")- print1CharacterStat -< (m_player_stats,"mind")--print1CharacterStat :: RSAnimAX any t i o (Maybe RoguestarTable,String) ()-print1CharacterStat = proc (m_player_stats,stat_str) ->- do let m_stat_int = (\x -> tableLookupInteger x ("property","value") stat_str) =<< m_player_stats- printTextA -< fmap (\x -> (Event,hrstring stat_str ++ ": " ++ show x)) m_stat_int-\end{code}--\subsection{The Game Over State}--\begin{code}-gameOver :: RSAnimA1 () Camera () Camera-gameOver = proc () ->- do printTextOnce -< Just (Event,"You have been killed.")- returnA -< basic_camera-\end{code}--\subsection{The Planar Gameplay Dispatch}--\begin{code}-planar_states :: [String]-planar_states = ["player-turn","pickup","drop","wield","attack","miss","killed"]--planarGameplayDispatch :: RSAnimA1 () Camera () Camera-planarGameplayDispatch = proc () ->- do mainStateHeader (`elem` planar_states) -< () - clearPrintTextOnce -< ()- frp1Context eventMessager -< ()- frpContext (maybeThreadIdentity terrainTileThreadIdentity) [(Nothing,terrainThreadLauncher)] -< ()- ctos <- arr (catMaybes . map (uncurry $ liftA2 (,))) <<< - frpContext (maybeThreadIdentity $ unionThreadIdentity (==)) - [(Nothing,visibleObjectThreadLauncher creatureAvatar)] -< ()- frpContext (maybeThreadIdentity $ unionThreadIdentity (==)) [(Nothing,visibleObjectThreadLauncher toolAvatar)] -< - ToolThreadInput {- tti_wield_points = Map.fromList $ map (\(uid,cto) -> (uid,cto_wield_point cto)) ctos } - lookat <- whenJust (approachA 1.0 (perSecond 3.0)) <<< sticky isJust Nothing <<<- arr (fmap (\(x,y) -> Point3D (realToFrac x) 0 (negate $ realToFrac y))) <<< centerCoordinates -< ()- accumulateSceneA -< (Infinite,lightSource $ DirectionalLight (Vector3D 0.15 1 (-0.3)) (scaleRGB 0.4 $ rgb 1.0 0.9 0.75) (scaleRGB 0.2 $ rgb 0.75 0.9 1.0))- returnA -< maybe basic_camera cameraLookAtToCamera lookat--cameraLookAtToCamera :: Point3D -> Camera-cameraLookAtToCamera look_at = PerspectiveCamera {- camera_position = translate (Vector3D 0 3 3) look_at,- camera_lookat = look_at,- camera_up = Vector3D 0 1 0,- camera_fov = fromDegrees 60 }--centerCoordinates :: RSAnimAX any t i o () (Maybe (Integer,Integer))-centerCoordinates = proc () ->- do m_center_coordinates_table <- sticky isJust Nothing <<< driverGetTableA -< ("center-coordinates","0")- returnA -< do center_coordinates_table <- m_center_coordinates_table- x <- tableLookupInteger center_coordinates_table ("axis","coordinate") "x" -- Maybe monad- y <- tableLookupInteger center_coordinates_table ("axis","coordinate") "y" - return (x,y)-\end{code}--\subsection{Terrain}--\begin{code}-terrainThreadLauncher :: RSAnimA (Maybe ProtocolTypes.TerrainTile) () () () ()-terrainThreadLauncher = spawnThreads <<< arr (map (\x -> (Just x,terrainTile x))) <<< terrainElements--terrainTile :: ProtocolTypes.TerrainTile -> RSAnimA (Maybe ProtocolTypes.TerrainTile) () () () ()-terrainTile terrain_tile = proc () ->- do t <- threadTime -< ()- still_here <- renderTerrainTile terrain_tile -< fromSeconds $ min 0 $ toSeconds t - 1- switchTerminate -< (if still_here then Nothing else Just $ terrainTile_Descending terrain_tile,())- -terrainTile_Descending :: ProtocolTypes.TerrainTile -> RSAnimA (Maybe ProtocolTypes.TerrainTile) () () () ()-terrainTile_Descending terrain_tile = proc () ->- do t <- threadTime -< ()- killThreadIf -< t >= fromSeconds 1- renderTerrainTile terrain_tile -< t- returnA -< ()--renderTerrainTile :: ProtocolTypes.TerrainTile -> RSAnimA t i o Time Bool-renderTerrainTile (ProtocolTypes.TerrainTile terrain_type (x,y)) = proc t ->- do let awayness = max 0 $ min 0.99 $ (toSeconds t)^2- terrain_elements <- terrainElements -< ()- transformA libraryA -< (Affine $ translate (Vector3D (realToFrac x) 0 (negate $ realToFrac y)) . scale' (1 - awayness),- (Local,Models.LibraryData.TerrainTile terrain_type))- returnA -< isJust $ find (\a -> tt_xy a == (x,y)) terrain_elements--terrainElements :: RSAnimA t i o () [ProtocolTypes.TerrainTile]-terrainElements = arr (maybe [] tableSelectTyped) <<< sticky isJust Nothing <<< driverGetTableA <<< arr (const ("visible-terrain","0"))--terrainTileThreadIdentity :: ThreadIdentity ProtocolTypes.TerrainTile-terrainTileThreadIdentity = unionThreadIdentity (\a b -> tt_xy a == tt_xy b)-\end{code}--\subsection{Creatures and Objects}--\begin{code}-data CreatureThreadOutput = CreatureThreadOutput {- cto_wield_point :: CoordinateSystem }--class AbstractEmpty a where- abstractEmpty :: a--instance AbstractEmpty () where- abstractEmpty = ()--instance AbstractEmpty (Maybe a) where- abstractEmpty = Nothing--visibleObjectThreadLauncher :: (AbstractEmpty o) => RSAnimA (Maybe Integer) i o i o -> RSAnimA (Maybe Integer) i o i o-visibleObjectThreadLauncher avatarA = arr (const abstractEmpty) <<< spawnThreads <<< arr (map (\x -> (Just x,avatarA))) <<< allObjects <<< arr (const ())--objectTypeGuard :: (String -> Bool) -> RSAnimA (Maybe Integer) a b () ()-objectTypeGuard f = proc () ->- do m_obj_type <- objectDetailsLookup "object-type" -< ()- killThreadIf -< maybe False (not . f) m_obj_type--creatureAvatar :: RSAnimA (Maybe Integer) () (Maybe CreatureThreadOutput) () (Maybe CreatureThreadOutput)-creatureAvatar = proc () ->- do objectTypeGuard (== "creature") -< ()- m_species <- objectDetailsLookup "species" -< ()- switchContinue -< (fmap switchTo m_species,())- returnA -< Nothing- where switchTo "encephalon" = encephalonAvatar- switchTo "recreant" = recreantAvatar- switchTo "androsynth" = androsynthAvatar- switchTo "ascendant" = ascendantAvatar- switchTo "caduceator" = caduceatorAvatar- switchTo "reptilian" = reptilianAvatar- switchTo _ = questionMarkAvatar--genericCreatureAvatar :: RSAnimA (Maybe Integer) () (Maybe CreatureThreadOutput) () CreatureThreadOutput ->- RSAnimA (Maybe Integer) () (Maybe CreatureThreadOutput) () (Maybe CreatureThreadOutput)-genericCreatureAvatar creatureA = proc () ->- do visibleObjectHeader -< ()- m_orientation <- objectIdealOrientation -< ()- switchTerminate -< if isNothing m_orientation then (Just $ genericCreatureAvatar creatureA,Nothing) else (Nothing,Nothing)- arr Just <<< transformA creatureA -< (fromMaybe (error "genericCreatureAvatar: fromMaybe") m_orientation,())--encephalonAvatar :: RSAnimA (Maybe Integer) () (Maybe CreatureThreadOutput) () (Maybe CreatureThreadOutput)-encephalonAvatar = genericCreatureAvatar $ proc () ->- do libraryA -< (Local,Encephalon)- wield_point <- exportCoordinateSystem <<< arr (joint_arm_hand . snd) <<< - bothArms MachineArmUpper MachineArmLower (Vector3D 0.66 0.66 0) (Point3D 0.145 0.145 0) 0.33 (Point3D 0.35 0.066 0.133) -< ()- returnA -< CreatureThreadOutput {- cto_wield_point = wield_point }--recreantAvatar :: RSAnimA (Maybe Integer) () (Maybe CreatureThreadOutput) () (Maybe CreatureThreadOutput)-recreantAvatar = genericCreatureAvatar $ floatBobbing 0.25 0.4 $ proc () ->- do libraryA -< (Local,Recreant)- wield_point <- exportCoordinateSystem <<< arr (joint_arm_hand . snd) <<<- bothArms MachineArmUpper MachineArmLower (Vector3D 0 (-1.0) 0) (Point3D 0.3 0.075 0) 0.5 (Point3D 0.5 0.075 0.2) -< ()- returnA -< CreatureThreadOutput {- cto_wield_point = wield_point }--androsynthAvatar :: RSAnimA (Maybe Integer) () (Maybe CreatureThreadOutput) () (Maybe CreatureThreadOutput)-androsynthAvatar = genericCreatureAvatar $ proc () ->- do libraryA -< (Local,Androsynth)- bothLegs ThinLimb ThinLimb (Vector3D 0 0 1) (Point3D (0.07) 0.5 (-0.08)) 0.7 (Point3D 0.07 0 0.0) -< ()- wield_point <- exportCoordinateSystem <<< arr (joint_arm_hand . snd) <<<- bothArms ThinLimb ThinLimb (Vector3D (1.0) (-1.0) (-1.0)) (Point3D 0.05 0.65 0.0) 0.45 (Point3D 0.15 0.34 0.1) -< ()- returnA -< CreatureThreadOutput {- cto_wield_point = wield_point }--glower :: Point3D -> Vector3D -> RSAnimA (Maybe Integer) i o () ()-glower p_init v_init = proc () ->- do local_origin <- exportToA root_coordinate_system -< origin_point_3d- transformA- (accelerationModel fps120 (p_init,perSecond $ v_init) - (proc () -> - do a <- derivative <<< derivative <<< exportToA root_coordinate_system -< origin_point_3d- returnA -< concatForces [quadraticTrap 10 p_init,- drag 1.0,- \_ _ _ -> scalarMultiply (-1) a,- \_ _ _ -> perSecond $ perSecond v_init,- \_ p _ -> perSecond $ perSecond $ vectorNormalize $- vectorToFrom origin_point_3d p `crossProduct` v_init]) - (proc (_,()) -> libraryPointAtCamera -< (Local,AscendantGlow))) -< - (translateToFrom local_origin origin_point_3d $ root_coordinate_system,())--ascendantAvatar :: RSAnimA (Maybe Integer) () (Maybe CreatureThreadOutput) () (Maybe CreatureThreadOutput)-ascendantAvatar = genericCreatureAvatar $ proc () ->- do glower (Point3D 0 0.5 0) zero -< ()- glower (Point3D 0 0.5 0.35) (Vector3D 0 0 (-1)) -< ()- glower (Point3D 0 0.5 (-0.35)) (Vector3D 0 0 1) -< ()- glower (Point3D 0.35 0.5 0) (Vector3D (-1) 0 0) -< ()- glower (Point3D (-0.35) 0.5 0) (Vector3D 1 0 0) -< ()- accumulateSceneA -< (Local,- lightSource $ PointLight (Point3D 0 0.5 0)- (measure (Point3D 0 0.5 0) (Point3D 0 0 0))- azure- azure)- t <- threadTime -< ()- wield_point <- exportCoordinateSystem -< translate (rotateY (fromRotations $ t `cyclical'` (fromSeconds 3)) $ Vector3D 0.25 0.5 0)- returnA -< CreatureThreadOutput {- cto_wield_point = wield_point }--caduceatorAvatar :: RSAnimA (Maybe Integer) () (Maybe CreatureThreadOutput) () (Maybe CreatureThreadOutput)-caduceatorAvatar = genericCreatureAvatar $ proc () ->- do libraryA -< (Local,Caduceator)- wield_point <- exportCoordinateSystem <<< arr (joint_arm_hand . snd) <<<- bothArms CaduceatorArmUpper CaduceatorArmLower (Vector3D 1.0 (-1.0) 1.0) (Point3D 0.1 0.15 0.257) 0.34 (Point3D 0.02 0.17 0.4) -< ()- returnA -< CreatureThreadOutput {- cto_wield_point = wield_point }--reptilianAvatar :: RSAnimA (Maybe Integer) () (Maybe CreatureThreadOutput) () (Maybe CreatureThreadOutput)-reptilianAvatar = genericCreatureAvatar $ proc () ->- do libraryA -< (Local,Reptilian)- bothLegs ReptilianLegUpper ReptilianLegLower (Vector3D 0 0 1) (Point3D (0.05) 0.25 (-0.1)) 0.29 (Point3D 0.07 0 0.0) -< ()- wield_point <- exportCoordinateSystem <<< arr (joint_arm_hand . snd) <<<- bothArms ReptilianArmUpper ReptilianArmLower (Vector3D 1.0 0.0 1.0) (Point3D (0.05) 0.35 (-0.1)) 0.25 (Point3D 0.07 0.25 0.12) -< ()- returnA -< CreatureThreadOutput {- cto_wield_point = wield_point }--toolAvatar :: RSAnimA (Maybe Integer) ToolThreadInput () ToolThreadInput ()-toolAvatar = proc tti ->- do objectTypeGuard (== "tool") -< ()- m_tool <- objectDetailsLookup "tool" -< ()- switchContinue -< (fmap switchTo m_tool,tti)- returnA -< ()- where switchTo "phase_pistol" = phasePistolAvatar- switchTo _ = questionMarkAvatar >>> arr (const ())--phasePistolAvatar :: RSAnimA (Maybe Integer) ToolThreadInput () ToolThreadInput ()-phasePistolAvatar = proc tti ->- do visibleObjectHeader -< ()- m_orientation <- wieldableObjectIdealOrientation -< tti- transformA libraryA -< maybe (root_coordinate_system,(Local,NullModel))- (\o -> (o,(Local,PhasePistol))) - m_orientation--floatBobbing :: Double -> Double -> RSAnimAX any t i o j p -> RSAnimAX any t i o j p-floatBobbing ay by animationA = proc j ->- do t <- threadTime -< ()- let float_y = lerpBetween (-1,sine $ fromRotations $ t `cyclical'` (fromSeconds 5),1) (ay,by)- transformA animationA -< (Affine $ translate (Vector3D 0 float_y 0),j)--questionMarkAvatar :: RSAnimA (Maybe Integer) i o i (Maybe CreatureThreadOutput)-questionMarkAvatar = proc _ ->- do visibleObjectHeader -< ()- t <- threadTime -< ()- m_object_type <- objectDetailsLookup "object-type" -< ()- m_species <- objectDetailsLookup "species" -< ()- m_tool <- objectDetailsLookup "tool" -< () - debugOnce -< if any (isJust) [m_object_type,m_species,m_tool] - then (Just $ "questionMarkAvatar: apparently didn't recognize object: " ++ - show m_object_type ++ ", " ++ show m_species ++ ", " ++ show m_tool)- else Nothing- m_position <- objectIdealPosition -< ()- let float_y = sine $ fromRotations $ t `cyclical'` (fromSeconds 5)- let m_transform = fmap (translate (Vector3D 0 (0.7 + float_y/10) 0)) m_position - transformA libraryA -< maybe (Affine id,(Local,NullModel)) (\p -> (Affine $ translateToFrom p origin_point_3d,(Local,QuestionMark))) m_transform- m_wield_point <- whenJust exportCoordinateSystem -< fmap (\p -> translate (vectorToFrom p origin_point_3d `add` Vector3D 0.4 0 0)) m_transform - returnA -< - do wield_point <- m_wield_point- return $ CreatureThreadOutput {- cto_wield_point = wield_point }-\end{code}--\subsection{Messages}--\begin{code}-eventStateHeader :: (String -> Bool) -> RSAnimA1 () () () ()-eventStateHeader = genericStateHeader switchTo- where switchTo s = fromMaybe eventMessager $ lookup s messages--eventMessager :: RSAnimA1 () () () ()-eventMessager = proc () -> - do eventStateHeader (isNothing . flip lookup messages) -< () - blockContinue -< True --messageState :: String -> RSAnimA1 () () () (Maybe String) -> (String,RSAnimA1 () () () ())-messageState s actionA = (s,eventStateHeader (== s) >>> (proc () ->- do m_string <- actionA -< ()- blockContinue -< isNothing m_string- printTextOnce -< fmap ((,) Event) m_string))--messages :: [(String,RSAnimA1 () () () ())]-messages = [- messageState "attack" $ proc () -> - do m_weapon <- driverGetAnswerA -< "weapon-used"- returnA -< - do weapon <- m_weapon- return $ case () of- () | weapon == "0" -> "It attacks! It hits!"- () | otherwise -> "You attack! You hit!",- messageState "miss" $ proc () -> - do m_weapon <- driverGetAnswerA -< "weapon-used"- returnA -<- do weapon <- m_weapon- return $ case () of- () | weapon == "0" -> "It attacks! It misses."- () | otherwise -> "You attack! You miss.",- messageState "killed" $ proc () -> - do m_who_killed <- driverGetAnswerA -< "who-killed"- returnA -<- do who_killed <- m_who_killed- return $ case () of- () | who_killed == "2" -> "You are mortally wounded."- () | otherwise -> "You kill it!"]-\end{code}
+ src/Statistics.hs view
@@ -0,0 +1,79 @@+module Statistics+ (Statistics,newStatistics,runStatistics)+ where++import qualified Data.Vector.Unboxed as V+import Statistics.Sample as S+import Data.List as L+import Statistics.Quantile+import Control.Concurrent+import RSAGL.FRP.Time+import System.IO+import System.IO.Unsafe+import RSAGL.Math.AbstractVector+import Control.Monad+import RSAGL.Types+import Text.Printf+import Control.Exception++{-# NOINLINE error_pump #-}+error_pump :: Chan String+error_pump = unsafePerformIO $+ do pump <- newChan+ _ <- forkIO $ forever $+ do hPutStrLn stderr =<< readChan pump+ return pump++data Statistics = Statistics {+ statistics_sample :: MVar [Double],+ statistics_max :: MVar Double,+ statistics_avg :: MVar Double,+ statistics_time :: MVar Time, -- time since last flushing of statistics+ statistics_name :: String }++newStatistics :: String -> IO Statistics+newStatistics name =+ do sample <- newMVar []+ t <- newMVar =<< getTime+ s_max <- newMVar 0+ s_avg <- newMVar 1000+ return $ Statistics {+ statistics_sample = sample,+ statistics_max = s_max,+ statistics_avg = s_avg,+ statistics_time = t,+ statistics_name = name }++runStatistics :: Statistics -> IO a -> IO a+runStatistics stats action =+ do t_start <- getTime+ a <- action+ t_end <- getTime+ let t_diff = f2f $ toSeconds $ t_end `sub` t_start+ modifyMVar_ (statistics_sample stats) $ return . (t_diff :)+ modifyMVar_ (statistics_max stats) $ return . (max t_diff)+ modifyMVar_ (statistics_avg stats) $ return . (\t -> (99*t + t_diff)/100)+ _ <- evaluate =<< readMVar (statistics_max stats)+ _ <- evaluate =<< readMVar (statistics_avg stats)+ modifyMVar_ (statistics_time stats) $ \t ->+ do let should_report = (toSeconds (t_end `sub` t) > 30)+ when should_report $ liftM (const ()) $ forkIO $+ do modifyMVar_ (statistics_sample stats) $ \x ->+ do let sv = V.fromList $ take (60*30) x :: Sample+ let f = printf "%.4f" . (*60) :: Double -> String+ writeChan error_pump $ "\nSTATISTICS " ++ statistics_name stats +++ "\nMEAN " ++ f (mean sv) +++ "\nSTD DEV " ++ f (stdDev sv) +++ "\nKURTOSIS " ++ f (kurtosis sv) +++ "\nQUARTILES " ++ (concat $ intersperse " | " $ map f $+ [continuousBy medianUnbiased 0 4 sv,+ continuousBy medianUnbiased 1 4 sv,+ continuousBy medianUnbiased 2 4 sv,+ continuousBy medianUnbiased 3 4 sv,+ continuousBy medianUnbiased 4 4 sv])+ writeChan error_pump =<< liftM ((statistics_name stats ++ " lifetime maximum ") ++) (liftM f $ readMVar $ statistics_max stats)+ writeChan error_pump =<< liftM ((statistics_name stats ++ " floating average ") ++) (liftM f $ readMVar $ statistics_avg stats)+ return []+ return $ if should_report then t_end else t+ return a+
+ src/Strings.hs view
@@ -0,0 +1,35 @@+{-# LANGUAGE OverloadedStrings #-}+module Strings+ (replace,capitalize,hrstring)+ where++import Data.Char+import qualified Data.ByteString.Char8 as B++-- | Replace all instances of the first string with the second string in the third string.+-- @replace "old" "new" "What's old?"@+replace :: B.ByteString -> B.ByteString -> B.ByteString -> B.ByteString+replace a b s = case B.breakSubstring a s of+ (x,y) | B.null y -> x+ (x,y) | otherwise -> x `B.append` b `B.append` replace a b (B.drop (B.length a) y)++-- | Just capitalize the first letter of the string.+capitalize :: B.ByteString -> B.ByteString+capitalize s = case B.uncons s of+ Nothing -> s+ Just (c,s') -> toUpper c `B.cons` s'++-- | Human readable strings, when we can't just rip the plaintext from the protocol.+hrstring :: B.ByteString -> B.ByteString+hrstring "str" = "Strength "+hrstring "spd" = "Speed "+hrstring "con" = "Endurance "+hrstring "int" = "Intellect "+hrstring "per" = "Perception "+hrstring "cha" = "Charisma "+hrstring "mind" = "Mindfulness "+hrstring "maxhp" = "Health "+hrstring "forceadept" = "force adept"+hrstring x = B.map (\c -> if c == '_' then ' ' else c) x++
− src/Strings.lhs
@@ -1,21 +0,0 @@-\section{Strings}--This section merely contains a mapping from strings used in the protocol to human-readable strings.-For example, "str" becomes "strength."--\begin{code}-module Strings- (hrstring)- where--hrstring :: String -> String-hrstring "str" = "strength"-hrstring "dex" = "dexterity"-hrstring "con" = "constitution"-hrstring "int" = "intelligence"-hrstring "per" = "perception"-hrstring "cha" = "charisma"-hrstring "mind" = "mindfulness"-hrstring "forceadept" = "force adept"-hrstring x = x-\end{code}
+ src/Tables.hs view
@@ -0,0 +1,59 @@+{-# LANGUAGE OverloadedStrings #-}+module Tables+ (RoguestarTable(..),+ TableDataFormat(..),+ tableSelect,+ tableSelectFormatted,+ tableLookup,+ tableLookupInteger,+ readInteger)+ where++import Data.List+import RSAGL.FRP.Time+import qualified Data.ByteString.Char8 as B++-- | 'RoguestarTable' is a crude implementation of a relational data table that is used to represent information that has been sent to us from roguestar-engine.+data RoguestarTable = RoguestarTable { table_created :: Time, table_name, table_id :: B.ByteString, table_header :: [B.ByteString], table_data :: [[B.ByteString]] }+ deriving (Eq,Show)++-- | 'tableSelect' selects rows from a table, like the SQL select statement.+-- For example: \texttt{tableSelect people ["name","sex","phone-number"] = [["bob","male","123-4567"],["susan","female","987-6543"]]}+-- If a given header is not in the table, it lists \texttt{"???"} as the value.+--+-- There is a guarantee that all select functions will return results in the same order.+tableSelect :: RoguestarTable -> [B.ByteString] -> [[B.ByteString]]+tableSelect table headers = let header_indices = map (\x -> elemIndex x $ table_header table) headers+ in map (rowSelect header_indices) $ table_data table++rowSelect :: [Maybe Int] -> [B.ByteString] -> [B.ByteString]+rowSelect (Nothing:more) row = "???" : rowSelect more row+rowSelect (Just x:more) row = (row !! x) : rowSelect more row+rowSelect [] _ = []++-- | 'tableSelectFormatted' allows Selection of arbitrary strings and integers.+tableSelectFormatted :: RoguestarTable -> [TableDataFormat B.ByteString B.ByteString] -> [[TableDataFormat B.ByteString Integer]]+tableSelectFormatted table headers = map (toFormat headers) $ tableSelect table (map toString headers)++-- | "tableLookup table \(\"name\",\"phone-number\"\) \"bob\"" yields Bob\'s phone number, or nothing if \"bob\" isn\'t in the table.+tableLookup :: RoguestarTable -> (B.ByteString,B.ByteString) -> B.ByteString -> Maybe B.ByteString+tableLookup table (k,v) x = fmap (!! 1) $ find ((== x) . head) $ tableSelect table [k,v]++-- | 'tableLookupInteger' works as 'tableLookup', but answers an 'Integer'+tableLookupInteger :: RoguestarTable -> (B.ByteString,B.ByteString) -> B.ByteString -> Maybe Integer+tableLookupInteger table headers x = tableLookup table headers x >>= readInteger++data TableDataFormat s n = TDString s+ | TDNumber n++readInteger :: B.ByteString -> Maybe Integer+readInteger = fmap fst . B.readInteger++toString :: TableDataFormat B.ByteString B.ByteString -> B.ByteString+toString (TDString str) = str+toString (TDNumber str) = str++toFormat :: [TableDataFormat a b] -> [B.ByteString] -> [TableDataFormat B.ByteString Integer]+toFormat headers row = zipWith toFormat_ headers row+ where toFormat_ (TDString {}) str = TDString str+ toFormat_ (TDNumber {}) str = maybe (TDString str) TDNumber $ readInteger str
− src/Tables.lhs
@@ -1,81 +0,0 @@-\section{Tables}--\begin{code}-module Tables- (RoguestarTable(..),- TableDataFormat(..),- tableSelect,- tableSelectFormatted,- tableLookup,- tableLookupInteger)- where--import Data.List-import Data.Maybe-import RSAGL.Time-import Control.Monad-\end{code}--\texttt{RoguestarTable} is a crude implementation of a relational data table that is used to represent information that has been sent to us from roguestar-engine.--\begin{code}-data RoguestarTable = RoguestarTable { table_created :: Time, table_name, table_id :: String, table_header :: [String], table_data :: [[String]] }- deriving (Eq,Show)-\end{code}--\texttt{tableSelect} selects rows from a table, like the SQL select statement.-For example: \texttt{tableSelect people ["name","sex","phone-number"] = [["bob","male","123-4567"],["susan","female","987-6543"]]}-If a given header is not in the table, it lists \texttt{"???"} as the value.--There is a guarantee that all select functions will return results in the same order.--\begin{code}-tableSelect :: RoguestarTable -> [String] -> [[String]]-tableSelect table headers = let header_indices = map (\x -> elemIndex x $ table_header table) headers- in map (rowSelect header_indices) $ table_data table--rowSelect :: [Maybe Int] -> [String] -> [String]-rowSelect (Nothing:more) row = "???" : rowSelect more row-rowSelect (Just x:more) row = (row !! x) : rowSelect more row-rowSelect [] _ = []-\end{code}--\texttt{tableSelectFormatted} allows Selection of arbitrary strings and integers.--\begin{code}-tableSelectFormatted :: RoguestarTable -> [TableDataFormat String String] -> [[TableDataFormat String Integer]]-tableSelectFormatted table headers = map (toFormat headers) $ tableSelect table (map toString headers)-\end{code}--\texttt{tableLookup table ("name","phone-number") "bob"} yields Bob's phone number, or nothing if "bob" isn't in the table.--\begin{code}-tableLookup :: RoguestarTable -> (String,String) -> String -> Maybe String-tableLookup table (k,v) x = fmap (!! 1) $ find ((== x) . head) $ tableSelect table [k,v]-\end{code}--\texttt{tableLookupInteger} works as \texttt{tableLookup}, but answers an \texttt{Integer}.--\begin{code}-tableLookupInteger :: RoguestarTable -> (String,String) -> String -> Maybe Integer-tableLookupInteger table headers x = tableLookup table headers x >>= readInteger-\end{code}--\subsection{Formatting Arbitrary Table Elements}--\begin{code}-data TableDataFormat s n = TDString s- | TDNumber n--readInteger :: String -> Maybe Integer-readInteger = listToMaybe . map fst . reads--toString :: TableDataFormat String String -> String-toString (TDString str) = str-toString (TDNumber str) = str--toFormat :: [TableDataFormat a b] -> [String] -> [TableDataFormat String Integer]-toFormat headers row = zipWith toFormat_ headers row- where toFormat_ (TDString {}) str = TDString str- toFormat_ (TDNumber {}) str = maybe (TDString str) TDNumber $ readInteger str-\end{code}
+ src/VisibleObject.hs view
@@ -0,0 +1,232 @@+{-# LANGUAGE+ Arrows,+ OverloadedStrings,+ Rank2Types,+ TypeFamilies,+ FlexibleContexts #-}++module VisibleObject+ (ToolThreadInput(..),+ CreatureThreadOutput(..),+ AbstractEmpty(..),+ AvatarSwitch,+ visibleObjectThreadLauncher,+ objectTypeGuard,+ questionMarkAvatar,+ visibleObjects,+ allObjects,+ VisibleObjectReference(..),+ getVisibleObject,+ isBeingWielded,+ isWielding,+ visibleObject,+ objectDetailsLookup,+ objectDestination,+ objectIdealPosition,+ objectFacing,+ objectIdealFacing,+ objectIdealOrientation,+ visibleObjectHeader,+ wieldableObjectIdealOrientation)+ where++import RSAGL.FRP+import Data.Maybe+import ProtocolTypes+import Animation+import Tables+import Control.Arrow+import Data.List+import RSAGL.Animation+import AnimationExtras+import RSAGL.Math+import qualified Data.Set as Set+import qualified Data.Map as Map+import Control.Monad+import Scene+import Models.LibraryData+import qualified Data.ByteString.Char8 as B++data ToolThreadInput = ToolThreadInput {+ -- | Wield points of all creatures that are on screen.+ tti_wield_points :: Map.Map Integer CoordinateSystem }++data CreatureThreadOutput = CreatureThreadOutput {+ -- | Wield point for this creature. This is the coordinate system in which+ -- a tool should be rendered, where the origin is the point at which the+ -- creature is grasping the weapon.+ cto_wield_point :: CoordinateSystem }++class AbstractEmpty a where+ abstractEmpty :: a++instance AbstractEmpty () where+ abstractEmpty = ()++instance AbstractEmpty (Maybe a) where+ abstractEmpty = Nothing++type AvatarSwitch i o m = RSwitch Enabled (Maybe Integer) i o m++-- | Avatar for unrecognized objects. Basically this is a big conspicuous+-- warning that something is implemented in the engine but not in the client.+questionMarkAvatar :: (FRPModel m) => FRP e (AvatarSwitch i o m) i (Maybe CreatureThreadOutput)+questionMarkAvatar = proc _ ->+ do visibleObjectHeader -< ()+ t <- threadTime -< ()+ m_object_type <- objectDetailsLookup ThisObject "object-type" -< ()+ m_species <- objectDetailsLookup ThisObject "species" -< ()+ m_tool <- objectDetailsLookup ThisObject "tool" -< ()+ m_building <- objectDetailsLookup ThisObject "building" -< ()+ debugOnce -< if any (isJust) [m_object_type,m_species,m_tool]+ then (Just $ B.concat $ ["questionMarkAvatar: apparently didn't recognize object: ",+ B.pack $ show m_object_type, ", ", B.pack $ show m_species, ", ", B.pack $ show m_tool, ", ", B.pack $ show m_building]) + else Nothing+ m_position <- objectIdealPosition ThisObject -< ()+ let float_y = sine $ fromRotations $ t `cyclical'` (fromSeconds 5)+ let m_transform = fmap (translate (Vector3D 0 (0.7 + float_y/10) 0)) m_position + transformA libraryA -< maybe (Affine id,(scene_layer_local,NullModel))+ (\p -> (Affine $ translateToFrom p origin_point_3d,+ (scene_layer_local,SimpleModel QuestionMark))) m_transform+ m_wield_point <- whenJust exportCoordinateSystem -< fmap (\p -> translate (vectorToFrom p origin_point_3d `add` Vector3D 0.4 0 0)) m_transform + returnA -< + do wield_point <- m_wield_point+ return $ CreatureThreadOutput {+ cto_wield_point = wield_point }+++-- | Launch threads to represent every visible object.+visibleObjectThreadLauncher :: (FRPModel m,AbstractEmpty o) =>+ FRP e (AvatarSwitch i o m) i o ->+ FRP e (AvatarSwitch i o m) i o+visibleObjectThreadLauncher avatarA =+ arr (const abstractEmpty) <<<+ spawnThreads <<<+ arr (map (\x -> (Just x,avatarA))) <<<+ newListElements <<< allObjects <<<+ arr (const ())++-- | Kill a thread if an object has the wrong \"object-type\" field, e.g. anything that isn't a \"creature\".+objectTypeGuard :: (FRPModel m, StateOf m ~ AnimationState, ThreadingOf m ~ Enabled, ThreadIDOf m ~ Maybe Integer) => (B.ByteString -> Bool) -> FRP e m () ()+objectTypeGuard f = proc () ->+ do m_obj_type <- objectDetailsLookup ThisObject "object-type" -< ()+ killThreadIf -< maybe False (not . f) m_obj_type++-- | Header function that just kills the current thread if it isn't a visible object.+visibleObjectHeader :: (FRPModel m, ThreadingOf m ~ Enabled, ThreadIDOf m ~ Maybe Integer, StateOf m ~ AnimationState) => FRP e m () ()+visibleObjectHeader = proc () ->+ do unique_id <- arr (fromMaybe (error "visibleObjectHeader: threadIdentity was Nothing")) <<< threadIdentity -< ()+ uids <- allObjects -< ()+ killThreadIf -< not $ unique_id `elem` uids++-- | List all 'VisibleObject' records.+visibleObjects :: (FRPModel m, StateOf m ~ AnimationState) => FRP e m () [VisibleObject]+visibleObjects = proc () -> arr (maybe [] tableSelectTyped) <<< sticky isJust Nothing <<< driverGetTableA -< ("visible-objects","0")++-- | List all object UIDs.+allObjects :: (FRPModel m,StateOf m ~ AnimationState) => FRP e m () [Integer]+allObjects = proc () ->+ do visible_object_ids <- arr (map vo_unique_id . maybe [] tableSelectTyped)+ <<< sticky isJust Nothing+ <<< driverGetTableA -< ("visible-objects","0")+ wielded_object_ids <- arr (map wo_unique_id . maybe [] tableSelectTyped)+ <<< sticky isJust Nothing+ <<< driverGetTableA -< ("wielded-objects","0")+ returnA -< Set.toList $ Set.fromList visible_object_ids `Set.union`+ Set.fromList wielded_object_ids++{-------------------------------------------------------------------------------+ Retrieving information about specific visible objects.+-------------------------------------------------------------------------------}++-- | Indirect or direct reference to a visible object.+data VisibleObjectReference =+ ThisObject+ | UniqueID Integer+ | WieldedParent VisibleObjectReference+ | WieldedTool VisibleObjectReference+ | Attacker++-- | As a Tool, who is wielding you?+wieldedParent :: (FRPModel m,StateOf m ~ AnimationState) => FRP e m (Maybe Integer) (Maybe Integer)+wieldedParent = proc m_unique_id -> + do wielded_pairs <- arr (maybe [] tableSelectTyped) <<< sticky isJust Nothing <<< driverGetTableA -< ("wielded-objects","0")+ returnA -< fmap wo_creature_id $ find ((== m_unique_id) . Just . wo_unique_id) wielded_pairs++-- | As a creature, what tool are you wielding?+wieldedTool :: (FRPModel m,StateOf m ~ AnimationState) => FRP e m (Maybe Integer) (Maybe Integer)+wieldedTool = proc m_unique_id ->+ do wielded_pairs <- arr (maybe [] tableSelectTyped) <<< sticky isJust Nothing <<< driverGetTableA -< ("wielded-objects","0")+ returnA -< fmap wo_unique_id $ find ((== m_unique_id) . Just . wo_creature_id) wielded_pairs++-- | Get the unique ID of the object specified.+getVisibleObject :: (FRPModel m, StateOf m ~ AnimationState, ThreadIDOf m ~ Maybe Integer) => VisibleObjectReference -> FRP e m () (Maybe Integer)+getVisibleObject (ThisObject) = threadIdentity+getVisibleObject (UniqueID unique_id) = proc () -> returnA -< Just unique_id+getVisibleObject (WieldedParent (WieldedTool ref)) = getVisibleObject ref+getVisibleObject (WieldedTool (WieldedParent ref)) = getVisibleObject ref+getVisibleObject (WieldedParent ref) = getVisibleObject ref >>> wieldedParent+getVisibleObject (WieldedTool ref) = getVisibleObject ref >>> wieldedTool+getVisibleObject Attacker = arr (const "who-attacks") >>> driverGetAnswerA >>> arr (maybe Nothing readInteger)++-- | As a Tool, are you currently being wielded by anyone?+isBeingWielded :: (FRPModel m,StateOf m ~ AnimationState,ThreadIDOf m ~ Maybe Integer) => VisibleObjectReference -> FRP e m () Bool+isBeingWielded obj = getVisibleObject (WieldedParent obj) >>> arr isJust++-- | As a Creature, are you currently wielding a tool?+isWielding :: (FRPModel m,StateOf m ~ AnimationState,ThreadIDOf m ~ Maybe Integer) => VisibleObjectReference -> FRP e m () Bool+isWielding obj = getVisibleObject (WieldedTool obj) >>> arr isJust++-- | Get the 'VisibleObject' record for any object.+visibleObject :: (FRPModel m,StateOf m ~ AnimationState,ThreadIDOf m ~ Maybe Integer) => VisibleObjectReference -> FRP e m () (Maybe VisibleObject)+visibleObject obj = proc () ->+ do m_unique_id <- getVisibleObject obj -< ()+ visible_objects <- visibleObjects -< ()+ returnA -< do find ((== m_unique_id) . Just . vo_unique_id) visible_objects++-- | Get an "object-details" field for the specified visible object.+objectDetailsLookup :: (FRPModel m,StateOf m ~ AnimationState,ThreadIDOf m ~ Maybe Integer) => VisibleObjectReference -> B.ByteString -> FRP e m () (Maybe B.ByteString)+objectDetailsLookup obj field = proc _ ->+ do m_unique_id <- getVisibleObject obj -< ()+ m_details_table <- arr (fromMaybe Nothing) <<< whenJust (driverGetTableA <<< arr (\x -> ("object-details",B.pack $ show x))) -< m_unique_id+ sticky isJust Nothing -< + do details_table <- m_details_table+ tableLookup details_table ("property","value") field++-- | Grid position to which the specified object should move.+objectDestination :: (FRPModel m,StateOf m ~ AnimationState,ThreadIDOf m ~ Maybe Integer) => VisibleObjectReference -> FRP e m () (Maybe (Integer,Integer))+objectDestination obj = arr (fmap vo_xy) <<< visibleObject obj++-- | Correct position of the specified object, with smooth translation.+objectIdealPosition :: (FRPModel m,StateOf m ~ AnimationState,ThreadIDOf m ~ Maybe Integer) => VisibleObjectReference -> FRP e m () (Maybe Point3D)+objectIdealPosition obj = + whenJust (approachA 0.25 (perSecond 3)) <<< + arr (fmap (\(x,y) -> Point3D (realToFrac x) 0 (negate $ realToFrac y))) <<< + objectDestination obj++-- | Goal direction in which the specified object should be pointed.+objectFacing :: (FRPModel m,StateOf m ~ AnimationState,ThreadIDOf m ~ Maybe Integer) => VisibleObjectReference -> FRP e m () (Maybe BoundAngle)+objectFacing obj = arr (fmap vo_facing) <<< visibleObject obj++-- | Direction in which the specified object should be pointed, with smooth rotation.+objectIdealFacing :: (FRPModel m,StateOf m ~ AnimationState,ThreadIDOf m ~ Maybe Integer) => VisibleObjectReference -> FRP e m () (Maybe Angle)+objectIdealFacing obj = arr (fmap unboundAngle) <<< whenJust (approachA 0.1 (perSecond 1)) <<< objectFacing obj++-- | Combine 'objectIdealPosition' and 'objectIdealFacing' to place an object.+objectIdealOrientation :: (FRPModel m,StateOf m ~ AnimationState,ThreadIDOf m ~ Maybe Integer) => VisibleObjectReference -> FRP e m () (Maybe CoordinateSystem)+objectIdealOrientation obj = proc () ->+ do m_p <- objectIdealPosition obj -< ()+ m_a <- objectIdealFacing obj -< ()+ returnA -< do p <- m_p+ a <- m_a+ return $ translate (vectorToFrom p origin_point_3d) $ rotateY a $ root_coordinate_system++-- | 'objectIdealOrientation' implementation that is aware of wield points for wieldable objects. If an object is being+-- wielded, it will snap to it's wield point.+wieldableObjectIdealOrientation :: (FRPModel m,StateOf m ~ AnimationState,ThreadIDOf m ~ Maybe Integer) => VisibleObjectReference -> FRP e m ToolThreadInput (Maybe CoordinateSystem)+wieldableObjectIdealOrientation obj = proc tti ->+ do ideal_resting <- objectIdealOrientation obj -< ()+ m_wielded_parent <- getVisibleObject (WieldedParent obj) -< ()+ let wield_point = do wielded_parent <- m_wielded_parent+ Map.lookup wielded_parent $ tti_wield_points tti+ returnA -< wield_point `mplus` ideal_resting
− src/VisibleObject.lhs
@@ -1,117 +0,0 @@-\section{Visible Objects}--\begin{code}-{-# LANGUAGE Arrows #-}--module VisibleObject- (ToolThreadInput(..),- visibleObjects,- allObjects,- wieldedParent,- wieldedTool,- visibleObject,- visibleObjectUniqueID,- objectDetailsLookup,- objectDestination,- objectIdealPosition,- objectFacing,- objectIdealFacing,- objectIdealOrientation,- visibleObjectHeader,- wieldableObjectIdealOrientation)- where--import RSAGL.FRP-import RSAGL.Edge-import Data.Maybe-import ProtocolTypes-import Animation-import Tables-import Control.Arrow-import Data.List-import RSAGL.Time-import RSAGL.InverseKinematics-import RSAGL.Vector-import RSAGL.Angle-import RSAGL.Affine-import RSAGL.CoordinateSystems-import qualified Data.Set as Set-import qualified Data.Map as Map-import Control.Monad--data ToolThreadInput = ToolThreadInput {- tti_wield_points :: Map.Map Integer CoordinateSystem }--visibleObjectHeader :: RSAnimA (Maybe Integer) i o () ()-visibleObjectHeader = proc () ->- do unique_id <- arr (fromMaybe (error "visibleObjectHeader: threadIdentity was Nothing")) <<< threadIdentity -< ()- uids <- allObjects -< ()- killThreadIf -< isNothing $ find (== unique_id) uids--visibleObjects :: RSAnimAX any t i o () [VisibleObject]-visibleObjects = proc () -> arr (maybe [] tableSelectTyped) <<< sticky isJust Nothing <<< driverGetTableA -< ("visible-objects","0")--allObjects :: RSAnimAX any a i o () [Integer]-allObjects = proc () ->- do visible_object_ids <- arr (map vo_unique_id . maybe [] tableSelectTyped) <<< sticky isJust Nothing <<< driverGetTableA -< ("visible-objects","0")- wielded_object_ids <- arr (map wo_unique_id . maybe [] tableSelectTyped) <<< sticky isJust Nothing <<< driverGetTableA -< ("wielded-objects","0")- returnA -< Set.toList $ Set.fromList visible_object_ids `Set.union` Set.fromList wielded_object_ids--wieldedParent :: RSAnimA (Maybe Integer) i o () (Maybe Integer)-wieldedParent = proc () -> - do unique_id <- visibleObjectUniqueID -< ()- wielded_pairs <- arr (maybe [] tableSelectTyped) <<< sticky isJust Nothing <<< driverGetTableA -< ("wielded-objects","0")- returnA -< fmap wo_creature_id $ find ((== unique_id) . wo_unique_id) wielded_pairs--wieldedTool :: RSAnimA (Maybe Integer) i o () (Maybe Integer)-wieldedTool = proc () ->- do unique_id <- visibleObjectUniqueID -< ()- wielded_pairs <- arr (maybe [] tableSelectTyped) <<< sticky isJust Nothing <<< driverGetTableA -< ("wielded-objects","0")- returnA -< fmap wo_unique_id $ find ((== unique_id) . wo_creature_id) wielded_pairs--visibleObjectUniqueID :: RSAnimA (Maybe Integer) i o () Integer-visibleObjectUniqueID = arr (fromMaybe (error "visibleObjectUniqueID: threadIdentity was Nothing")) <<< threadIdentity--visibleObject :: RSAnimA (Maybe Integer) i o () (Maybe VisibleObject)-visibleObject = proc () ->- do unique_id <- visibleObjectUniqueID -< ()- visible_objects <- visibleObjects -< ()- returnA -< do find ((== unique_id) . vo_unique_id) visible_objects--objectDetailsLookup :: String -> RSAnimA (Maybe Integer) i o () (Maybe String)-objectDetailsLookup field = proc _ ->- do unique_id <- visibleObjectUniqueID -< ()- m_details_table <- driverGetTableA -< ("object-details",show unique_id)- sticky isJust Nothing -< (\x -> tableLookup x ("property","value") field) =<< m_details_table--objectDestination :: RSAnimA (Maybe Integer) i o () (Maybe (Integer,Integer))-objectDestination = arr (fmap vo_xy) <<< visibleObject- -objectIdealPosition :: RSAnimA (Maybe Integer) i o () (Maybe Point3D)-objectIdealPosition = - whenJust (approachA 0.25 (perSecond 3)) <<< - arr (fmap (\(x,y) -> Point3D (realToFrac x) 0 (negate $ realToFrac y))) <<< - objectDestination--objectFacing :: RSAnimA (Maybe Integer) i o () (Maybe BoundAngle)-objectFacing = arr (fmap vo_facing) <<< visibleObject--objectIdealFacing :: RSAnimA (Maybe Integer) i o () (Maybe Angle)-objectIdealFacing = arr (fmap unboundAngle) <<< whenJust (approachA 0.1 (perSecond 1)) <<< objectFacing--objectIdealOrientation :: RSAnimA (Maybe Integer) i o () (Maybe CoordinateSystem)-objectIdealOrientation = proc () ->- do m_p <- objectIdealPosition -< ()- m_a <- objectIdealFacing -< ()- returnA -< do p <- m_p- a <- m_a- return $ translate (vectorToFrom p origin_point_3d) $ rotateY a $ root_coordinate_system--wieldableObjectIdealOrientation :: RSAnimA (Maybe Integer) i o ToolThreadInput (Maybe CoordinateSystem)-wieldableObjectIdealOrientation = proc tti ->- do ideal_resting <- objectIdealOrientation -< ()- m_wielded_parent <- wieldedParent -< ()- let wield_point = do wielded_parent <- m_wielded_parent- Map.lookup wielded_parent $ tti_wield_points tti- returnA -< wield_point `mplus` ideal_resting-\end{code}