antisplice 0.7.2.0 → 0.8.0.1
raw patch · 14 files changed
+521/−97 lines, 14 filesdep −comonad
Dependencies removed: comonad
Files
- antisplice.cabal +4/−3
- src/Game/Antisplice.hs +69/−0
- src/Game/Antisplice/Errors.hs +4/−1
- src/Game/Antisplice/Lang.hs +100/−52
- src/Game/Antisplice/Monad/Dungeon.hs +49/−5
- src/Game/Antisplice/Rooms.hs +57/−36
- src/Game/Antisplice/SingleUser.hs +65/−0
- src/Game/Antisplice/Terminal/Repl.hs +3/−0
- src/Game/Antisplice/Utils/AVL.hs +10/−0
- src/Game/Antisplice/Utils/BST.hs +13/−0
- src/Game/Antisplice/Utils/Focus.hs +61/−0
- src/Game/Antisplice/Utils/Graph.hs +4/−0
- src/Game/Antisplice/Utils/None.hs +78/−0
- src/Game/Antisplice/Utils/TST.hs +4/−0
antisplice.cabal view
@@ -10,7 +10,7 @@ -- PVP summary: +-+------- breaking API changes -- | | +----- non-breaking API additions -- | | | +--- code changes with no API change-version: 0.7.2.0+version: 0.8.0.1 -- A short (one-line) description of the package. synopsis: An engine for text-based dungeons.@@ -48,7 +48,7 @@ library -- Modules exported by the library.- exposed-modules: Game.Antisplice.Prototypes, Game.Antisplice.Templates, Game.Antisplice.Lang, Game.Antisplice.Monad, Game.Antisplice.Rooms, Game.Antisplice.Errors, Game.Antisplice.Utils.TST, Game.Antisplice.Utils.AVL, Game.Antisplice.Utils.Run, Game.Antisplice.Utils.BST, Game.Antisplice.Utils.Graph, Game.Antisplice.Utils.Counter, Game.Antisplice.Utils.Fail, Game.Antisplice.Monad.Dungeon, Game.Antisplice.Terminal.Repl, Game.Antisplice.Events, Game.Antisplice.Monad.Vocab, Game.Antisplice.Utils.Atoms, Game.Antisplice.Stats+ exposed-modules: Game.Antisplice.Prototypes, Game.Antisplice.Templates, Game.Antisplice.Lang, Game.Antisplice.Monad, Game.Antisplice.Rooms, Game.Antisplice.Errors, Game.Antisplice.Utils.TST, Game.Antisplice.Utils.AVL, Game.Antisplice.Utils.Run, Game.Antisplice.Utils.BST, Game.Antisplice.Utils.Graph, Game.Antisplice.Utils.Counter, Game.Antisplice.Utils.Fail, Game.Antisplice.Monad.Dungeon, Game.Antisplice.Terminal.Repl, Game.Antisplice.Events, Game.Antisplice.Monad.Vocab, Game.Antisplice.Utils.Atoms, Game.Antisplice.Stats, Game.Antisplice.Utils.None, Game.Antisplice.Utils.Focus, Game.Antisplice.SingleUser, Game.Antisplice -- Modules included in this library but not exported. -- other-modules: @@ -57,10 +57,11 @@ other-extensions: TemplateHaskell, QuasiQuotes, FlexibleInstances, MultiParamTypeClasses, TypeSynonymInstances, FlexibleContexts, UndecidableInstances, RankNTypes, TypeFamilies, FunctionalDependencies -- Other library packages from which modules are imported.- build-depends: base >=4.6 && <4.7, chatty >=0.2 && <0.3, text >=0.11 && <0.12, template-haskell >=2.8 && <2.9, mtl >=2.1 && <2.2, transformers >=0.3 && <0.4, haskeline >= 0.7 && < 0.8, time >= 1.4 && < 1.5, comonad >=4.0 && <4.1+ build-depends: base >=4.6 && <4.7, chatty >=0.2 && <0.3, text >=0.11 && <0.12, template-haskell >=2.8 && <2.9, mtl >=2.1 && <2.2, transformers >=0.3 && <0.4, haskeline >= 0.7 && < 0.8, time >= 1.4 && < 1.5 -- Directories containing source files. hs-source-dirs: src -- Base language which the package is written in. default-language: Haskell2010+
+ src/Game/Antisplice.hs view
@@ -0,0 +1,69 @@+{-# LANGUAGE RankNTypes, FlexibleContexts #-}++{-+ This module is part of Antisplice.+ Copyleft (c) 2014 Marvin Cohrs++ All wrongs reversed. Sharing is an act of love, not crime.+ Please share Antisplice with everyone you like.++ Antisplice is free software: you can redistribute it and/or modify+ it under the terms of the GNU Affero General Public License as published by+ the Free Software Foundation, either version 3 of the License, or+ (at your option) any later version.++ Antisplice is distributed in the hope that it will be useful,+ but WITHOUT ANY WARRANTY; without even the implied warranty of+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the+ GNU Affero General Public License for more details.++ You should have received a copy of the GNU Affero General Public License+ along with Antisplice. If not, see <http://www.gnu.org/licenses/>.+-}++-- | Reexports all relevant modules++module Game.Antisplice (+ -- * Reexported modules+ module Game.Antisplice.Monad,+ module Game.Antisplice.Monad.Dungeon,+ module Game.Antisplice.Monad.Vocab,+ module Game.Antisplice.Utils.Fail,+ module Game.Antisplice.Utils.Atoms,+ module Game.Antisplice.Utils.None,+ module Game.Antisplice.Utils.BST,+ module Game.Antisplice.Utils.Counter,+ module Game.Antisplice.Rooms,+ module Game.Antisplice.Stats,+ module Game.Antisplice.Prototypes,+ module Game.Antisplice.Lang,+ module Game.Antisplice.Errors,+ module Game.Antisplice.Terminal.Repl,+ module Game.Antisplice.Events,+ -- * Type abbreviations+ Constructor) where++import Text.Chatty.Printer+import Text.Chatty.Scanner+import Text.Chatty.Expansion+import Text.Chatty.Extended.Printer+import System.Chatty.Misc+import Game.Antisplice.Monad+import Game.Antisplice.Monad.Dungeon+import Game.Antisplice.Monad.Vocab+import Game.Antisplice.Utils.Fail+import Game.Antisplice.Utils.Atoms+import Game.Antisplice.Utils.None+import Game.Antisplice.Utils.BST+import Game.Antisplice.Utils.Counter+import Game.Antisplice.Rooms+import Game.Antisplice.Stats+import Game.Antisplice.Prototypes+import Game.Antisplice.Lang+import Game.Antisplice.Errors+import Game.Antisplice.Terminal.Repl+import Game.Antisplice.Events+import Control.Monad.Error.Class++-- | A dungeon constructor function+type Constructor = forall m.(Functor m,ExtendedPrinter m,MonadExpand m,ExpanderEnv m,MonadScanner m,MonadAtoms m,MonadClock m,MonadVocab m,MonadError SplErr m) => DungeonT m ()
src/Game/Antisplice/Errors.hs view
@@ -32,7 +32,10 @@ | VerbMustFirstError | CantSeeOneError | DontCarryOneError- | WhichOneError + | WhichOneError+ | CantEquipThatError+ | CantEquipThatThereError+ | WhereToEquipError | CantWalkThereError deriving Show -- | Alias for 'FailT' 'SplErr'
src/Game/Antisplice/Lang.hs view
@@ -27,6 +27,7 @@ import Text.Chatty.Printer import Text.Chatty.Scanner import Text.Chatty.Expansion+import Text.Chatty.Extended.Printer import Game.Antisplice.Errors import Game.Antisplice.Monad import Game.Antisplice.Monad.Dungeon@@ -34,6 +35,8 @@ import Game.Antisplice.Utils.Graph import Game.Antisplice.Utils.AVL import Game.Antisplice.Utils.TST+import Game.Antisplice.Utils.None+import Game.Antisplice.Utils.ListBuilder import Game.Antisplice.Rooms import Game.Antisplice.Stats import Control.Monad.Error@@ -41,59 +44,76 @@ import Data.Text (unpack) aliases :: [(String,String)]-aliases =- ("l","look"):- ("n","go north"):- ("ne","go northeast"):- ("e","go east"):- ("se","go southeast"):- ("s","go south"):- ("sw","go southwest"):- ("w","go west"):- ("nw","go northwest"):- ("u","ascend"):- ("d","descend"):- ("q","quit"):- ("i","list inventory"):- ("ex","list exits"):- ("get","acquire"):- ("take","acquire"):- ("show","list"):- ("sco","list score"):- []+aliases = strictBuild $ do+ lit "l" "look"+ lit "n" "go north"+ lit "ne" "go northeast"+ lit "e" "go east"+ lit "se" "go southeast"+ lit "s" "go south"+ lit "sw" "go southwest"+ lit "w" "go west"+ lit "nw" "go northwest"+ lit "u" "ascend"+ lit "d" "descend"+ lit "q" "quit"+ lit "i" "list inventory"+ lit "ex" "list exits"+ lit "get" "acquire"+ lit "take" "acquire"+ lit "show" "list"+ lit "sco" "list score"+ lit "eq" "list equipment" defVocab :: TST Token-defVocab = foldr (\(k,v) -> tstInsert k (v k)) EmptyTST (- ("look",Verb):- ("quit",Verb):- ("read",Verb):- ("acquire",Verb):- ("drop",Verb):- ("idiot",Noun):- ("first",Ordn 1):- ("next",Ordn 1):- ("primary",Ordn 1):- ("second",Ordn 2):- ("commit",Verb):- ("suicide",Noun):- ("go",Verb):- ("ascend",Verb):- ("descend",Verb):- ("north",Fixe):- ("south",Fixe):- ("east",Fixe):- ("west",Fixe):- ("northeast",Fixe):- ("northwest",Fixe):- ("southeast",Fixe):- ("southwest",Fixe):- ("at",Prep):- ("enter",Verb):- ("list",Verb):- ("exits",Fixe):- ("inventory",Fixe):- ("score",Fixe):- [])+defVocab = foldr (\(k,v) -> tstInsert k (v k)) EmptyTST $ strictBuild $ do+ lit "look" Verb+ lit "quit" Verb+ lit "read" Verb+ lit "acquire" Verb+ lit "drop" Verb+ lit "idiot" Noun+ lit "first" $ Ordn 1+ lit "next" $ Ordn 1+ lit "primary" $ Ordn 1+ lit "second" $ Ordn 2+ lit "third" $ Ordn 3+ lit "commit" Verb+ lit "suicide" Noun+ lit "go" Verb+ lit "ascend" Verb+ lit "descend" Verb+ lit "north" Fixe+ lit "south" Fixe+ lit "east" Fixe+ lit "west" Fixe+ lit "northeast" Fixe+ lit "northwest" Fixe+ lit "southeast" Fixe+ lit "southwest" Fixe+ lit "at" Prep+ lit "enter" Verb+ lit "list" Verb+ lit "exits" Fixe+ lit "inventory" Fixe+ lit "score" Fixe+ lit "main" Fixe+ lit "hand" Fixe+ lit "off" Fixe+ lit "chest" Fixe+ lit "feet" Fixe+ lit "wrists" Fixe+ lit "waist" Fixe+ lit "head" Fixe+ lit "legs" Fixe+ lit "back" Fixe+ lit "hands" Fixe+ lit "neck" Fixe+ lit "finger" Fixe+ lit "left" Fixe+ lit "right" Fixe+ lit "equipment" Fixe+ lit "equip" Verb replaceAliases :: [String] -> [String] replaceAliases [] = []@@ -144,8 +164,11 @@ return () act' (Verb "quit":[]) = throwError QuitError+act' (Verb "commit":Nounc _ _ "suicide":[]) = do+ eprintLn (Vivid Red) "You stab yourself in the chest, finally dying."+ throwError QuitError act' (Verb "look":[]) = roomTriggerOnLookOf =<< getRoomState-act' (Verb "look":Prep "at":n@Nounc{}:[]) = getAvailableObject n >>= objectTriggerOnLookAtOf+act' (Verb "look":Prep "at":n@Nounc{}:[]) = getAvailableObject n >>= objectTriggerOnLookAtOf act' (Verb "read":n@Nounc{}:[]) = getAvailableObject n >>= objectTriggerOnReadOf act' (Verb "enter":n@Nounc{}:[]) = getAvailableObject n >>= objectTriggerOnEnterOf act' (Verb "ascend":[]) = changeRoom Up@@ -178,6 +201,31 @@ mprintLn $ printf "Intelligence: %5i Spirit: %5i" int spi mprintLn $ printf "Stamina: %5i Armor: %5i" sta arm mprintLn $ printf "Attack power: %5i" akp+act' (Verb "equip":n@Nounc{}:[]) = do+ o <- getCarriedObject n >>= equipObject+ case o of+ Nothing -> noneM+ Just o -> modifyPlayerState $ \s -> s{playerInventoryOf=avlInsert o $ playerInventoryOf s}+act' (Verb "equip":n@Nounc{}:Prep "at":ps) = do+ k <- case ps of+ [Fixe "main",Fixe "hand"] -> return MainHand+ [Fixe "off",Fixe "hand"] -> return OffHand+ [Fixe "chest"] -> return Chest+ [Fixe "feet"] -> return Feet+ [Fixe "wrists"] -> return Wrists+ [Fixe "waist"] -> return Waist+ [Fixe "head"] -> return Head+ [Fixe "legs"] -> return Legs+ [Fixe "back"] -> return Back+ [Fixe "hands"] -> return Hands+ [Fixe "neck"] -> return Neck+ [Fixe "right",Fixe "finger"] -> return Finger1+ [Fixe "left",Fixe "finger"] -> return Finger2+ _ -> throwError UnintellegibleError+ o <- getCarriedObject n >>= equipObjectAt k+ case o of+ Nothing -> noneM+ Just o -> modifyPlayerState $ \s -> s{playerInventoryOf=avlInsert o $ playerInventoryOf s} act' (Verb "acquire":n@Nounc{}:[]) = getSeenObject n >>= (acquireObject . objectIdOf) act' (Verb "drop":n@Nounc{}:[]) = getCarriedObject n >>= (dropObject . objectIdOf) act' _ = throwError UnintellegibleError
src/Game/Antisplice/Monad/Dungeon.hs view
@@ -48,6 +48,7 @@ Faction (..), Attitude (..), -- * Players+ PlayerId (..), PlayerStereo (..), PlayerState (..), PlayerT (..),@@ -55,6 +56,7 @@ -- * Dungeons DungeonState (..), currentRoomOf,+ playerOf, DungeonT (..), MonadDungeon (..) ) where@@ -71,15 +73,17 @@ import Game.Antisplice.Errors import Game.Antisplice.Utils.Fail import Game.Antisplice.Utils.Counter+import Game.Antisplice.Utils.None+import Game.Antisplice.Utils.Focus import Game.Antisplice.Monad.Vocab import Control.Arrow import Control.Monad.Error import Control.Monad.Trans.Class-import Control.Comonad import Data.Text import Control.Monad import Data.Time.Clock import Data.Typeable+import Debug.Trace -- | Matches any 'MonadDungeon' context type DungeonM a = forall m.(MonadDungeon m) => m a@@ -102,6 +106,12 @@ -- | Key for equipment slot data EquipKey = MainHand | OffHand | Chest | Feet | Wrists | Waist | Head | Legs | Back | Hands | Neck | Finger1 | Finger2 deriving (Ord,Eq) +instance Indexable EquipKey EquipKey EquipKey where+ type ValueOf EquipKey = EquipKey+ type IndexOf EquipKey = EquipKey+ indexOf = id+ valueOf = id+ -- | State type for RoomT data RoomState = RoomState { roomTitleOf :: !Text,@@ -170,9 +180,18 @@ | Mobile -- ^ May move around. deriving (Ord,Eq) +instance Indexable Feature Feature Feature where+ type IndexOf Feature = Feature+ type ValueOf Feature = Feature+ indexOf = id+ valueOf = id+ -- | Phantom ID type for objects. data ObjectId = ObjectId Int | FalseObject deriving (Eq,Ord) +instance None ObjectId where+ none = FalseObject+ -- | State type for ObjectT data ObjectState = ObjectState { objectIdOf :: !ObjectId,@@ -188,7 +207,8 @@ objectCurHealthOf :: !Int, objectStatsOf :: AVL (StatKey,Int), objectRouteOf :: ![NodeId],- objectFeaturesOf :: AVL (Feature,()),+ objectFeaturesOf :: AVL Feature,+ objectWearableAtOf :: AVL EquipKey, objectNearImplicationsOf :: [Implication], objectCarryImplicationsOf :: [Implication], objectWearImplicationsOf :: [Implication],@@ -247,8 +267,12 @@ stereoCalcStatBonus :: (StatKey -> Int) -> StatKey -> Int } deriving Typeable +-- | Phantom ID type for players+newtype PlayerId = PlayerId Int deriving (Eq,Ord)+ -- | State type for PlayerT data PlayerState = PlayerState {+ playerIdOf :: !PlayerId, playerRoomOf :: !NodeId, playerMaxHealthOf :: !Int, playerCurHealthOf :: !Int,@@ -259,6 +283,12 @@ playerReputationOf :: AVL (Atom Faction,Int) } +instance Indexable PlayerState PlayerId PlayerState where+ type IndexOf PlayerState = PlayerId+ type ValueOf PlayerState = PlayerState+ indexOf = playerIdOf+ valueOf = id+ -- | The player monad. Used to create or modify players. newtype PlayerT m a = Player { runPlayerT :: PlayerState -> m (a,PlayerState) } @@ -278,6 +308,11 @@ getPlayerState :: m PlayerState -- | Put the player state. putPlayerState :: PlayerState -> m ()+ -- | Modify the player state.+ modifyPlayerState :: (PlayerState -> PlayerState) -> m ()+ modifyPlayerState f = do+ s <- getPlayerState+ putPlayerState $ f s instance Monad m => MonadPlayer (PlayerT m) where getPlayerState = Player $ \s -> return (s,s)@@ -289,13 +324,19 @@ -- | State type for DungeonT data DungeonState = DungeonState { roomsOf :: Graph RoomState Direction,- playerOf :: Maybe PlayerState,+ playersOf :: Focus PlayerState, timeTriggersOf :: AVL (NominalDiffTime,TriggerBox) } +instance None DungeonState where+ none = DungeonState none none none+ -- | For compatibility. Earlier versions of DungeonT had a field for that. currentRoomOf = fmap playerRoomOf . playerOf +-- | For compatibility. Earlier versions of DungeonT had a field for that.+playerOf = anyBstHead . playersOf+ -- | The dungeon monad. Everything runs inside this. newtype DungeonT m a = Dungeon { runDungeonT :: DungeonState -> m (a,DungeonState) } @@ -318,8 +359,11 @@ instance Monad m => MonadPlayer (DungeonT m) where getPlayerState = Dungeon $ \s -> case playerOf s of Just p -> return (p,s)- putPlayerState s' = Dungeon $ \s -> return ((),s{playerOf=Just s'})-+ putPlayerState s' = Dungeon $ \s -> return (none,s{playersOf=anyBstInsert s' $ playersOf s})+ modifyPlayerState f = Dungeon $ \s -> case playerOf s of+ Just p -> return ((),s{playersOf=anyBstInsert (f p) $ playersOf s})+ Nothing -> return ((),s)+ -- | Typeclass for all dungeon monads. class (MonadRoom m,MonadPlayer m) => MonadDungeon m where -- | Get the dungeon state.
src/Game/Antisplice/Rooms.hs view
@@ -26,7 +26,6 @@ -- * Room modification modifyRoomState, getRoomDesc,- --setRoomDesc, getRoomTitle, setRoomTitle, markRoom,@@ -71,10 +70,11 @@ schedule, -- * Players subscribePlayer,- modifyPlayerState, setPlayerRoom, acquireObject, dropObject,+ equipObject,+ equipObjectAt, -- * Masquerades withRoom, guardVisible@@ -88,8 +88,10 @@ import Game.Antisplice.Monad import Game.Antisplice.Utils.Graph import Game.Antisplice.Utils.Counter+import Game.Antisplice.Utils.BST import Game.Antisplice.Utils.AVL import Game.Antisplice.Utils.Atoms+import Game.Antisplice.Utils.None import Game.Antisplice.Errors import Control.Monad import Control.Monad.Error@@ -112,7 +114,7 @@ s <- getRoomState --return (fromText $ roomDescOf s) let descseg (DescSeg a) = getAtom a- descseg _ = return []+ descseg _ = return none is <- mapM descseg $ concatMap objectNearImplicationsOf $ avlInorder $ roomObjectsOf s return $ concat $ intersperse " " is @@ -143,7 +145,7 @@ rs0 <- getRoomState roomTriggerOnLeaveOf rs0 s <- getDungeonState- putDungeonState s{playerOf=fmap (\p -> p{playerRoomOf=n}) $ playerOf s}+ modifyPlayerState $ \p -> p{playerRoomOf=n} let marked = nodeMarked $ getNode' n $ roomsOf s rs <- getRoomState unless marked $ do@@ -174,11 +176,11 @@ -- | Construct a new room using the room monad. constructRoom :: (Monad m,MonadDungeon (t m),MonadTrans t) => RoomT m a -> t m NodeId constructRoom m = do- (_,rs) <- lift $ runRoomT m $ RoomState (pack "") EmptyAVL (return ()) (return ()) (return ()) (return ()) (return ())+ (_,rs) <- lift $ runRoomT m $ RoomState none none noneM noneM noneM noneM noneM s <- getDungeonState let (nid,g) = addNode' rs $ roomsOf s- s' = if isNothing $ currentRoomOf s then s{roomsOf=g,playerOf=fmap (\p -> p{playerRoomOf=nid}) $ playerOf s} else s{roomsOf=g}- putDungeonState s'+ putDungeonState s{roomsOf=g}+ when (isNothing $ currentRoomOf s) $ modifyPlayerState $ \p -> p{playerRoomOf=nid} return nid -- | Run a function in the context of the given room.@@ -218,31 +220,31 @@ constructObject :: Monad m => ObjectT m a -> m ObjectState constructObject m = do (_,o) <- runObjectT m $ ObjectState- FalseObject -- id+ none -- id (pack "Something") -- title (pack "I don't know what this is.") -- desc- [] [] False False False False -- names, attr, 1seen?, 1acq?, 1insp?, 1eq?+ none none False False False False -- names, attr, 1seen?, 1acq?, 1insp?, 1eq? 100 100 -- maxhp, curhp- EmptyAVL [] -- stats, route- EmptyAVL [] [] [] -- feat, impl (n,i,w)- Nothing -- faction- (return ()) -- on 1 sight- (return ()) -- on sight- (return ()) -- on 1 acq- (return ()) -- on acq- (return ()) -- on 1 insp- (return ()) -- on insp+ none none none -- stats, route, feat+ none none none none -- wear, impl (n,i,w)+ none -- faction+ noneM -- on 1 sight+ noneM -- on sight+ noneM -- on 1 acq+ noneM -- on acq+ noneM -- on 1 insp+ noneM -- on insp (mprintLn "There is nothing special about this.") -- on look at (mprintLn "There is nothing inside this.") -- on look in (mprintLn "There is nothing written on this.") -- on read (mprintLn "You cannot enter this.") -- on enter- (return ()) -- on enter room- (return ()) -- on leave room- (return ()) -- on announce- (return ()) -- on drop- (return ()) -- on 1 eq- (return ()) -- on eq- (return ()) -- on uneq+ noneM -- on enter room+ noneM -- on leave room+ noneM -- on announce+ noneM -- on drop+ noneM -- on 1 eq+ noneM -- on eq+ noneM -- on uneq return o -- | Remove an object from the current room and return its state.@@ -304,17 +306,12 @@ addObjectAttr t = modifyObjectState $ \s -> s{objectAttributesOf=t:objectAttributesOf s} -- | Create a new player using the player monad-subscribePlayer :: (MonadDungeon (t m),MonadTrans t,Monad m) => PlayerT m a -> t m ()+subscribePlayer :: (MonadDungeon (t m),MonadTrans t,MonadCounter (t m),Monad m) => PlayerT m a -> t m () subscribePlayer m = do s <- getDungeonState- (_,a) <- lift $ runPlayerT m $ PlayerState (rootNode $ roomsOf s) 100 100 EmptyAVL EmptyAVL EmptyAVL [] EmptyAVL- putDungeonState s{playerOf=Just a}---- | Modify the player state-modifyPlayerState :: MonadPlayer m => (PlayerState -> PlayerState) -> m ()-modifyPlayerState f = do- s <- getPlayerState- putPlayerState $ f s+ i <- countOn+ (_,a) <- lift $ runPlayerT m $ PlayerState (PlayerId i) (rootNode $ roomsOf s) 100 100 none none none none none+ putDungeonState s{playersOf=anyBstInsert a $ playersOf s} -- | Move the current player to the given room, but don't trigger anything. setPlayerRoom :: MonadPlayer m => NodeId -> m ()@@ -327,7 +324,7 @@ -- | Set whether the current object is a mob. setObjectIsMob :: MonadObject m => Bool -> m () setObjectIsMob b = modifyObjectState $ \o -> o{objectFeaturesOf=a b $ objectFeaturesOf o}- where a True = avlInsert (Mobile,())+ where a True = avlInsert Mobile a False = avlRemove Mobile -- | Check if the current object is acquirable.@@ -337,7 +334,7 @@ -- | Set whether the current object is acquirable. setObjectIsAcquirable :: MonadObject m => Bool -> m () setObjectIsAcquirable b = modifyObjectState $ \o -> o{objectFeaturesOf=a b $ objectFeaturesOf o}- where a True = avlInsert (Acquirable,())+ where a True = avlInsert Acquirable a False = avlRemove Acquirable -- | Schedule an event for a given time offset (in milliseconds).@@ -431,3 +428,27 @@ o <- getAtom a insertRoomObject o{objectIdOf=ObjectId i} return $ ObjectId i++-- | Equip the given object at a given key+equipObjectAt :: (MonadPlayer m,MonadError SplErr m) => EquipKey -> ObjectState -> m (Maybe ObjectState)+equipObjectAt k o+ | isJust (avlLookup k $ objectWearableAtOf o) = do+ p <- getPlayerState+ let o1 = avlLookup k $ playerEquipOf p+ putPlayerState p{playerEquipOf=avlInsert (k,o) $ playerEquipOf p}+ return o1+ | otherwise = throwError CantEquipThatThereError++-- | Equip the given object somewhere+equipObject :: (MonadPlayer m,MonadError SplErr m) => ObjectState -> m (Maybe ObjectState)+equipObject o =+ case avlInorder $ objectWearableAtOf o of+ [] -> throwError CantEquipThatError+ [k] -> equipObjectAt k o+ ks -> do+ p <- getPlayerState+ case filter (isJust.flip avlLookup (playerEquipOf p)) ks of+ [] -> throwError WhereToEquipError+ [k] -> equipObjectAt k o+ _ -> throwError WhereToEquipError+
+ src/Game/Antisplice/SingleUser.hs view
@@ -0,0 +1,65 @@+{-+ This module is part of Antisplice.+ Copyleft (c) 2014 Marvin Cohrs++ All wrongs reversed. Sharing is an act of love, not crime.+ Please share Antisplice with everyone you like.++ Antisplice is free software: you can redistribute it and/or modify+ it under the terms of the GNU Affero General Public License as published by+ the Free Software Foundation, either version 3 of the License, or+ (at your option) any later version.++ Antisplice is distributed in the hope that it will be useful,+ but WITHOUT ANY WARRANTY; without even the implied warranty of+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the+ GNU Affero General Public License for more details.++ You should have received a copy of the GNU Affero General Public License+ along with Antisplice. If not, see <http://www.gnu.org/licenses/>.+-}++module Game.Antisplice.SingleUser where++import Text.Chatty.Printer+import Text.Chatty.Finalizer+import Text.Chatty.Interactor+import Text.Chatty.Expansion+import Text.Chatty.Extended.Printer+import Text.Chatty.Extended.ANSI+import Game.Antisplice.Monad.Dungeon+import Game.Antisplice.Monad.Vocab+import Game.Antisplice.Utils.Fail+import Game.Antisplice.Utils.Counter+import Game.Antisplice.Utils.Atoms+import Game.Antisplice.Utils.None+import Game.Antisplice.Errors+import Game.Antisplice.Monad+import Game.Antisplice.Rooms+import Game.Antisplice.Lang+import Game.Antisplice.Terminal.Repl+import Text.Printf+import System.IO+import Control.Monad+import Control.Monad.IO.Class++singleUser :: DungeonT (FailT SplErr (VocabT (AtomStoreT (CounterT (AnsiPrinterT (ExpanderT (HandleCloserT IO))))))) () -> IO ()+singleUser init =+ void $+ withLazyIO $+ localEnvironment $+ flip runAnsiPrinterT (Dull White) $+ withCounter $+ flip runAtomStoreT none $+ flip runVocabT defVocab $+ do+ r <- runFailT $ flip runDungeonT none $ do+ init+ reenterCurrentRoom+ roomTriggerOnAnnounceOf =<< getRoomState+ roomTriggerOnLookOf =<< getRoomState+ repl+ case r :: Either SplErr ((),DungeonState) of+ Left QuitError -> mprintLn "Bye bye!"+ Left e -> mprintLn $ printf "Error occured: %s\n" $ show e+ Right x -> noneM
src/Game/Antisplice/Terminal/Repl.hs view
@@ -78,6 +78,9 @@ Left WhichOneError -> mprintLn "Which one do you mean?" Left CantSeeOneError -> mprintLn "I can't see one here." Left DontCarryOneError -> mprintLn "You don't carry one."+ Left CantEquipThatError -> mprintLn "I can't equip that."+ Left CantEquipThatThereError -> mprintLn "I can't wear that there. You might want to try some other place?"+ Left WhereToEquipError -> mprintLn "Where?" Left e -> throwError e throwError DoneError Just Nothing -> liftIO (killThread thr) >> mprintLn "quit" >> throwError QuitError
src/Game/Antisplice/Utils/AVL.hs view
@@ -25,6 +25,7 @@ module Game.Antisplice.Utils.AVL (avlMax,avlMin,avlLookup,avlHeight,avlSize,avlInsert,avlRemove,AVL (EmptyAVL,AVL),avlRoot,avlPreorder,avlPostorder,avlInorder) where import Game.Antisplice.Utils.BST+import Game.Antisplice.Utils.None instance Indexable i o v => AnyBST AVL i o v where anyBstMax = avlMax@@ -33,7 +34,11 @@ anyBstEmpty = EmptyAVL anyBstInsert = avlInsert anyBstRemove = avlRemove+ anyBstHead = avlHead +instance None (AVL a) where+ none = EmptyAVL+ -- | An AVL tree. data AVL a = EmptyAVL | AVL a Int Int !(AVL a) !(AVL a) @@ -142,6 +147,11 @@ avlRoot :: AVL i -> i avlRoot EmptyAVL = error "Trying to get the root of an empty AVL tree." avlRoot (AVL a _ _ _ _) = a++-- | Get the root of the tree (safely)+avlHead :: AVL i -> Maybe i+avlHead EmptyAVL = Nothing+avlHead t = Just $ avlRoot t -- | Traverse the tree, order (head, left, right) avlPreorder :: AVL i -> [i]
src/Game/Antisplice/Utils/BST.hs view
@@ -24,6 +24,8 @@ -- | Provides a typeclass for all binary search trees and an unbalanced implementation module Game.Antisplice.Utils.BST where +import Game.Antisplice.Utils.None+ -- | Only instances of Indexable may be saved in a BST class Ord o => Indexable i o v | i -> o, i -> v where type IndexOf i@@ -59,6 +61,8 @@ anyBstLookup :: o -> t i -> Maybe v -- | An empty tree anyBstEmpty :: t i+ -- | The root of the tree+ anyBstHead :: t i -> Maybe i instance Indexable i o v => AnyBST BST i o v where anyBstInsert = bstInsert@@ -67,7 +71,11 @@ anyBstMin = bstMin anyBstLookup = bstLookup anyBstEmpty = EmptyBST+ anyBstHead = bstHead +instance None (BST a) where+ none = EmptyBST+ -- | An unbalanced binary search tree data BST a = EmptyBST | BST a !(BST a) !(BST a) @@ -108,3 +116,8 @@ | o == indexOf a = Just $ valueOf a | o < indexOf a = bstLookup o l | o > indexOf a = bstLookup o r++-- | Return the tree's root+bstHead :: Indexable i o v => BST i -> Maybe i+bstHead EmptyBST = Nothing+bstHead (BST a _ _) = Just a
+ src/Game/Antisplice/Utils/Focus.hs view
@@ -0,0 +1,61 @@+{-# LANGUAGE MultiParamTypeClasses, FlexibleInstances, FunctionalDependencies, TypeFamilies #-}++{-+ This module is part of Antisplice.+ Copyleft (c) 2014 Marvin Cohrs++ All wrongs reversed. Sharing is an act of love, not crime.+ Please share Antisplice with everyone you like.++ Antisplice is free software: you can redistribute it and/or modify+ it under the terms of the GNU Affero General Public License as published by+ the Free Software Foundation, either version 3 of the License, or+ (at your option) any later version.++ Antisplice is distributed in the hope that it will be useful,+ but WITHOUT ANY WARRANTY; without even the implied warranty of+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the+ GNU Affero General Public License for more details.++ You should have received a copy of the GNU Affero General Public License+ along with Antisplice. If not, see <http://www.gnu.org/licenses/>.+-}++-- | Provides an unbalanced BST with a focus on the root.+module Game.Antisplice.Utils.Focus (Focus, focusSelect) where++import Game.Antisplice.Utils.BST+import Game.Antisplice.Utils.None++-- | An unbalanced BST with a focus on the root.+newtype Focus a = Focus { runFocus :: BST a }++instance None (Focus a) where+ none = Focus none++instance Indexable i o v => AnyBST Focus i o v where+ anyBstInsert i t = Focus $ focusInsert' i $ runFocus t+ anyBstRemove o t = Focus $ bstRemove o $ runFocus t+ anyBstMax t = bstMax $ runFocus t+ anyBstMin t = bstMin $ runFocus t+ anyBstLookup o t = bstLookup o $ runFocus t+ anyBstHead t = bstHead $ runFocus t+ anyBstEmpty = Focus none++focusSelect' :: Indexable i o v => o -> BST i -> Maybe (BST i)+focusSelect' _ EmptyBST = Nothing+focusSelect' o t@(BST a l r)+ | o == indexOf a = Just t+ | o < indexOf a = case focusSelect' o l of+ Just (BST a' l' r') -> Just $ BST a' l' (BST a r' r)+ Nothing -> Nothing+ | otherwise = case focusSelect' o r of+ Nothing -> Nothing+ Just (BST a' l' r') -> Just $ BST a' (BST a l l') r'++focusInsert' :: Indexable i o v => i -> BST i -> BST i+focusInsert' i = unjust . focusSelect' (indexOf i) . bstInsert i+ where unjust (Just j) = j++-- | Rotate the tree such that the given element becomes the root node.+focusSelect o t = fmap Focus $ focusSelect' o $ runFocus t
src/Game/Antisplice/Utils/Graph.hs view
@@ -26,6 +26,7 @@ import Game.Antisplice.Utils.BST import Game.Antisplice.Utils.AVL+import Game.Antisplice.Utils.None -- | Phantom type for a node ID newtype NodeId = NodeId Int deriving (Eq,Show,Ord)@@ -61,6 +62,9 @@ -- | An empty graph emptyGraph :: Graph a b emptyGraph = Graph EmptyAVL [] (NodeId 0)++instance None (Graph a b) where+ none = emptyGraph -- | Add a node to the graph addNode :: a -> Graph a b -> Graph a b
+ src/Game/Antisplice/Utils/None.hs view
@@ -0,0 +1,78 @@+{-# LANGUAGE FlexibleInstances #-}++{-+ This module is part of Antisplice.+ Copyleft (c) 2014 Marvin Cohrs++ All wrongs reversed. Sharing is an act of love, not crime.+ Please share Antisplice with everyone you like.++ Antisplice is free software: you can redistribute it and/or modify+ it under the terms of the GNU Affero General Public License as published by+ the Free Software Foundation, either version 3 of the License, or+ (at your option) any later version.++ Antisplice is distributed in the hope that it will be useful,+ but WITHOUT ANY WARRANTY; without even the implied warranty of+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the+ GNU Affero General Public License for more details.++ You should have received a copy of the GNU Affero General Public License+ along with Antisplice. If not, see <http://www.gnu.org/licenses/>.+-}++-- | Provides a typeclass for everything that may carry a void value+module Game.Antisplice.Utils.None where++import Data.Maybe+import Data.Text (pack, Text)++-- | Typeclass for everything that may carry a void value+class None n where+ none :: n++instance None [a] where+ none = []++instance None (Maybe a) where+ none = Nothing++instance None () where+ none = ()++instance None Text where+ none = pack ""++instance None (a -> a) where+ none = id++instance Monad m => None (a -> m a) where+ none = return++-- | Wrap the void into a monad.+noneM :: (Monad m,None n) => m n+noneM = return none++-- | Join a maybe into the underlying type. 'Nothing' becomes 'none'.+joinMaybe :: None n => Maybe n -> n+joinMaybe (Just j) = j+joinMaybe Nothing = none++-- | Wrap the value into a maybe. 'none' becomes 'Nothing'.+expandMaybe :: (Eq n,None n) => n -> Maybe n+expandMaybe n+ | n == none = Nothing+ | otherwise = Just n++-- | Clean the maybe by pulling wrapped 'none's to the outside (as a 'Nothing').+cleanMaybe :: (Eq n,None n) => Maybe n -> Maybe n+cleanMaybe = expandMaybe . joinMaybe++-- | Eliminate all void elements from the list.+reduce :: (Eq n,None n) => [n] -> [n]+reduce = filter (/=none)++-- | Eliminate all 'Nothing's from the list and unjust all remaining values.+reduceMaybe :: [Maybe a] -> [a]+reduceMaybe = map unjust . filter isJust+ where unjust (Just j) = j
src/Game/Antisplice/Utils/TST.hs view
@@ -23,6 +23,7 @@ module Game.Antisplice.Utils.TST where import Data.Maybe+import Game.Antisplice.Utils.None -- | A ternary search trie data TST a = EmptyTST | TST Char (Maybe a) (TST a) (TST a) (TST a)@@ -49,3 +50,6 @@ -- | Check if the TST contains the given key tstContains :: String -> TST a -> Bool tstContains cs = isJust . tstLookup cs++instance None (TST a) where+ none = EmptyTST