module Game.Map ( Map, load, render, isBlocked, ) where import Control.Monad import qualified Game.Utils as U import qualified SDL import Text.JSON import Text.JSON.Types data Tileset = Tileset { tsCols :: Int, tsWidth :: Int, tsHeight :: Int, tsFirstGid :: Int } deriving (Show) data TileLayer = TileLayer { tlName :: String, tlTiles :: [Int] } deriving (Show) data MapData = MapData { mWidth :: Int, mHeight :: Int, mTileset :: Tileset, mLayers :: [TileLayer] } deriving (Show) data Map = Map MapData SDL.Texture instance JSON Tileset where showJSON = undefined readJSON (JSArray [JSObject obj]) = do Tileset <$> valFromObj "columns" obj <*> valFromObj "tilewidth" obj <*> valFromObj "tileheight" obj <*> valFromObj "firstgid" obj readJSON _ = mzero instance JSON TileLayer where showJSON = undefined readJSON (JSObject obj) = case get_field obj "type" of Just "tilelayer" -> TileLayer <$> valFromObj "name" obj <*> valFromObj "data" obj _ -> Error "unsupported layer type" readJSON _ = mzero instance JSON MapData where showJSON = undefined readJSON (JSObject obj) = MapData <$> valFromObj "width" obj <*> valFromObj "height" obj <*> valFromObj "tilesets" obj <*> valFromObj "layers" obj readJSON _ = mzero -- | Loads a map from a JSON file. load :: String -> SDL.Texture -> IO Map load filename tex = do d <- readFile filename case decode d :: Result MapData of Ok s -> pure $ Map s tex Error e -> error e -- | Check for collision detection vs "Blocked" TileLayer that MUST be last layer. -- x and y in pixels. isBlocked :: Map -> Int -> Int -> Bool isBlocked (Map (MapData width _ ts layers) _) x y = tiles !! ((x `div` tw) + (y `div` th) * width) >= firstgid where tiles = tlTiles (last layers) tw = tsWidth ts th = tsHeight ts firstgid = tsFirstGid ts -- | Renders a map. render :: SDL.Renderer -> Map -> IO () render renderer (Map mapData tex) = do mapM_ ( \layer -> mapM_ ( \(x, y) -> renderTile x y $ tlTiles layer !! (x + (y * mWidth mapData)) ) index ) (init $ mLayers mapData) where mw = mWidth mapData mh = mHeight mapData index = [(x, y) | x <- [0 .. mw - 1], y <- [0 .. mh - 1]] ts = mTileset mapData firstgid = tsFirstGid ts cols = tsCols ts tileWidth = tsWidth ts tileHeight = tsHeight ts renderTile :: Int -> Int -> Int -> IO () renderTile x y tile | tile < firstgid = pure () | otherwise = do let tx = (tile - firstgid) `rem` cols ty = (tile - firstgid) `div` cols src = U.rect (tx * tileWidth) (ty * tileHeight) tileWidth tileHeight dst = U.rect (x * tileWidth) (y * tileHeight) tileWidth tileHeight SDL.copy renderer tex (Just src) (Just dst)