module RPGEngine.Data.Level -- Everything is exported where import GHC.IO (unsafePerformIO) import System.Directory (getDirectoryContents) import RPGEngine.Input.Core (ListSelector(..)) import RPGEngine.Data (Action(..), Level (..), Physical (..), Direction (..), Entity (..), Game (..), Item (..), Player (..), State (..), X, Y, Layout, Condition (InventoryFull, InventoryContains, Not, AlwaysFalse), ItemId) import RPGEngine.Config (levelFolder, inventorySize) import Data.Foldable (find) ------------------------------ Exported ------------------------------ -- Find first position of a Physical -- Graceful exit by giving Nothing if there is nothing found. findFirstOf :: Level -> Physical -> Maybe (X, Y) findFirstOf l@Level{ index = index } physical = try where matches = filter (\(x, y, v) -> v == physical) index try | not (null matches) = Just $ (\(x, y, _) -> (x, y)) $ head matches | otherwise = Nothing -- What is located at a given position in the level? findAt :: (X, Y) -> Level -> Physical findAt pos lvl@Level{ index = index } = try where matches = map (\(_, _, v) -> v) $ filter (\(x, y, v) -> (x, y) == pos) index try | not (null matches) = head matches | otherwise = Void hasAt :: (X, Y) -> Level -> Maybe (Either Item Entity) hasAt pos level = match firstItem firstEntity where match :: Maybe Item -> Maybe Entity -> Maybe (Either Item Entity) match (Just a) _ = Just $ Left a match _ (Just a) = Just $ Right a match _ _ = Nothing firstEntity = find ((== pos) . getECoord) $ entities level getECoord e = (entityX e, entityY e) firstItem = find ((== pos) . getICoord) $ items level getICoord i = (itemX i, itemY i) getWithId :: String -> Level -> Maybe (Either Item Entity) getWithId id level = match firstItem firstEntity where match :: Maybe Item -> Maybe Entity -> Maybe (Either Item Entity) match (Just a) _ = Just $ Left a match _ (Just a) = Just $ Right a match _ _ = Nothing firstEntity = find ((== id) . entityId) $ entities level firstItem = find ((== id) . itemId) $ items level directionOffsets :: Direction -> (X, Y) directionOffsets North = ( 0, 1) directionOffsets East = ( 1, 0) directionOffsets South = ( 0, -1) directionOffsets West = (-1, 0) directionOffsets Stay = ( 0, 0) getLevelList :: [FilePath] getLevelList = drop 2 $ unsafePerformIO $ getDirectoryContents levelFolder -- Get the actions of either an entity or an item getActions :: Either Item Entity -> [([Condition], Action)] getActions (Left item) = itemActions item getActions (Right entity) = entityActions entity getActionText :: Action -> String getActionText Leave = "Leave" getActionText (RetrieveItem _) = "Pick up" getActionText (UseItem _) = "Use item" getActionText (IncreasePlayerHp _) = "Take a healing potion" getActionText (DecreaseHp _ used) = "Attack using " ++ used getActionText _ = "ERROR" -- Filter based on the conditions, keep only the actions of which the -- conditions are met. -- Should receive a Playing state filterActions :: State -> [([Condition], Action)] -> [Action] filterActions _ [] = [] filterActions s (entry:others) = met entry $ filterActions s others where met (conditions, action) l | all (meetsCondition s) conditions = action:l | otherwise = l -- Check if a condition is met or not. meetsCondition :: State -> Condition -> Bool meetsCondition s InventoryFull = isInventoryFull $ player s meetsCondition s (InventoryContains id) = inventoryContains id $ player s meetsCondition s (Not condition) = not $ meetsCondition s condition meetsCondition _ AlwaysFalse = False -- Check if the inventory of the player is full. isInventoryFull :: Player -> Bool isInventoryFull p = inventorySize <= length (inventory p) -- Check if the inventory of the player contains an item. inventoryContains :: ItemId -> Player -> Bool inventoryContains id p = any ((== id) . itemId) $ inventory p -- Retrieve an item from inventory itemFromInventory :: ItemId -> [Item] -> (Maybe Item, [Item]) itemFromInventory iid list = (match, filteredList) where match = find ((== iid) . itemId) list filteredList = filter ((/= iid) . itemId) list