packages feed

roguestar-gl-0.4.0.1: src/AnimationEvents.hs

{-# 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` ")"