packages feed

MazesOfMonad-1.0.7: src/MoresmauJP/Rpg/Inventory.hs

-- | Inventory management
-- (c) JP Moresmau 2009
module MoresmauJP.Rpg.Inventory (
        Inventory(), Position(..), ItemType(..), InventoryError(..),PotionType(..),
        makeEmptyInventory, makeFullInventory, takeItem, dropItem, listItems, 
        listAllowedPositions,listActiveItems,listCarriedItems,listCarriedItemsUniqueObject,
        getCarriedItem,        positionAllowed, addGold, getGold,
        Gold, isActive
        ) where

import Data.List
import Data.Maybe
import qualified Data.Map as M

data ItemType = Weapon {
                itName::String, 
                damageLow::Int, 
                damageHigh::Int,
                hands::Int,
                price::Gold}
        | Armor {
                itName::String, 
                damageLow::Int, 
                damageHigh::Int,
                price::Gold}
        | Shield {
                itName::String, 
                damageLow::Int, 
                damageHigh::Int,
                price::Gold}
        | Helmet {
                itName::String, 
                damageLow::Int, 
                damageHigh::Int,
                price::Gold}
        | Potion {
                itName::String,
                potionType::PotionType,
                price::Gold}
        | Scroll {
                itName::String,
                spellType::String,
                price::Gold}
        | Trap {
                itName::String,
                damageLow::Int, 
                damageHigh::Int,
                triggerDescription::String
                }
        deriving (Show,Read,Eq)
        

data Position= RightHand | LeftHand | Body | Head | Bag Int
        deriving (Show,Read,Eq,Ord)

-- | Inventory Map item by Position Bag Size 
data Inventory = Inventory {
        invItems::(M.Map Position ItemType),
        invBagCapacity:: Int,
        invGold:: Gold
        } deriving (Eq,Show,Read)

type Gold=Int

data InventoryError = InvalidBagPosition Int
        | InvalidPositionForItem ItemType Position
        deriving (Eq,Show,Read)

data PotionType = HealingPotion Int | MindPotion Int
        deriving (Show,Read,Eq)

makeEmptyInventory :: Int -> Gold -> Inventory
makeEmptyInventory a gold= Inventory M.empty a gold

{--
makeFullInventory :: [(Position,ItemType)] -> Gold -> Inventory
makeFullInventory [] gold= makeEmptyInventory 0 gold
makeFullInventory posAndItems gold= let
        maxPos=maximum (map (getBagPos . fst) posAndItems)
        in
                reconcile2Hand (Inventory M.empty maxPos gold) posAndItems
        where
                getBagPos (Bag p)=p
                getBagPos _ =0
--}

makeFullInventory :: [(Position,ItemType)] -> Int -> Gold -> Inventory
makeFullInventory [] maxPos gold= makeEmptyInventory maxPos gold
makeFullInventory posAndItems maxPos gold= reconcile2Hand (Inventory M.empty maxPos gold) posAndItems

addGold :: Inventory -> Gold -> Inventory
addGold i g=let        oldGold=invGold i
        in i{invGold=max 0 (oldGold+g)}

getGold :: Inventory -> Gold
getGold i=invGold i

reconcile2Hand :: Inventory -> [(Position,ItemType)] -> Inventory
reconcile2Hand i poss=foldr f i poss
        where 
                f:: (Position,ItemType) -> Inventory -> Inventory
                f pos i=
                        case getCarriedItem i (fst pos)        of
                                Right Nothing ->
                                        let Right (i2,_)=takeItem i (snd pos) (fst pos)
                                        in i2
                                _->i

takeItem :: Inventory -> ItemType -> Position -> Either InventoryError (Inventory,[ItemType])
takeItem i@(Inventory {invBagCapacity= sz}) it p=  do
        p2 <- checkPos sz p
        ti1 <- takeItem' i it p2 True
        takeTwoHandsItem ti1 it p2
        
takeTwoHandsItem ::(Inventory,Maybe ItemType) -> ItemType -> Position  -> Either InventoryError (Inventory,[ItemType])
takeTwoHandsItem (i,it1) it@(Weapon {hands=2}) RightHand= do
        (iv2,it2) <- takeItem' i it LeftHand (needSndDrop it1)
        return (iv2,catMaybes [it1,it2])
takeTwoHandsItem (i,it1) it@(Weapon {hands=2}) LeftHand= do
        (iv2,it2) <- takeItem' i it RightHand (needSndDrop it1)
        return (iv2,catMaybes [it1,it2])
takeTwoHandsItem (i,it1)  _ _=do
        return (i,catMaybes [it1])
        
needSndDrop :: Maybe ItemType -> Bool
needSndDrop (Just (Weapon {hands=2}))=False
needSndDrop _=True

takeItem' :: Inventory -> ItemType -> Position -> Bool -> Either InventoryError (Inventory,Maybe ItemType)
takeItem' i it p needDrop=  do
        let allowed=positionAllowed it p
        if allowed 
                then do
                        (i2@(Inventory {invItems=m}),it2) <- if needDrop 
                                then dropItemToBag i p
                                else Right (i, Nothing)
                        return ((i2{invItems=(M.insert p it m)}),it2)
                else Left (InvalidPositionForItem it p)

dropItemToBag :: Inventory -> Position -> Either InventoryError (Inventory,Maybe ItemType)
dropItemToBag i p@(Bag _)=dropItem i p
dropItemToBag i p=do
        mi2@(i2,it2)<- dropItem i p
        if isJust it2
                then do
                        let freeBagPos=getFirstFreeBagPos i2
                        if (isJust freeBagPos)
                                then do
                                        (i3,_)<- takeItem i2 (fromJust it2) (fromJust freeBagPos)
                                        return (i3,Nothing)
                                else 
                                        return mi2
                else return mi2

--instance Monad (Either InventoryError) where
--        Left a >>= _ = Left a
--        Right a >>= f = f a
--        return = Right 

getFirstFreeBagPos:: Inventory -> Maybe Position
getFirstFreeBagPos (Inventory {invItems=m,invBagCapacity=sz})=listToMaybe $ (filter (flip M.notMember m) (map Bag [1 .. sz]))


dropItem :: Inventory -> Position -> Either InventoryError (Inventory,Maybe ItemType)
dropItem i@(Inventory {invItems=m,invBagCapacity=sz}) p = do
        p2 <- checkPos sz p
        let item = M.lookup p2 m
        let m2 = M.delete p2 m
        return (i{invItems=(dropTwoHandsItem m2 item p2)},item)

dropTwoHandsItem ::(M.Map Position ItemType) -> Maybe ItemType -> Position  -> (M.Map Position ItemType)
dropTwoHandsItem m (Just (Weapon {hands=2})) RightHand= M.delete LeftHand m
dropTwoHandsItem m (Just (Weapon {hands=2})) LeftHand= M.delete RightHand m
dropTwoHandsItem m  _ _=m

checkPos :: Int -> Position -> Either InventoryError Position
checkPos capacity (Bag pos) | pos<1 = Left (InvalidBagPosition capacity)
checkPos capacity (Bag pos) | pos>capacity = Left (InvalidBagPosition capacity)
checkPos _ p = Right p

listItems :: Inventory -> [(Position,Maybe ItemType)]
listItems (Inventory {invItems=m,invBagCapacity=sz}) = map 
        (\x -> (x,M.lookup x m))
        ([RightHand, LeftHand, Body, Head] ++ (map Bag [1..sz]))
        
listCarriedItems :: Inventory -> [(Position,ItemType)]        
listCarriedItems i = map (\(x,y)->(x,fromJust y)) (filter (isJust . snd) (listItems i))
        
listCarriedItemsUniqueType :: Inventory -> [(Position,ItemType)]        
listCarriedItemsUniqueType = nubBy (\x y -> (snd x)==(snd y)) . listCarriedItems

listCarriedItemsUniqueObject :: Inventory -> [(Position,ItemType)]        
listCarriedItemsUniqueObject = (filter f) . listCarriedItems
        where 
                f (LeftHand,Weapon{hands=2})=False
                f _=True
        
        
getCarriedItem :: Inventory -> Position ->  Either InventoryError (Maybe ItemType)
getCarriedItem (Inventory {invItems=m,invBagCapacity=sz}) pos=do
        p2<- checkPos sz pos 
        return (M.lookup p2 m)
        
listActiveItems :: Inventory -> [ItemType]
listActiveItems i= (map snd (filter (isActive . fst) (listCarriedItems i)))
        
isActive :: Position -> Bool
isActive (Bag {})=False
isActive _ =True
        
listAllowedPositions :: Inventory -> ItemType -> [(Position,Maybe ItemType)]
listAllowedPositions i it=filter ((positionAllowed it) . fst) (listItems i)
        
positionAllowed :: ItemType -> Position -> Bool
-- everything can go in the bag
positionAllowed _ (Bag {})=True
positionAllowed (Weapon {}) RightHand=True
positionAllowed (Weapon {}) LeftHand=True
-- only armor on body
positionAllowed (Armor {}) Body=True
-- only helmer on head
positionAllowed (Helmet {}) Head=True
positionAllowed (Shield {}) RightHand=True
positionAllowed (Shield {}) LeftHand=True
positionAllowed _ Body=False
positionAllowed _ Head=False
positionAllowed _ RightHand=False
positionAllowed _ LeftHand=False