aboutsummaryrefslogtreecommitdiff
path: root/src/Game/Map.hs
blob: 7866f9792cfcdd853aecfdb0ab2847713a4cf4bd (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
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)