diff --git a/antisplice.cabal b/antisplice.cabal
--- a/antisplice.cabal
+++ b/antisplice.cabal
@@ -10,7 +10,7 @@
 -- PVP summary:      +-+------- breaking API changes
 --                   | | +----- non-breaking API additions
 --                   | | | +--- code changes with no API change
-version:             0.14.0.3
+version:             0.15.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, Game.Antisplice.Utils.None, Game.Antisplice.Utils.Focus, Game.Antisplice.SingleUser, Game.Antisplice, Game.Antisplice.Skills, Game.Antisplice.Stereos, Game.Antisplice.Paths, Game.Antisplice.Action, Game.Antisplice.Utils.ListBuilder
+  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, Game.Antisplice.Skills, Game.Antisplice.Stereos, Game.Antisplice.Paths, Game.Antisplice.Action, Game.Antisplice.Utils.ListBuilder, Game.Antisplice.Call
   
   -- Modules included in this library but not exported.
   -- other-modules:       
@@ -64,4 +64,6 @@
   
   -- Base language which the package is written in.
   default-language:    Haskell2010
+
+  ghc-options: -fprof-auto
 
diff --git a/src/Game/Antisplice.hs b/src/Game/Antisplice.hs
--- a/src/Game/Antisplice.hs
+++ b/src/Game/Antisplice.hs
@@ -26,6 +26,7 @@
 module Game.Antisplice (
   -- * Reexported modules
   module Game.Antisplice.Monad,
+  module Game.Antisplice.Call,
   module Game.Antisplice.Monad.Dungeon,
   module Game.Antisplice.Monad.Vocab,
   module Game.Antisplice.Utils.Fail,
@@ -72,6 +73,7 @@
 import Game.Antisplice.Events
 import Game.Antisplice.Action
 import Game.Antisplice.Paths
+import Game.Antisplice.Call
 import Control.Monad.Error.Class
 
 -- | A dungeon constructor function
diff --git a/src/Game/Antisplice/Action.hs b/src/Game/Antisplice/Action.hs
--- a/src/Game/Antisplice/Action.hs
+++ b/src/Game/Antisplice/Action.hs
@@ -31,6 +31,8 @@
 import Game.Antisplice.Stats
 import Game.Antisplice.Utils.Counter
 import Game.Antisplice.Utils.None
+import Game.Antisplice.Utils.AVL
+import Game.Antisplice.Utils.BST
 
 -- | A typeclass for all action types carrying an execution condition.
 class IsAction a where
@@ -126,6 +128,19 @@
       c1 <- getCurrency c
       return (c1 > h))
   (modifyCurrency c (subtract h))
+
+-- | Use for actions that consume an object of the given kind.
+consumeKindA :: KindId -> Int -> Action
+consumeKindA k h =
+  let getObjs :: ChattyDungeonM [ObjectId]
+      getObjs = liftM (map indexOf . filter ((==k) . objectKindOf) . avlInorder . playerInventoryOf) getPlayerState
+  in Action
+    (do
+        objs <- getObjs
+        return (length objs >= h))
+    (do
+        objs <- getObjs
+        modifyPlayerState $ \p -> p{playerInventoryOf=foldr avlRemove (playerInventoryOf p) $ take h objs})
 
 -- | Deal damage to opponent
 dealDamageA :: ChattyDungeonM Int -> Action
diff --git a/src/Game/Antisplice/Call.hs b/src/Game/Antisplice/Call.hs
new file mode 100644
--- /dev/null
+++ b/src/Game/Antisplice/Call.hs
@@ -0,0 +1,288 @@
+{-# LANGUAGE MultiParamTypeClasses, FunctionalDependencies, FlexibleInstances, UndecidableInstances, 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/>.
+-}
+
+-- | Provides a powerful language for user input evaluation
+module Game.Antisplice.Call (
+  -- * Heterogenous list
+  Cons (..),
+  Nil (..),
+  Append (..),
+  Tuplify (..),
+  -- * Using call masks
+  processMask,
+  tryMask,
+  -- * Call mask segments
+  CallMask (..),
+  EnsureLineEnd (..),
+  CatchByType (..),
+  CatchToken (..),
+  CatchOrd (..),
+  Remaining (..),
+  CatchObj (..),
+  ) where
+
+import Control.Monad
+import Control.Monad.State
+import Control.Monad.Identity
+import Control.Monad.Error.Class
+import Data.Maybe
+import Game.Antisplice.Errors
+import Game.Antisplice.Monad.Dungeon
+import Game.Antisplice.Monad.Vocab
+import Game.Antisplice.Utils.AVL
+
+infixr 8 :-:
+-- | The Cons type for a heterogenous list
+data Cons a b = (:-:) a b
+-- | The empty list
+data Nil = Nil
+
+-- | Typeclass for appending one heterogenous list to another one
+class Append a b ab | a b -> ab where
+  tappend :: a -> b -> ab
+instance Append Nil b b where
+  tappend Nil b = b
+instance Append b c bc => Append (Cons a b) c (Cons a bc) where
+  tappend (a :-: b) c = a :-: tappend b c
+
+-- | Typeclass for use input masks (either single modules or lists of modules)
+class CallMask cm l | cm -> l where
+  usemask :: (MonadRoom m,MonadPlayer m) => cm -> StateT [(String,Token)] m (Maybe l)
+
+-- | Ensures that the end of the input is reached
+data EnsureLineEnd = EnsureLineEnd
+-- | Catches the string of a token matching the given token type
+data CatchByType = CatchVerb | CatchPrep | CatchNoun | CatchAdj | CatchOrdn Int | CatchFixe | CatchSkilln | CatchUnint | CatchAny
+-- | Catches an entire token
+data CatchToken = CatchToken | CatchNounc
+-- | Catches the number of an Ordn token
+data CatchOrd = CatchOrd
+-- | Catches the remaining part of the line
+data Remaining = Remaining
+-- | Catches an available, carried or seen object
+data CatchObj = AvailableObject | SeenObject | CarriedObject
+
+instance CallMask Token Nil where
+  usemask t = do
+    ss <- get
+    if null ss then return Nothing
+               else let (s:_) = ss in do
+      modify tail
+      return $ if snd s == t then Just Nil else Nothing
+
+instance CallMask String Nil where
+  usemask t = do
+    ss <- get
+    if null ss then return Nothing
+               else let (s:_) =ss in do
+      modify tail
+      return $ if fst s == t then Just Nil else Nothing
+      
+instance CallMask EnsureLineEnd Nil where
+  usemask EnsureLineEnd = do
+    s <- get
+    return $ if null s then Just Nil else Nothing
+    
+instance CallMask CatchByType (Cons String Nil) where
+  usemask ct = do
+    ss <- get
+    if null ss then return Nothing
+               else let (s:_) = ss in do     
+      modify tail
+      return $ case (ct,snd s) of
+        (CatchVerb, Verb t) -> Just (t :-: Nil)
+        (CatchAny, Verb t) -> Just (t :-: Nil)
+        (CatchPrep, Prep t) -> Just (t :-: Nil)
+        (CatchAny, Prep t) -> Just (t :-: Nil)
+        (CatchNoun, Noun t) -> Just (t :-: Nil)
+        (CatchAny, Noun t) -> Just (t :-: Nil)
+        (CatchAdj, Adj t) -> Just (t :-: Nil)
+        (CatchAny, Adj t) -> Just (t :-: Nil)
+        (CatchOrdn i, Ordn j t) -> if i == j then Just (t :-: Nil) else Nothing
+        (CatchAny, Ordn _ t) -> Just (t :-: Nil)
+        (CatchFixe, Fixe t) -> Just (t :-: Nil)
+        (CatchAny, Fixe t) -> Just (t :-: Nil)
+        (CatchSkilln, Skilln t) -> Just (t :-: Nil)
+        (CatchAny, Skilln t) -> Just (t :-: Nil)
+        (CatchUnint, Unintellegible t) -> Just (t :-: Nil)
+        (CatchAny, Unintellegible t) -> Just (t :-: Nil)
+        _ -> Nothing
+        
+instance CallMask CatchOrd (Cons Int Nil) where
+  usemask CatchOrd = do
+    ss <- get
+    if null ss then return Nothing
+               else let (s:_) = ss in do     
+      modify tail
+      case snd s of
+        Ordn i _ -> return $ Just (i :-: Nil)
+        _ -> return Nothing
+
+instance CallMask CatchToken (Cons Token Nil) where
+  usemask CatchToken = do
+    ss <- get
+    if null ss then return Nothing
+               else modify tail >> return (Just (snd (head ss) :-: Nil))
+  usemask CatchNounc = do
+    ss <- get
+    case mergeNoun ss of
+      Nothing -> return Nothing
+      Just (t,xs) -> do put xs; return $ Just (t :-: Nil)
+
+instance CallMask CatchObj (Cons ObjectState Nil) where
+  usemask k = do
+    ss <- get
+    as <- lift $ case k of
+      AvailableObject -> availableObjects
+      SeenObject -> seenObjects
+      CarriedObject -> carriedObjects
+    case mergeNoun ss of
+      Nothing -> return Nothing
+      Just (t,xs) -> do
+        put xs
+        case getObject' as t of
+          Found x -> return $ Just (x :-: Nil)
+          _ -> return Nothing
+
+instance CallMask Remaining (Cons [String] Nil) where
+  usemask Remaining = do
+    ss <- get
+    put []
+    return $ Just (map fst ss :-: Nil)
+
+instance CallMask Nil Nil where
+  usemask Nil = return $ Just Nil
+  
+instance (CallMask x r,CallMask xs rs,Append r rs rx) => CallMask (Cons x xs) rx where
+  usemask (x :-: xs) = do
+    l1 <- usemask x
+    case l1 of
+      Nothing -> return Nothing
+      Just l1 -> do
+        l2 <- usemask xs
+        case l2 of
+          Nothing -> return Nothing
+          Just l2 -> return $ Just $ tappend l1 l2
+
+-- | Typeclass for everything that may be converted to a tuple
+class Tuplify l t | l -> t where
+  tuplify :: l -> t
+
+instance Tuplify Nil () where
+  tuplify Nil = ()
+
+instance Tuplify (Cons a Nil) a where
+  tuplify (a :-: Nil) = a
+
+instance Tuplify (Cons a (Cons b Nil)) (a,b) where
+  tuplify (a :-: b :-: Nil) = (a,b)
+
+instance Tuplify (Cons a (Cons b (Cons c Nil))) (a,b,c) where
+  tuplify (a :-: b :-: c :-: Nil) = (a,b,c)
+
+instance Tuplify (Cons a (Cons b (Cons c (Cons d Nil)))) (a,b,c,d) where
+  tuplify (a :-: b :-: c :-: d :-: Nil) = (a,b,c,d)
+
+instance Tuplify (Cons a (Cons b (Cons c (Cons d (Cons e Nil))))) (a,b,c,d,e) where
+  tuplify (a :-: b :-: c :-: d :-: e :-: Nil) = (a,b,c,d,e)
+
+instance Tuplify (Cons a (Cons b (Cons c (Cons d (Cons e (Cons f Nil)))))) (a,b,c,d,e,f) where
+  tuplify (a :-: b :-: c :-: d :-: e :-: f :-: Nil) = (a,b,c,d,e,f)
+
+instance Tuplify ObjectState ObjectState where
+  tuplify = id
+
+instance Tuplify Int Int where
+  tuplify = id
+
+instance Tuplify String String where
+  tuplify = id
+
+-- | Use a mask on a list of tokens
+processMask' :: CallMask m r => m -> [(String,Token)] -> DungeonM (Maybe r)
+processMask' m s = evalStateT (usemask m) s
+
+-- | Use a mask on a list of tokens and tuplify the result. Dispatch errors to the underlying monad.
+processMask :: (CallMask m r, Append r Nil r, Tuplify r t) => m -> [String] -> DungeonM t
+processMask m s = do
+  ss <- mapM lookupVocab s
+  mr <- processMask' (m :-: EnsureLineEnd :-: Nil) $ zip s ss
+  case mr of
+    Nothing -> throwError UnintellegibleError
+    Just r -> return $ tuplify r
+
+-- | Try to use a mask on a list of tokens. Only return whether it succeeded.
+tryMask :: (Append r Nil r, CallMask m r) => m -> [String] -> DungeonM Bool
+tryMask m s = do
+  ss <- mapM lookupVocab s
+  mr <- processMask' (m :-: EnsureLineEnd :-: Nil) $ zip s ss
+  return $ isJust mr
+
+-- | Token is adjective?
+isAdj (Adj _) = True
+isAdj _ = False
+
+-- | Token is noun?
+isNoun (Noun _) = True
+isNoun _ = False
+
+-- | Merge nouns and adjectives to a complex noun
+mergeNoun :: [(String,Token)] -> Maybe (Token,[(String,Token)])
+mergeNoun [] = Nothing
+mergeNoun ts@((_,Noun s):_) =
+  let as = takeWhile (\(_,t) -> isAdj t || isNoun t) ts
+  in Just (Nounc (map fst $ init as) Nothing (fst $ last as), drop (length as) ts)
+mergeNoun ts@((_,Adj s):_) =
+  let as = takeWhile (\(_,t) -> isAdj t || isNoun t) ts
+  in Just (Nounc (map fst $ init as) Nothing (fst $ last as), drop (length as) ts)
+mergeNoun (o@(_,Ordn i _):ts) =
+  let as = takeWhile (\(_,t) -> isAdj t || isNoun t) ts
+  in case as of
+    [] -> Nothing
+    _ ->  Just (Nounc (map fst $ init as) (Just i) (fst $ last as), drop (length as) ts)
+mergeNoun _ = Nothing
+
+getObject' :: [ObjectState] -> Token -> GetterResponse
+getObject' os (Nounc as i n) =
+  let ns1 = filter (elem n . objectNamesOf) os
+      ns2 = foldr (\a ns -> filter (elem a . objectAttributesOf) ns) ns1 as
+  in case ns2 of
+    [] -> NoneFound
+    xs -> case i of
+      Nothing -> case xs of
+        [x] -> Found x
+        _ -> TooMany
+      Just idx -> if idx > length xs then NoneFound
+                                     else Found (xs !! (idx-1))
+                                          
+availableObjects :: (MonadRoom m,MonadPlayer m) => m [ObjectState]
+availableObjects = do
+  rs <- getRoomState
+  ps <- getPlayerState
+  return (avlInorder (roomObjectsOf rs) ++ avlInorder (playerInventoryOf ps))
+    
+carriedObjects :: MonadPlayer m => m [ObjectState]
+carriedObjects = liftM (avlInorder.playerInventoryOf) getPlayerState
+
+seenObjects :: MonadRoom m => m [ObjectState]
+seenObjects = liftM (avlInorder.roomObjectsOf) getRoomState
diff --git a/src/Game/Antisplice/Errors.hs b/src/Game/Antisplice/Errors.hs
--- a/src/Game/Antisplice/Errors.hs
+++ b/src/Game/Antisplice/Errors.hs
@@ -40,6 +40,7 @@
             | CantWalkThereError         -- ^ "I can't walk there."
             | CantAcquireThatError       -- ^ "I can't take that."
             | WontHitThatError           -- ^ "I won't hit that."
+            | WrongMethodError           -- ^ "Wrong method for creating that."
             deriving Show
 
 -- | Alias for 'FailT' 'SplErr'
diff --git a/src/Game/Antisplice/Lang.hs b/src/Game/Antisplice/Lang.hs
--- a/src/Game/Antisplice/Lang.hs
+++ b/src/Game/Antisplice/Lang.hs
@@ -39,6 +39,9 @@
 import Game.Antisplice.Utils.ListBuilder
 import Game.Antisplice.Rooms
 import Game.Antisplice.Stats
+import Game.Antisplice.Skills
+import Game.Antisplice.Call
+import Game.Antisplice.Action
 import Control.Arrow
 import Control.Monad.Error
 import Text.Printf
@@ -94,6 +97,8 @@
   lit "southwest" Fixe
   lit "at" Prep
   lit "on" Prep
+  lit "for" Prep
+  lit "of" Prep
   lit "enter" Verb
   lit "list" Verb
   lit "exits" Fixe
@@ -124,195 +129,103 @@
           [] -> x
           (a:_) -> snd a
 
-isIntellegible :: Token -> Bool
-isIntellegible (Unintellegible _) = False
-isIntellegible _ = True
-
-isNoun :: Token -> Bool
-isNoun (Noun _) = True
-isNoun _ = False
-
-isAdj :: Token -> Bool
-isAdj (Adj _) = True
-isAdj _ = False
-
-mergeNouns :: [Token] -> [Token]
-mergeNouns [] = []
-mergeNouns (Noun s:ts) = Nounc [] Nothing s : mergeNouns ts
-mergeNouns ts@(Adj _:_) =
-  let as = takeWhile isAdj ts
-      ns = dropWhile isAdj ts
-  in case ns of
-    (n:nx) -> Nounc (map (\(Adj a) -> a) as) Nothing ((\(Noun n) -> n) n) : mergeNouns nx
-    _ -> as
-mergeNouns (o@(Ordn i _):ts) =
-  let as = takeWhile isAdj ts
-      ns = dropWhile isAdj ts
-  in case ns of
-    (n:nx) -> Nounc (map (\(Adj a) -> a) as) (Just i) ((\(Noun n) -> n) n) : mergeNouns nx
-    _ -> o:as
-mergeNouns (t:ts) = t : mergeNouns ts
-
 -- | Run a given input line.
 act :: String -> ChattyDungeonM ()
 act s = do
-  ts <- mapM lookupVocab $ replaceAliases $ words $ map toLower s
-  let ss = mergeNouns ts
-  unless (all isIntellegible ss) $ throwError UnintellegibleError
-  case ss of
-    [] -> return ()
-    (Verb v:_) -> act' ss
-    (Skilln n:ps) -> do
-      ste <- totalStereo
-      case stereoSkillBonus ste n of
-        Nothing -> throwError CantCastThatNowError
-        Just t -> runHandler . t =<< do
-          gao <- liftM getObject' availableObjects
-          gco <- liftM getObject' carriedObjects
-          gso <- liftM getObject' seenObjects
-          let getters = Getters gao gco gso
-              tmplt = none{paramGettersOf=getters}
-          case ps of
-            [] -> return tmplt
-            [n@Nounc{}] -> return tmplt{paramDirectOf=Just n}
-            [Prep "at",n@Nounc{}] -> return tmplt{paramAtOf=Just n}
-            [n1@Nounc{},Prep "at",n2@Nounc{}] -> return tmplt{paramDirectOf=Just n1,paramAtOf=Just n2}
-            [Prep "on",n@Nounc{}] -> return tmplt{paramOnOf=Just n}
-            [n1@Nounc{},Prep "on",n2@Nounc{}] -> return tmplt{paramDirectOf=Just n1,paramOnOf=Just n2}
-            _ -> throwError UnintellegibleError
-    _ -> throwError VerbMustFirstError
+  let sp = replaceAliases $ words $ map toLower s
+  ts <- mapM lookupVocab sp
+  unless (null sp) $ do
+    v1 <- lookupVocab (head sp)
+    case v1 of
+      Verb v -> runHandler $ runConsumer react sp
+      Skilln n -> do
+        ste <- totalStereo
+        case stereoSkillBonus ste n of
+          Nothing -> throwError CantCastThatNowError
+          Just t -> runHandler $ t $ tail sp
+      _ -> throwError VerbMustFirstError
   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 "enter":n@Nounc{}:[]) = getAvailableObject_ n >>= objectTriggerOnEnterOf
-act' (Verb "ascend":[]) = changeRoom Up
-act' (Verb "descend":[]) = changeRoom Down
-act' (Verb "go":Fixe d:[]) =
-  changeRoom $ case d of
-    "north" -> North
-    "south" -> South
-    "east" -> East
-    "west" -> West
-    "northeast" -> NorthEast
-    "northwest" -> NorthWest
-    "southeast" -> SouthEast
-    "southwest" -> SouthWest
-    "up" -> Up
-    "down" -> Down
-act' (Verb "list":Fixe "exits":[]) = do
-  s <- getDungeonState
-  let (Just r) = currentRoomOf s
-  forM_ (listEdges r $ roomsOf s) $ \(l,c,n) -> do
-    b <- pathPrerequisiteOf c
-    mprint "  " >> mprint (show l) >> mprint " -> "
-    mprint =<< withRoom n getRoomTitle
-    if not b then mprintLn " (locked)"
-             else mprintLn ""
-act' (Verb "list":Fixe "inventory":[]) = do
-  ps <- getPlayerState
-  mprintLn "Your inventory:"
-  forM_ (avlInorder $ playerInventoryOf ps) $ \os -> mprintLn $ printf "  %s" $ unpack $ objectTitleOf os
-act' (Verb "list":Fixe "score":[]) = do
-  [str, agi, sta, int, spi, arm, akp] <- mapM calcStat [Strength, Agility, Stamina, Intelligence, Spirit, Armor, AttackPower]
-  [hst, gcd] <- mapM calcStat [Haste, CooldownDuration]
-  mprintLn $ printf "Strength:     %5i   Agility: %5i" str agi
-  mprintLn $ printf "Intelligence: %5i   Spirit:  %5i" int spi
-  mprintLn $ printf "Stamina:      %5i   Armor:   %5i" sta arm
-  mprintLn $ printf "Attack power: %5i   Haste:   %5i" akp hst
-  mprintLn $ printf "Global cooldown:  %5i" gcd
-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 "list":Fixe "equipment":[]) = 
-  let pr (Just k) s = mprintLn $ printf "%-15s%s" s $ unpack $ objectTitleOf k
-      pr Nothing _ = noneM
-      firstM = runKleisli . first . Kleisli
-  in mapM_ (firstM getEquipment >=> uncurry pr) $ lazyBuild $ do
-    lit MainHand "Main hand:"
-    lit OffHand "Off hand:"
-    lit Chest "Chest:"
-    lit Feet "Feet:"
-    lit Wrists "Wrists:"
-    lit Waist "Waist:"
-    lit Head "Head:"
-    lit Legs "Legs:"
-    lit Back "Back:"
-    lit Hands "Hands:"
-    lit Neck "Neck:"
-    lit Finger1 "Right finger:"
-    lit Finger2 "Left finger:"
-act' (Verb "acquire":n@Nounc{}:[]) = getSeenObject_ n >>= (acquireObject . objectIdOf)
-act' (Verb "drop":n@Nounc{}:[]) = getCarriedObject_ n >>= (dropObject . objectIdOf)
-act' _ = throwError UnintellegibleError
-
-getObject :: (MonadError SplErr m) => Token -> [ObjectState] -> SplErr -> m ObjectState
-getObject n os err =
-  case getObject' os n of
-    NoneFound -> throwError err
-    TooMany -> throwError WhichOneError
-    Found x -> return x
-
-getObject' :: [ObjectState] -> Token -> GetterResponse
-getObject' os (Nounc as i n) =
-  let ns1 = filter (elem n . objectNamesOf) os
-      ns2 = foldr (\a ns -> filter (elem a . objectAttributesOf) ns) ns1 as
-  in case ns2 of
-    [] -> NoneFound
-    xs -> case i of
-      Nothing -> case xs of
-        [x] -> Found x
-        _ -> TooMany
-      Just idx -> if idx > length xs then NoneFound
-                                     else Found (xs !! (idx-1))
-
-availableObjects :: (MonadRoom m,MonadPlayer m) => m [ObjectState]
-availableObjects = do
-  rs <- getRoomState
-  ps <- getPlayerState
-  return (avlInorder (roomObjectsOf rs) ++ avlInorder (playerInventoryOf ps))
-    
-getAvailableObject_ :: (MonadRoom m, MonadError SplErr m, MonadPlayer m) => Token -> m ObjectState
-getAvailableObject_ n = do
-  os <- availableObjects
-  getObject n os CantSeeOneError
-
-carriedObjects :: MonadPlayer m => m [ObjectState]
-carriedObjects = liftM (avlInorder.playerInventoryOf) getPlayerState
-
-getCarriedObject_ :: (MonadPlayer m, MonadError SplErr m) => Token -> m ObjectState
-getCarriedObject_ n = do
-  os <- carriedObjects
-  getObject n os DontCarryOneError
-
-seenObjects :: MonadRoom m => m [ObjectState]
-seenObjects = liftM (avlInorder.roomObjectsOf) getRoomState
-
-getSeenObject_ :: (MonadRoom m, MonadError SplErr m) => Token -> m ObjectState
-getSeenObject_ n = do
-  os <- seenObjects
-  getObject n os CantSeeOneError
+react :: Consumer
+react = Nil #->> noneM
+  #|| Verb "quit" #->> throwError QuitError
+  #|| Verb "commit" :-: Noun "suicide" :-: Nil #->> (do
+    eprintLn (Vivid Red) "You stab yourself in the chest, finally dying."
+    throwError QuitError)
+  #|| Verb "enter" :-: AvailableObject :-: Nil #-> objectTriggerOnEnterOf
+  #|| Verb "ascend" #->> changeRoom Up
+  #|| Verb "descend" #->> changeRoom Down
+  #|| Verb "go" :-: CatchFixe :-: Nil #-> (\d -> changeRoom $ case d of
+                                       "north" -> North
+                                       "south" -> South
+                                       "east" -> East
+                                       "west" -> West
+                                       "northeast" -> NorthEast
+                                       "northwest" -> NorthWest
+                                       "southeast" -> SouthEast
+                                       "southwest" -> SouthWest
+                                       "up" -> Up
+                                       "down" -> Down)
+  #|| Verb "list" :-: Fixe "exits" :-: Nil #->> (do
+                                           s <- getDungeonState
+                                           let (Just r) = currentRoomOf s
+                                           forM_ (listEdges r $ roomsOf s) $ \(l,c,n) -> do
+                                             b <- pathPrerequisiteOf c
+                                             mprint "  " >> mprint (show l) >> mprint " -> "
+                                             mprint =<< withRoom n getRoomTitle
+                                             if not b then mprintLn " (locked)"
+                                               else mprintLn "")
+  #|| Verb "list" :-: Fixe "inventory" :-: Nil #->> (do
+                                               ps <- getPlayerState
+                                               mprintLn "Your inventory:"
+                                               forM_ (avlInorder $ playerInventoryOf ps) $ \os -> mprintLn $ printf "  %s" $ unpack $ objectTitleOf os)
+  #|| Verb "list" :-: Fixe "score" :-: Nil #->> (do
+                                            [str, agi, sta, int, spi, arm, akp] <- mapM calcStat [Strength, Agility, Stamina, Intelligence, Spirit, Armor, AttackPower]
+                                            [hst, gcd] <- mapM calcStat [Haste, CooldownDuration]
+                                            mprintLn $ printf "Strength:     %5i   Agility: %5i" str agi
+                                            mprintLn $ printf "Intelligence: %5i   Spirit:  %5i" int spi
+                                            mprintLn $ printf "Stamina:      %5i   Armor:   %5i" sta arm
+                                            mprintLn $ printf "Attack power: %5i   Haste:   %5i" akp hst
+                                            mprintLn $ printf "Global cooldown:  %5i" gcd)
+  #|| Verb "equip" :-: CarriedObject :-: Nil #-> (equipObject >=> \o -> case o of
+                                             Nothing -> noneM
+                                             Just o -> modifyPlayerState $ \s -> s{playerInventoryOf=avlInsert o $ playerInventoryOf s})
+  #|| Verb "equip" :-: CarriedObject :-: Prep "at" :-: Remaining :-: Nil #-> (\(o,ps) -> do
+                                                                                 k <- case ps of
+                                                                                   ["main","hand"] -> return MainHand
+                                                                                   ["off","hand"] -> return OffHand
+                                                                                   ["chest"] -> return Chest
+                                                                                   ["feet"] -> return Feet
+                                                                                   ["wrists"] -> return Wrists
+                                                                                   ["waist"] -> return Waist
+                                                                                   ["head"] -> return Head
+                                                                                   ["legs"] -> return Legs
+                                                                                   ["back"] -> return Back
+                                                                                   ["hands"] -> return Hands
+                                                                                   ["neck"] -> return Neck
+                                                                                   ["right","finger"] -> return Finger1
+                                                                                   ["left","finger"] -> return Finger2
+                                                                                   _ -> throwError UnintellegibleError
+                                                                                 equipObjectAt k o >>= \o -> case o of
+                                                                                   Nothing -> noneM
+                                                                                   Just o -> modifyPlayerState $ \s -> s{playerInventoryOf=avlInsert o $ playerInventoryOf s})
+  #|| Verb "list" :-: Fixe "equipment" :-: Nil #->> (
+    let pr (Just k) s = mprintLn $ printf "%-15s%s" s $ unpack $ objectTitleOf k
+        pr Nothing _ = noneM
+        firstM = runKleisli . first . Kleisli
+    in mapM_ (firstM getEquipment >=> uncurry pr) $ lazyBuild $ do
+      lit MainHand "Main hand:"
+      lit OffHand "Off hand:"
+      lit Chest "Chest:"
+      lit Feet "Feet:"
+      lit Wrists "Wrists:"
+      lit Waist "Waist:"
+      lit Head "Head:"
+      lit Legs "Legs:"
+      lit Back "Back:"
+      lit Hands "Hands:"
+      lit Neck "Neck:"
+      lit Finger1 "Right finger:"
+      lit Finger2 "Left finger:")
+  #|| Verb "acquire" :-: SeenObject :-: Nil #-> acquireObject . objectIdOf
+  #|| Verb "drop" :-: CarriedObject :-: Nil #-> dropObject . objectIdOf
diff --git a/src/Game/Antisplice/Monad/Dungeon.hs b/src/Game/Antisplice/Monad/Dungeon.hs
--- a/src/Game/Antisplice/Monad/Dungeon.hs
+++ b/src/Game/Antisplice/Monad/Dungeon.hs
@@ -64,9 +64,10 @@
     -- * Stereotypes
     PlayerStereo (..),
     CooldownId (..),
-    SkillParam (..),
     GetterResponse (..),
-    Getters(..),
+    Invokable,
+    InvokableP,
+    RecipeMethod(..),
     -- * Dungeons
     DungeonState (..),
     currentRoomOf,
@@ -103,7 +104,7 @@
 import Debug.Trace
 
 -- | Matches any 'MonadDungeon' context
-type DungeonM a = forall m.(MonadDungeon m) => m a
+type DungeonM a = forall m.(MonadDungeon m,MonadError SplErr m,MonadVocab m) => m a
 -- | Matches any 'MonadDungeon' context that also implements most of the Chatty classes and some of our utility classes.
 type ChattyDungeonM a = forall m.(Functor m,ExtendedPrinter m,MonadExpand m,ExpanderEnv m,MonadDungeon m,MonadError SplErr m,MonadAtoms m,MonadClock m,MonadVocab m,MonadRandom m,Broadcaster PlayerId m) => m a
 -- | The common type of all event handlers.
@@ -307,10 +308,17 @@
   getObjectState = Object $ \s -> return (s,s)
   putObjectState s = Object $ \_ -> return ((),s)
 
+-- | Some handler that may be invoked by the user
+type Invokable = [String] -> HandlerBox
+
+-- | Some prerequisite that may be invoked by the user
+type InvokableP = [String] -> PrerequisiteBox
+
 -- | A player stereotype
 data PlayerStereo = PlayerStereo {
     stereoCalcStatBonus :: (StatKey -> Int) -> StatKey -> Int,
-    stereoSkillBonus :: String -> Maybe (SkillParam -> HandlerBox)
+    stereoSkillBonus :: String -> Maybe Invokable,
+    stereoRecipeBonus ::  String -> Maybe (RecipeMethod -> Invokable)
   } deriving Typeable
 
 -- | Phantom ID type for players
@@ -326,28 +334,12 @@
     currencyNameOf :: String
   } deriving (Ord,Eq)
 
--- | Parameter for skill invocation
-data SkillParam = SkillParam {
-    paramGettersOf :: Getters,
-    paramDirectOf :: Maybe Token,
-    paramAtOf :: Maybe Token,
-    paramOnOf :: Maybe Token
-  }
+-- | Method of using recipes
+data RecipeMethod = RecipeMethod Int deriving (Eq,Ord)
 
 -- | Response of an object getter
 data GetterResponse = Found ObjectState | TooMany | NoneFound
 
--- | Tuple of object getters
-data Getters = Getters {
-    getAvailableObject :: Token -> GetterResponse,
-    getCarriedObject :: Token -> GetterResponse,
-    getSeenObject :: Token -> GetterResponse
-  }
-
-instance None SkillParam where
-  none = SkillParam none none none none
-instance None Getters where
-  none = Getters (return none) (return none) (return none)
 instance None GetterResponse where
   none = NoneFound
 
diff --git a/src/Game/Antisplice/Monad/Vocab.hs b/src/Game/Antisplice/Monad/Vocab.hs
--- a/src/Game/Antisplice/Monad/Vocab.hs
+++ b/src/Game/Antisplice/Monad/Vocab.hs
@@ -40,7 +40,7 @@
            | Skilln String
            | Nounc [String] (Maybe Int) String
              -- ^ Complex noun, with attributes and maybe ordinal index.
-             deriving Show
+             deriving (Eq,Show)
 
 -- | The vocab monad. Carries the currently noun vocab as its state.
 newtype VocabT m a = Vocab { runVocabT :: TST Token -> m (a,TST Token) }
diff --git a/src/Game/Antisplice/Rooms.hs b/src/Game/Antisplice/Rooms.hs
--- a/src/Game/Antisplice/Rooms.hs
+++ b/src/Game/Antisplice/Rooms.hs
@@ -124,6 +124,7 @@
 import Data.Text (pack,unpack)
 import Data.Maybe
 import Data.List
+import Data.Char
 import Data.Time.Clock
 import Text.Printf
 
@@ -660,29 +661,46 @@
       prnum Nothing Nothing i = show i
       prnum (Just d) Nothing i = show (i `div` d)
       prnum Nothing (Just m) i = let exp s l = replicate (l-length s) '0' ++ s in exp (show (i `mod` m)) (length $ show (m-1))
-      prnum (Just d) (Just m) i = prnum (Just d) Nothing i ++ "." ++ prnum (Just m) Nothing i
-      replace :: (MonadPrinter m,MonadDungeon m) => String -> Maybe Int -> Maybe Int -> m ()
-      replace "health" d m = liftM (prnum d m . joinMaybe . avlLookup Health . playerCurrenciesOf) getPlayerState >>= mprint
-      replace "ohealth" d m = do
+      prnum (Just d) (Just m) i = prnum (Just d) Nothing i ++ "." ++ prnum Nothing (Just m) i
+      prstr Nothing Nothing s = s
+      prstr (Just d) _ s = take d s
+      prstr Nothing (Just m) s = drop m s
+      numfun (Just "inc") i = i + 1
+      numfun (Just "dec") i = i - 1
+      numfun Nothing i = i
+      numfun _ _ = 0
+      strfun (Just "up") s = map toUpper s
+      strfun (Just "down") s = map toLower s
+      strfun (Just "capital") "" = ""
+      strfun (Just "capital") (s:ss) = toUpper s : ss
+      strfun Nothing s = s
+      strfun _ _ = ""
+      replace :: (MonadPrinter m,MonadDungeon m) => String -> Maybe Int -> Maybe Int -> Maybe String -> m ()
+      replace "health" d m f = liftM (prnum d m . numfun f . joinMaybe . avlLookup Health . playerCurrenciesOf) getPlayerState >>= mprint
+      replace "ohealth" d m f = do
         i <- liftM playerOpponentOf getPlayerState
         o <- withObject i getObjectState
-        mprint $ prnum d m $ objectCurHealthOf o
-      replace "otitle" _ _ = do
+        mprint $ prnum d m $ numfun f $ objectCurHealthOf o
+      replace "otitle" d m f = do
         i <- liftM playerOpponentOf getPlayerState
         o <- withObject i getObjectState
-        mprint $ unpack $ objectTitleOf o
-      replace s d m = do
+        mprint $ prstr d m $ strfun f $ unpack $ objectTitleOf o
+      replace s d m f = do
         cs <- liftM (avlInorder . currenciesOf) getDungeonState
         case filter ((==s).currencyNameOf) cs of
           [] -> return ()
-          [c] -> liftM (prnum d m .  joinMaybe . avlLookup (currencyIdOf c) . playerCurrenciesOf) getPlayerState >>= mprint
+          [c] -> liftM (prnum d m . numfun f . joinMaybe . avlLookup (currencyIdOf c) . playerCurrenciesOf) getPlayerState >>= mprint
           cs -> mprintLn $ concat $ map currencyNameOf cs
+      (cm,nmc)
+        | '!' `elem` nm = (Just $ takeWhile (/='!') nm, tail $ dropWhile (/='!') nm)
+        | otherwise = (Nothing, nm)
       (nm1,dm1,mm1)
-        | '/' `elem` nm = (takeWhile (/='/') nm, Just $ read $ tail $ dropWhile (/='/') nm, Nothing)
-        | '%' `elem` nm = (takeWhile (/='%') nm, Nothing, Just $ read $ tail $ dropWhile (/='%') nm)
-        | otherwise = (nm,Nothing,Nothing)
+        | '/' `elem` nmc = (takeWhile (/='/') nmc, Just $ read $ tail $ dropWhile (/='/') nmc, Nothing)
+        | '%' `elem` nmc = (takeWhile (/='%') nmc, Nothing, Just $ read $ tail $ dropWhile (/='%') nmc)
+        | '.' `elem` nmc = let xz = Just $ read $ tail $ dropWhile (/='.') nmc in (takeWhile (/='.') nmc, xz, xz)
+        | otherwise = (nmc,Nothing,Nothing)
   in do
-    replace nm1 dm1 mm1
+    replace nm1 dm1 mm1 cm
     expandDun rm
 expandDun ('#':ss) =
   let (nm,rm) = (takeWhile isAnum &&& dropWhile isAnum) ss
diff --git a/src/Game/Antisplice/Skills.hs b/src/Game/Antisplice/Skills.hs
--- a/src/Game/Antisplice/Skills.hs
+++ b/src/Game/Antisplice/Skills.hs
@@ -24,31 +24,38 @@
 -- | Provides utility functions for composing skills
 module Game.Antisplice.Skills (
   -- * Utility types
-  Condition,
-  Consumer,
+  Condition(..),
+  Consumer(..),
   Skill (..),
+  Recipe (..),
   ToConsumer (toConsumer),
   -- * Compositors
-  (!+), (>!+), (!+>), (>!+>),
-  -- * Skill builders
+  Extensible(..),
+  (>!+), (!+>), (>!+>),
+  -- * Builders
   skill,
-  bareAction,
-  bareCondition,
+  recipe,
+  validConsumer,
+  validCondition,
+  (#->),
+  (#->>),
   -- * Sample consumers
   focusDirectC,
   optionallyFocusDirectC,
+  callRecipe,
   -- * Final wrappers
-  runSkill,
+  runConsumer,
   wrapSkills,
-  -- * Misc
-  dispget
+  wrapRecipes,
   ) where
 
 import Control.Arrow
 import Control.Monad
+import Control.Monad.Identity
 import Control.Monad.Error.Class
 import Data.Monoid
 import Game.Antisplice.Action
+import Game.Antisplice.Call
 import Game.Antisplice.Errors
 import Game.Antisplice.Monad.Dungeon
 import Game.Antisplice.Monad.Vocab
@@ -59,28 +66,30 @@
 import Game.Antisplice.Utils.Atoms
 
 -- | A wrapper type for skill execution preconditions.
-newtype Condition = Condition { runCondition :: SkillParam -> Prerequisite }
+newtype Condition = Condition { runCondition' :: InvokableP }
+runCondition :: Condition -> [String] -> ChattyDungeonM Bool
+runCondition c ps = runPrerequisite $ runCondition' c ps
 
 instance Monoid Condition where
-  mempty = Condition $ \_ -> return True
-  a `mappend` b = Condition $ \p -> do
-    a' <- runCondition a p
-    b' <- runCondition b p
+  mempty = Condition $ \_ -> Prerequisite $ return True
+  a `mappend` b = Condition $ \ps -> Prerequisite $ do
+    a' <- runCondition a ps
+    b' <- runCondition b ps
     return (a' && b')
 
 instance IsAction Condition where
-  p #&& q = Condition $ \x -> do
-    a <- runCondition p x
-    if a then runCondition q x
+  p #&& q = Condition $ \ps -> Prerequisite $ do
+    a <- runCondition p ps
+    if a then runCondition q ps
          else return False
-  p #|| q = Condition $ \x -> do
-    a <- runCondition p x
+  p #|| q = Condition $ \ps -> Prerequisite $ do
+    a <- runCondition p ps
     if a then return True
-         else runCondition q x
+         else runCondition q ps
   p !&& q = p <> q
-  p !|| q = Condition $ \x -> do
-    a <- runCondition p x
-    b <- runCondition q x
+  p !|| q = Condition $ \ps -> Prerequisite $ do
+    a <- runCondition p ps
+    b <- runCondition q ps
     return (a || b)
               
 instance None Condition where
@@ -89,41 +98,55 @@
 -- | A skill. Build it using the '!+' compositor and the 'skill' function.
 data Skill = Skill {
     skillConditionOf :: !Condition,
-    skillActionOf :: !(SkillParam -> Handler),
+    skillActionOf :: !Invokable,
     skillNameOf :: !String
   }
 
+-- | A recipe. Build it using the '!+' compositor and the 'recipe' function.
+data Recipe = Recipe {
+    recipeConditionOf :: !Condition,
+    recipeActionOf :: !Invokable,
+    recipeMethodOf :: !RecipeMethod,
+    recipeNameOf :: !String
+  }
+
 -- | A single consumer. Build it using 'bareAction', 'bareCondition' and monoid concatenation.
-data Consumer = Consumer !Condition !(SkillParam -> Handler)
+data Consumer = Consumer !Condition !Invokable
 
 instance Monoid Consumer where
-  mempty = Consumer none (const noneM)
-  (Consumer c1 a1) `mappend` (Consumer c2 a2) = Consumer (c1 <> c2) (\p -> a1 p >> a2 p)
+  mempty = Consumer none noneM
+  (Consumer c1 a1) `mappend` (Consumer c2 a2) = Consumer (c1 <> c2) (\ps -> Handler (runHandler (a1 ps) >> runHandler (a2 ps)))
 
 instance IsAction Consumer where
-  (Consumer c1 a1) #&& (Consumer c2 a2) = Consumer (c1 #&& c2) (\p -> a1 p >> a2 p)
-  (Consumer c1 a1) #|| (Consumer c2 a2) = Consumer (c1 #|| c2) (\p -> runCondition c1 p >>= \q -> if q then a1 p else a2 p)
+  (Consumer c1 a1) #&& (Consumer c2 a2) = Consumer (c1 #&& c2) (\ps -> Handler (runHandler (a1 ps) >> runHandler (a2 ps)))
+  (Consumer c1 a1) #|| (Consumer c2 a2) = Consumer (c1 #|| c2) (\ps -> Handler (runCondition c1 ps >>= \q -> if q then runHandler (a1 ps) else runHandler (a2 ps)))
   p !&& q = p <> q
-  (Consumer c1 a1) !|| (Consumer c2 a2) = Consumer (c1 !|| c2) (\p -> runCondition c1 p >>= \q -> runCondition c2 p >> if q then a1 p else a2 p)
+  (Consumer c1 a1) !|| (Consumer c2 a2) = Consumer (c1 !|| c2) (\ps -> Handler (runCondition c1 ps >>= \q -> runCondition c2 ps >> if q then runHandler (a1 ps) else runHandler (a2 ps)))
 
 instance None Consumer where
   none = mempty
 
 -- | Focus direct object
 focusDirectC :: Consumer
-focusDirectC = bareAction (\p -> case p of
-                                   SkillParam Getters{..} (Just o) _ _ -> focusOpponent =<< return.objectIdOf =<< dispget getSeenObject o
-                                   _ -> throwError UnintellegibleError)
+{-focusDirectC = bareAction (\p -> case p of
+                                   SkillParam Getters{..} (Just o) _ _ _ _ -> focusOpponent =<< return.objectIdOf =<< dispget getSeenObject o
+                                   _ -> throwError UnintellegibleError)-}
+focusDirectC = validConsumer SeenObject $ \o -> focusOpponent $ objectIdOf o
 
 -- | Optionally ocus direct object (obligatory if none is focused yet)
 optionallyFocusDirectC :: Consumer
-optionallyFocusDirectC = bareAction (\p -> case p of
-                                        SkillParam Getters{..} (Just o) _ _ -> focusOpponent =<< return.objectIdOf =<< dispget getSeenObject o
-                                        SkillParam _ Nothing _ _ -> do
+{-optionallyFocusDirectC = bareAction (\p -> case p of
+                                        SkillParam Getters{..} (Just o) _ _ _ _ -> focusOpponent =<< return.objectIdOf =<< dispget getSeenObject o
+                                        SkillParam _ Nothing _ _ _ _ -> do
                                           o <- liftM playerOpponentOf getPlayerState
                                           case o of
                                             FalseObject -> throwError UnintellegibleError
-                                            _ -> noneM)
+                                            _ -> noneM)-}
+optionallyFocusDirectC = focusDirectC #|| validConsumer Nil (\_ ->  do
+                                                                o <- liftM playerOpponentOf getPlayerState
+                                                                case o of
+                                                                  FalseObject -> throwError UnintellegibleError
+                                                                  _ -> noneM)
 
 -- | Typeclass for everything that may act as a consumer.
 class ToConsumer c where
@@ -135,79 +158,127 @@
 instance ToConsumer Skill where
   toConsumer (Skill c a n) = Consumer c a
 
-instance ToConsumer HandlerBox where
-  toConsumer (Handler t) = Consumer none (const t)
+instance ToConsumer Recipe where
+  toConsumer (Recipe c a m n) = Consumer c a
 
-instance ToConsumer (SkillParam -> HandlerBox) where
-  toConsumer t = Consumer none (runHandler . t)
+instance ToConsumer HandlerBox where
+  toConsumer t = Consumer none (\_ -> t)
 
 instance ToConsumer Condition where
-  toConsumer c = Consumer c (const noneM)
+  toConsumer c = Consumer c (\_ -> Handler $ return none)
 
 instance ToConsumer PrerequisiteBox where
-  toConsumer p = Consumer (Condition $ \_ -> runPrerequisite p) (const noneM)
+  toConsumer p = Consumer (Condition $ \_ -> p) (\_ -> Handler $ return none)
 
 instance ToConsumer Action where
-  toConsumer a = Consumer (Condition $ \_ -> askAction a) (\_ -> runAction a)
+  toConsumer a = Consumer (Condition $ \_ -> Prerequisite $ askAction a) (\_ -> Handler $ runAction a)
 
-infixl 5 !+
--- | Add a consumer to the skill.
-(!+) :: ToConsumer c => Skill -> c -> Skill
-s !+ c = s !+! toConsumer c
-  where (Skill c a n) !+! (Consumer c1 a1) = Skill (c <> c1) (\p -> a p >> a1 p) n
+-- | Typeclass for everything that may be extended by consumers using !+
+class Extensible e where
+  infixl 5 !+
+  -- | Add a consumer to the skill.
+  (!+) :: ToConsumer c => e -> c -> e
 
+instance Extensible Skill where  
+  s !+ c = s !+! toConsumer c
+    where (Skill c a n) !+! (Consumer c1 a1) = Skill (c <> c1) (\ps -> Handler (runHandler (a ps) >> runHandler (a1 ps))) n
+
+instance Extensible Recipe where
+  s !+ c = s !+! toConsumer c
+    where (Recipe c a m n) !+! (Consumer c1 a1) = Recipe (c <> c1) (\ps -> Handler (runHandler (a ps) >> runHandler (a1 ps))) m n
+
+instance Extensible Consumer where
+  s !+ c = s <> toConsumer c
+
 infixl 5 >!+
--- | Add a consumer to the monadic skill
-(>!+) :: (ToConsumer c,Monad m) => m Skill -> c -> m Skill
+-- | Add a consumer to the monadic extensible
+(>!+) :: (ToConsumer c,Monad m,Extensible e) => m e -> c -> m e
 m >!+ c = do
   s <- m
   return (s !+ c)
 
 infixl 5 !+>
--- | Add a monadic consumer to the skill
-(!+>) :: (ToConsumer c,Monad m) => Skill -> m c -> m Skill
+-- | Add a monadic consumer to the extensible
+(!+>) :: (ToConsumer c,Monad m,Extensible e) => e -> m c -> m e
 s !+> m = do
   c <- m
   return (s !+ c)
 
 infixl 5 >!+>
--- | Add a monadic consumer to the monadic skill
-(>!+>) :: (ToConsumer c,Monad m) => m Skill -> m c -> m Skill
+-- | Add a monadic consumer to the monadic extensible
+(>!+>) :: (ToConsumer c,Monad m,Extensible e) => m e -> m c -> m e
 ms >!+> mc = do
   s <- ms
   c <- mc
   return (s !+ c)
 
--- | Build a consumer from an action.
-bareAction :: (SkillParam -> Handler) -> Consumer
-bareAction t = toConsumer $ \p -> Handler (t p)
+-- | Build a consumer using new-style input validation
+validConsumer :: (Append r Nil r,CallMask m r,Tuplify r t) => m -> (t -> Handler) -> Consumer
+validConsumer m h = Consumer
+  (Condition $ \ps -> Prerequisite $ tryMask m ps)
+  (\ps -> Handler $ do
+      t <- processMask m ps
+      h t)
 
--- | Build a consumer from a condition.
-bareCondition :: (SkillParam -> ChattyDungeonM Bool) -> Consumer
-bareCondition = toConsumer.Condition
+infixr 7 #->
+-- | Infix version of validConsumer
+(#->) :: (Append r Nil r, CallMask m r, Tuplify r t) => m -> (t -> Handler) -> Consumer
+(#->) = validConsumer
 
+infixr 7 #->>
+-- | Infix version of validConsumer, swallowing the empty handler parameter
+(#->>) :: CallMask m Nil => m -> Handler -> Consumer
+m #->> h = m #-> const h
+
+-- | Build a condition using new-style input validation
+validCondition :: (Append r Nil r, CallMask m r, Tuplify r t) => m -> (t -> Prerequisite) -> Condition
+validCondition m p =
+  Condition (\ps -> Prerequisite $ tryMask m ps) <>
+  Condition (\ps -> Prerequisite $ do t <- processMask m ps; p t)
+
+-- | Dispatch the remaining part of the line as a recipe call
+callRecipe :: RecipeMethod -> Consumer
+{-callRecipe m =  Consumer none (\f ps -> Handler $ case ps of
+                                    [] -> throwError UnintellegibleError
+                                    p:ps -> do
+                                      ste <- totalStereo
+                                      case stereoRecipeBonus ste p of
+                                        Nothing -> throwError CantCastThatNowError
+                                        Just r -> runHandler $ r m f ps)-}
+callRecipe m = CatchAny :-: Remaining :-: Nil #->
+  \(p,ps) -> do
+      ste <- totalStereo
+      case stereoRecipeBonus ste p of
+        Nothing -> throwError CantCastThatNowError
+        Just r -> runHandler $ r m ps
+
 -- | Build a bogus skill that does nothing but has a name. Use this with '!+' to build powerful skills.
 skill :: String -> Skill
-skill = Skill none (const noneM)
+skill = Skill none noneM
 
+-- | Build a bogus recipe that does nothing has a name and a usage method. Use this with '!+' to build powerful recipes.
+recipe :: RecipeMethod -> String -> Recipe
+recipe = Recipe none noneM
+
 -- | Wrap the skills into a form that is accepted by stereotypes.
-wrapSkills :: [Skill] -> String -> Maybe (SkillParam -> HandlerBox)
+wrapSkills :: [Skill] -> String -> Maybe Invokable
 wrapSkills sk n =
   case filter ((==n).skillNameOf) sk of
     [] -> Nothing
-    sk:_ -> Just $ \p -> Handler (runSkill sk p)
-    
--- | Run the given skill
-runSkill :: Skill -> SkillParam -> Handler
-runSkill (Skill c a _) p = do
-  b <- runCondition c p
-  unless b $ throwError CantCastThatNowError
-  a p
+    sk:_ -> Just $ runConsumer sk
 
--- | Dispatch the getter to the underlying monad
-dispget :: MonadError SplErr m => (Token -> GetterResponse) -> Token -> m ObjectState
-dispget f t =
-  case f t of
-    NoneFound -> throwError CantSeeOneError
-    TooMany -> throwError WhichOneError
-    Found x -> return x
+-- | Wrap the recipes into a form that is accepted by stereotypes.
+wrapRecipes :: [Recipe] -> String -> Maybe (RecipeMethod -> Invokable)
+wrapRecipes sk n =
+  case filter ((==n).recipeNameOf) sk of
+    [] -> Nothing
+    sk:_ -> Just $ \m -> if m == recipeMethodOf sk then runConsumer sk else \_ -> Handler $ throwError WrongMethodError
+
+-- | Run the given consumer
+runConsumer :: ToConsumer c => c -> Invokable
+runConsumer c = runConsumer' $ toConsumer c
+  where runConsumer' :: Consumer -> Invokable
+        runConsumer' (Consumer c a) = \ps -> Handler $ do
+          b <- runCondition c ps
+          unless b $ throwError CantCastThatNowError
+          runHandler $ a ps
diff --git a/src/Game/Antisplice/Stats.hs b/src/Game/Antisplice/Stats.hs
--- a/src/Game/Antisplice/Stats.hs
+++ b/src/Game/Antisplice/Stats.hs
@@ -82,11 +82,14 @@
   return $ mconcat stes
 
 instance Monoid PlayerStereo where
-  mempty = PlayerStereo (\_ _ -> 0) (\_ -> Nothing)
+  mempty = PlayerStereo (\_ _ -> 0) (\_ -> Nothing) (\_ -> Nothing)
   a `mappend` b = PlayerStereo
     (\get k -> stereoCalcStatBonus a (\k -> stereoCalcStatBonus b get k + get k) k + stereoCalcStatBonus b get k)
     (\s -> case stereoSkillBonus a s of
           Nothing -> stereoSkillBonus b s
+          Just b -> Just b)
+    (\s -> case stereoRecipeBonus a s of
+          Nothing -> stereoRecipeBonus b s
           Just b -> Just b)
 
 instance None PlayerStereo where
diff --git a/src/Game/Antisplice/Stereos.hs b/src/Game/Antisplice/Stereos.hs
--- a/src/Game/Antisplice/Stereos.hs
+++ b/src/Game/Antisplice/Stereos.hs
@@ -37,7 +37,9 @@
     mergeStereo,
     mergeStereoM,
     mergeSkill,
+    mergeRecipe,
     mergeSkillM,
+    mergeRecipeM,
     -- * Default stereos
     defaultStereo,
     visualStereo,
@@ -51,6 +53,7 @@
 import Control.Arrow
 import Data.Monoid
 import Game.Antisplice.Action
+import Game.Antisplice.Call
 import Game.Antisplice.Errors
 import Game.Antisplice.Monad.Dungeon
 import Game.Antisplice.Monad.Vocab
@@ -80,18 +83,28 @@
 
 -- | Create a stereotype that carries a stats modifier
 statsStereo :: ((StatKey -> Int) -> StatKey -> Int) -> PlayerStereo
-statsStereo m = PlayerStereo m (const none)
+statsStereo m = PlayerStereo m (const none) (const none)
 
 -- | Create a stereotype that carries skills
 skillsStereo :: [Skill] -> PlayerStereo
-skillsStereo sk = PlayerStereo (\_ _ -> 0) $ wrapSkills sk
+skillsStereo sk = PlayerStereo (\_ _ -> 0) (wrapSkills sk) (const none)
 
+-- | Create a stereotype that carries recipes
+recipesStereo :: [Recipe] -> PlayerStereo
+recipesStereo rp = PlayerStereo (\_ _ -> 0) (const none) (wrapRecipes rp)
+
 -- | Create a stereotype that carries a skill from a monadic composition
 skillStereoM :: Monad m => m Skill -> m PlayerStereo
 skillStereoM m = do
   sk <- m
   return $ skillsStereo [sk]
 
+-- | Create a stereotype that carries a skill from a monadic composition
+recipeStereoM :: Monad m => m Recipe -> m PlayerStereo
+recipeStereoM m = do
+  rp <- m
+  return $ recipesStereo [rp]
+
 -- | For monadic stereotype construction
 newtype StereoBuilderT m a = StereoBuilder { runStereoBuilderT :: PlayerStereo -> m (a,PlayerStereo) }
 
@@ -127,6 +140,18 @@
   sk <- lift m
   mergeSkill sk
 
+-- | Merge the given pure recipe into the built stereotype
+mergeRecipe :: MonadVocab m => Recipe -> StereoBuilderT m ()
+mergeRecipe rp = do
+  insertVocab (recipeNameOf rp) Noun
+  mergeStereo $ recipesStereo [rp]
+
+-- | Merge the given monadic recipe into the built stereotype
+mergeRecipeM :: MonadVocab m => m Recipe -> StereoBuilderT m ()
+mergeRecipeM m = do
+  rp <- lift m
+  mergeRecipe rp
+
 -- | Process the builder chain and register the resulting stereotype
 registerStereoM :: MonadAtoms m => StereoBuilderT m () -> m (Atom PlayerStereo)
 registerStereoM m = do
@@ -143,22 +168,14 @@
       AttackPower -> get Strength * 2
       CooldownDuration -> (get CooldownDuration ^ 2) `div` (get CooldownDuration + get Haste) - get CooldownDuration
       _ -> 0
-  mergeSkill $ skill "use" !+ bareAction (\p ->
-    case p of
-      SkillParam Getters{..} (Just o) Nothing Nothing -> objectTriggerOnUseOf =<< dispget getAvailableObject o
-      _ -> throwError UnintellegibleError)
+  mergeSkill $ skill "use" !+ validConsumer AvailableObject objectTriggerOnUseOf
 
 visualStereo :: MonadVocab m => m PlayerStereo
 visualStereo = liftM snd $ flip runStereoBuilderT none $ do
-  mergeSkill $ skill "look" !+ bareAction (\p ->
-    case p of
-      SkillParam Getters{..} Nothing Nothing Nothing -> roomTriggerOnLookOf =<< getRoomState
-      SkillParam Getters{..} Nothing (Just n) Nothing -> objectTriggerOnLookAtOf =<< dispget getAvailableObject n
-      _ -> throwError UnintellegibleError)
-  mergeSkill $ skill "read" !+ bareAction (\p ->
-    case p of
-      SkillParam Getters{..} (Just n) Nothing Nothing -> objectTriggerOnReadOf =<< dispget getAvailableObject n
-      _ -> throwError UnintellegibleError)
+  mergeSkill $ skill "look" !+
+    validConsumer Nil (\_ -> roomTriggerOnLookOf =<< getRoomState) #||
+    validConsumer (Prep "at" :-: AvailableObject :-: Nil) objectTriggerOnLookAtOf
+  mergeSkill $ skill "read" !+ validConsumer AvailableObject objectTriggerOnReadOf
 
 manualStereo :: MonadVocab m => m PlayerStereo
 manualStereo = liftM snd $ flip runStereoBuilderT none $ do
@@ -166,11 +183,5 @@
 
 consumeStereo :: MonadVocab m => m PlayerStereo
 consumeStereo = liftM snd $ flip runStereoBuilderT none $ do
-  mergeSkill $ skill "drink" !+ bareAction (\p ->
-    case p of
-      SkillParam Getters{..} (Just o) Nothing Nothing -> objectTriggerOnDrinkOf =<< dispget getAvailableObject o
-      _ -> throwError UnintellegibleError)
-  mergeSkill $ skill "eat" !+ bareAction (\p ->
-    case p of
-      SkillParam Getters{..} (Just o) Nothing Nothing -> objectTriggerOnEatOf =<< dispget getAvailableObject o
-      _ -> throwError UnintellegibleError)
+  mergeSkill $ skill "drink" !+ validConsumer AvailableObject objectTriggerOnDrinkOf
+  mergeSkill $ skill "eat" !+ validConsumer AvailableObject objectTriggerOnEatOf
