{-# LANGUAGE Arrows, OverloadedStrings, TypeFamilies, RankNTypes, FlexibleContexts #-} module AnimationEvents (eventMessager,recognized_events) where import Animation import Data.Maybe import Control.Arrow import AnimationExtras import PrintTextData import MaybeArrow import Data.Monoid import Tables import RSAGL.FRP import Strings import qualified Data.ByteString.Char8 as B eventStateHeader :: (FRPModel m) => (B.ByteString -> Bool) -> EventHandler e m () () eventStateHeader stateP = proc () -> do genericStateHeader switchTo stateP -< () acs <- driverGetAnswerA -< "action-count" iacs <- initial -< acs switchContinue -< (if acs /= iacs then Just eventMessager else Nothing,()) where switchTo s = fromMaybe eventMessager $ lookup s messages type EventSwitch m = RSwitch Disabled () () () m type EventHandler e m a b = FRP e (EventSwitch m) a b type MessageHandler e m a b = MaybeArrow (FRP e (EventSwitch m)) a b -- | Print messages about game events. eventMessager :: (FRPModel m) => EventHandler e m () () eventMessager = proc () -> do eventStateHeader (not . (`elem` recognized_events)) -< () blockContinue -< True -- | A handler for messages from a specific event state, such as \"attack-event\". -- Parameters are: -- * The name of the event (\"attack-event\") -- * The handler for the event. messageState :: (FRPModel m) => B.ByteString -> MessageHandler e m () B.ByteString -> (B.ByteString,EventHandler e m () ()) messageState s actionA = (s,eventStateHeader (== s) >>> (proc () -> do m_string <- runMaybeArrow actionA -< Just () printTextOnce -< fmap ((,) Event) m_string t <- threadTime -< () let time_out = t > fromSeconds 3 printTextOnce -< if time_out then Just (UnexpectedEvent,"Hmmmm . . . RogueStar is puzzled. (" `B.append` s `B.append` ")") else Nothing blockContinue -< isNothing m_string && not time_out)) -- | As 'messageState', but just prints a simple string. messagePrompt :: (FRPModel m) => B.ByteString -> B.ByteString -> (B.ByteString,EventHandler e m () ()) messagePrompt s prompt = messageState s $ arr (const prompt) -- | As 'messageState', but constructs an alternate message handler to be switched -- via 'continueWith'. alternateMessage :: (FRPModel m) => B.ByteString -> MessageHandler e m () B.ByteString -> EventHandler e m () () alternateMessage s actionA = snd $ messageState s actionA -- | Provide a default value to substitute if a computation doesn't yield a value after the specified timeout period. timeout :: (FRPModel m) => Time -> b -> MessageHandler e m a b -> MessageHandler e m a b timeout duration default_value handler = (>>>) (extract handler) $ MaybeArrow $ frp1Context $ proc m_m_o -> do let m_o = fromMaybe Nothing m_m_o t <- threadTime -< () switchContinue -< (if isNothing m_o && t > duration then Just (arr $ const $ Just default_value) else Nothing,m_m_o) returnA -< m_o -- | As 'driverGetAnswerA' answer :: B.ByteString -> MessageHandler e m () B.ByteString answer s = liftConst s driverGetAnswerA -- | As 'driverGetTableA' that gets one element of the 'object-details' table. detail :: (FRPModel m) => B.ByteString -> MessageHandler e m B.ByteString B.ByteString detail field = proc unique_id -> MaybeArrow (arr $ maybe Nothing $ \x -> tableLookup x ("property","value") field) <<< MaybeArrow (arr (fromMaybe Nothing) <<< whenJust driverGetTableA) -< ("object-details",unique_id) -- | Switch to an alternate message handler constructed with 'alternateMessage'. continueWith :: (FRPModel m) => MessageHandler e m (EventHandler e m () ()) () continueWith = liftJust $ switchContinue <<< arr (\x -> (x,())) -- | Get a noun from a uid for any tool or character. nameOf :: (FRPModel m) => B.ByteString -> MessageHandler e m () Noun nameOf who = proc () -> do who_id <- answer who -< () who_player <- answer "who-player" -< () liftJust debugOnce <<< maybeA -< if who_player == "0" then Just "nameOf: I don't know who you are . . ." else Nothing returnA -< case () of () | who_id == who_player -> You () | otherwise -> Singular who_id "recreant" data Noun = X | You | Singular { _noun_id, _noun_word :: B.ByteString } deriving (Eq) nounToString :: Noun -> B.ByteString nounToString You = "you" nounToString (Singular _ s) = "the " `B.append` s nounToString X = "it" possessiveToString :: Noun -> B.ByteString possessiveToString You = "your" possessiveToString (Singular _ s) = B.concat ["the ",s,"'s"] possessiveToString X = "its" possessivePronounToString :: Noun -> B.ByteString possessivePronounToString You = "your" possessivePronounToString (Singular {}) = "its" possessivePronounToString X = "its" nounToUID :: MessageHandler e m Noun B.ByteString nounToUID = proc noun -> do you_id <- answer "who-player" -< () returnA -< case noun of X -> "0" You -> you_id Singular uid _ -> uid isPlural :: Noun -> Bool isPlural You = True isPlural (Singular {}) = False isPlural X = False sentence :: Noun -> Noun -> Noun -> B.ByteString -> B.ByteString sentence subject he1 he2 = appEndo $ mconcat $ map Endo $ he he2 ++ [replace "%" "$"] ++ he he1 ++ [replace "$you" $ nounToString subject, replace "$You" $ capitalize $ nounToString subject, replace "$your" $ possessiveToString subject, replace "$Your" $ capitalize $ possessiveToString subject, replace "$(your)" $ possessivePronounToString subject, replace "$(Your)" $ capitalize $ possessivePronounToString subject, replace "(s)" $ if isPlural subject then "" else "s", replace "(es)" $ if isPlural subject then "" else "es", replace "$have" $ if isPlural subject then "have" else "has", replace "$Have" $ if isPlural subject then "Have" else "has"] where he obj = [replace "$he" $ nounToString obj, replace "$He" $ capitalize $ nounToString obj, replace "$him" $ nounToString obj, replace "$Him" $ capitalize $ nounToString obj, replace "$his" $ possessiveToString obj, replace "$His" $ capitalize $ possessiveToString obj, replace "$(his)" $ possessivePronounToString obj, replace "$(His)" $ capitalize $ possessivePronounToString obj] recognized_events :: [B.ByteString] recognized_events = map fst (messages :: [(B.ByteString,EventHandler e () () ())]) messages :: (FRPModel m) => [(B.ByteString,EventHandler e m () ())] messages = [ messageState "attack-event" $ proc () -> do weapon_used <- answer "weapon-used" -< () continueWith -< if weapon_used == "0" then unarmedAttack else armedAttack guardA -< False returnA -< error "messageState: \"attack-event\" unreachable", messageState "miss-event" $ proc () -> do who_attacks <- nameOf "who-attacks" -< () returnA -< sentence who_attacks X X $ "$You miss(es).", messageState "killed-event" $ proc () -> do who_killed <- nameOf "who-killed" -< () returnA -< sentence who_killed X X "$You $have been killed.", messageState "weapon-overheats-event" $ proc () -> do who_surprised <- nameOf "who-attacks" -< () player_hp_string <- playerHPString -< who_surprised returnA -< (if who_surprised == You then "Ouch! " else "") `B.append` (sentence who_surprised X X $ "$Your weapon overheats!" `B.append` player_hp_string), messageState "weapon-explodes-event" $ proc () -> do who_surprised <- nameOf "who-attacks" -< () weapon_type <- detail "tool-type" <<< answer "weapon-used" -< () player_hp_string <- playerHPString -< who_surprised returnA -< sentence who_surprised X X $ (if who_surprised == You then "Ouch! Frak!\n" else "") `B.append` "$Your weapon explodes in $(your) hand!" `B.append` (if who_surprised == You && weapon_type == "gun" then "\nAre you sure you're qualified to operate a directed energy firearm?" else "") `B.append` (if who_surprised == You && weapon_type == "sword" then "\nDo you have ANY training with that thing?" else "") `B.append` player_hp_string, messageState "disarm-event" $ proc () -> do who_attacks <- nameOf "who-attacks" -< () who_hit <- nameOf "who-hit" -< () returnA -< sentence who_attacks who_hit X "$You disarm(s) $him!", messageState "sunder-event" $ proc () -> do who_attacks <- nameOf "who-attacks" -< () who_hit <- nameOf "who-hit" -< () returnA -< sentence who_attacks who_hit X "$You sunder(s) $his weapon!", messageState "heal-event" $ proc () -> do who_healed <- nameOf "who-event" -< () player_hp_string <- playerHPString -< who_healed returnA -< sentence who_healed X X "$You $have been healed!" `B.append` player_hp_string, messageState "expend-tool-event" $ proc () -> do returnA -< "That object has been used up.", messagePrompt "attack" "Attack. Direction:", messagePrompt "fire" "Fire. Direction:", messagePrompt "move" "Walk. Direction:", messagePrompt "jump" "Teleport jump. Direction:", messagePrompt "clear-terrain" "Clear terrain. Direction:", messagePrompt "turn" "Turn. Direction:"] unarmedAttack :: (FRPModel m) => EventHandler e m () () unarmedAttack = alternateMessage "attack-event" $ proc () -> do who_attacks <- nameOf "who-attacks" -< () who_hit <- nameOf "who-hit" -< () player_hp_string <- playerHPString -< who_hit returnA -< sentence who_attacks who_hit X $ "$You strike(s) $him!" `B.append` player_hp_string armedAttack :: (FRPModel m) => EventHandler e m () () armedAttack = alternateMessage "attack-event" $ proc () -> do weapon_used <- answer "weapon-used" -< () who_attacks <- nameOf "who-attacks" -< () who_hit <- nameOf "who-hit" -< () weapon_type <- detail "tool-type" -< weapon_used player_hp_string <- playerHPString -< who_hit returnA -< case weapon_type of "gun" -> sentence who_attacks who_hit X $ "$You shoot(s) $him!" `B.append` player_hp_string "sword" -> sentence who_attacks who_hit X $ "$You hit(s) $him!" `B.append` player_hp_string _ -> sentence who_attacks who_hit X $ "$You attack(s) $him!" `B.append` player_hp_string -- | Generates a string for the hit points of a creature, if that information is available. playerHPString :: (FRPModel m) => MessageHandler e m Noun B.ByteString playerHPString = timeout (fromSeconds 0.1) "" $ proc noun -> do uid <- nounToUID -< noun hp <- detail "hp" -< uid maxhp <- detail "maxhp" -< uid returnA -< " (" `B.append` hp `B.append` "/" `B.append` maxhp `B.append` ")"