Map.lua 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343
  1. -- Copyright (c) 2011-2012 Casey Baxter
  2. -- See LICENSE file for details
  3. ---------------------------------------------------------------------------------------------------
  4. -- -= Map =-
  5. ---------------------------------------------------------------------------------------------------
  6. -- Setup
  7. -- Import the other classes
  8. local PATH = (...):gsub("[\\/]", ""):match(".+%.") or ''
  9. local Tile = require(PATH .. "Tile")
  10. local TileSet = require(PATH .. "TileSet")
  11. local TileLayer = require(PATH .. "TileLayer")
  12. local Object = require(PATH .. "Object")
  13. local ObjectLayer = require(PATH .. "ObjectLayer")
  14. -- Localize some functions so they are faster
  15. local love = love
  16. local table = table
  17. local ceil = math.ceil
  18. local floor = math.floor
  19. -- Make our map class
  20. local Map = {class = "Map"}
  21. Map.__index = Map
  22. ---------------------------------------------------------------------------------------------------
  23. -- Returns a new map
  24. function Map:new(name, width, height, tileWidth, tileHeight, orientation, path, prop)
  25. -- Our map
  26. local map = setmetatable({}, Map)
  27. prop = prop or {}
  28. -- Public:
  29. map.name = name or "Unnamed Map" -- Name of the map
  30. map.width = width or 0 -- Width of the map in tiles
  31. map.height = height or 0 -- Height of the map in tiles
  32. map.tileWidth = tileWidth or 0 -- Width in pixels of each tile
  33. map.tileHeight = tileHeight or 0 -- Height in pixels of each tile
  34. map.orientation = orientation or "orthogonal" -- Type of map. orthogonal or isometric
  35. map.properties = prop or {} -- Properties of the map set by Tiled
  36. map.useSpriteBatch = prop.atl_useSpriteBatch -- True = TileLayers use sprite batches
  37. map.visible = true -- False = the map will not be drawn
  38. map.drawObjects = prop.atl_drawObjects -- True = object layers will be drawn
  39. map.viewX = prop.atl_viewX or 0 -- X coord of the viewing screen.
  40. map.viewY = prop.atl_viewY or 0 -- Y coord of the viewing screen.
  41. map.viewW = prop.atl_viewW or love.graphics.getWidth() -- The width of the viewing screen
  42. map.viewH = prop.atl_viewH or love.graphics.getHeight() -- The height of the viewing screen
  43. map.viewScaling = 1 -- The game scaling
  44. map.viewPadding = 10 -- Padding for the view
  45. map.offsetX = prop.atl_offsetX or 0 -- Drawing offset X
  46. map.offsetY = prop.atl_offsetY or 0 -- Drawing offset Y
  47. map.layers = {} -- Layers of the map indexed by name
  48. map.tilesets = {} -- Tilesets indexed by name
  49. map.tiles = {} -- Tiles indexed by id
  50. map.layerOrder = {} -- The order of the layers. Callbacks are called in this order.
  51. -- Private:
  52. map._widestTile = 0 -- The widest tile on the map.
  53. map._highestTile = 0 -- The tallest tile on the map.
  54. map._forceRedraw = false -- If true then the next redraw is forced
  55. map._previousUseSpriteBatch = false -- The previous useSpiteBatch.
  56. map._tileClipboard = nil -- The value that stored for tile copying and pasting.
  57. map._directory = path -- The directory the map is in
  58. -- Return the new map
  59. return map
  60. end
  61. ---------------------------------------------------------------------------------------------------
  62. -- Creates a new tileset and adds it to the map. The map will then auto-update its tiles.
  63. function Map:newTileSet(...)
  64. local tileset = TileSet:new(...)
  65. local name = tileset.name
  66. if self.tilesets[name] then
  67. error( string.format("Map:newTileSet - A tile set named \"%s\" already exists.", name) )
  68. end
  69. self.tilesets[name] = TileSet:new(...)
  70. self:updateTiles()
  71. return tileset
  72. end
  73. ---------------------------------------------------------------------------------------------------
  74. -- Creates a new TileLayer and adds it to the map. The position parameter is the position to insert
  75. -- the layer into the layerOrder.
  76. function Map:newTileLayer(position, ...)
  77. local layer = TileLayer:new(self, ...)
  78. local name = layer.name
  79. if self.layers[name] then
  80. error( string.format("Map:newTileLayer - A layer named \"%s\" already exists.", name) )
  81. end
  82. self.layers[name] = layer
  83. table.insert(self.layerOrder, position or #self.layerOrder + 1, layer)
  84. return layer
  85. end
  86. ---------------------------------------------------------------------------------------------------
  87. -- Creates a new ObjectLayer and inserts it into the map
  88. function Map:newObjectLayer(position, ...)
  89. local layer = ObjectLayer:new(self, ...)
  90. local name = layer.name
  91. if self.layers[name] then
  92. error( string.format("Map:newObjectLayer - A layer named \"%s\" already exists.", name) )
  93. end
  94. self.layers[name] = layer
  95. table.insert(self.layerOrder, position or #self.layerOrder + 1, layer)
  96. return layer
  97. end
  98. ---------------------------------------------------------------------------------------------------
  99. -- Add a custom layer to the map. You can include a predefined layer table or one will be created.
  100. function Map:newCustomLayer(name, position, layer)
  101. if self.layers[name] then
  102. error( string.format("Map:newCustomLayer - The layer name \"%s\" already exists.", name) )
  103. end
  104. self.layers[name] = layer or {name=name}
  105. self.layers[name].class = "CustomLayer"
  106. table.insert(self.layerOrder, position or #self.layerOrder + 1, self.layers[name])
  107. return self.layers[name]
  108. end
  109. ---------------------------------------------------------------------------------------------------
  110. -- Cuts tiles out of tilesets and stores them in the tiles tables under their id
  111. -- Call this after the tilesets are set up
  112. function Map:updateTiles()
  113. self.tiles = {}
  114. self._widestTile = 0
  115. self._highestTile = 0
  116. for _, ts in pairs(self.tilesets) do
  117. if ts.tileWidth > self._widestTile then self._widestTile = ts.tileWidth end
  118. if ts.tileHeight > self._highestTile then self._highestTile = ts.tileHeight end
  119. for id, val in pairs(ts:getTiles()) do
  120. self.tiles[id] = val
  121. end
  122. end
  123. end
  124. ---------------------------------------------------------------------------------------------------
  125. -- Forces the map to redraw the sprite batches.
  126. function Map:forceRedraw()
  127. self._forceRedraw = true
  128. end
  129. ---------------------------------------------------------------------------------------------------
  130. -- Performs a callback on all map layers.
  131. local layer
  132. function Map:callback(cb, ...)
  133. if cb == "draw" then self:_updateTileRange() end
  134. for i = 1, #self.layerOrder do
  135. layer = self.layerOrder[i]
  136. if layer[cb] then layer[cb](layer, ...) end
  137. end
  138. end
  139. ---------------------------------------------------------------------------------------------------
  140. -- Draw the map.
  141. function Map:draw()
  142. if self.visible then self:callback("draw") end
  143. end
  144. ---------------------------------------------------------------------------------------------------
  145. -- Returns the position of the layer inside the map's layerOrder. Can be the layer name or table.
  146. function Map:layerPosition(layer)
  147. if type(layer) == "string" then layer = self.layers[layer] end
  148. for i = 1,#self.layerOrder do
  149. if self.layerOrder[i] == layer then return i end
  150. end
  151. end
  152. ---------------------------------------------------------------------------------------------------
  153. -- Returns the position of the layer inside the map's layerOrder. The passed layers can be the
  154. -- layer name, the layer tables themselves, or the layer positions.
  155. function Map:swapLayers(layer1, layer2)
  156. if type(layer1) ~= "number" then layer1 = self:layerPosition(layer1) end
  157. if type(layer2) ~= "number" then layer2 = self:layerPosition(layer2) end
  158. local bubble = self.layers[layer1]
  159. self.layers[layer1] = self.layers[layer2]
  160. self.layers[layer2] = bubble
  161. end
  162. ---------------------------------------------------------------------------------------------------
  163. -- Removes a layer from the map
  164. function Map:removeLayer(layer)
  165. if type(layer) ~= "number" then layer = self:layerPosition(layer) end
  166. layer = table.remove(self.layerOrder, layer)
  167. self.layers[layer.name] = nil
  168. return layer
  169. end
  170. ---------------------------------------------------------------------------------------------------
  171. -- Turns an isometric location into a world location. The unit length for isometric tiles is always
  172. -- the map's tileHeight. This is both for width and height.
  173. local h, tw, th
  174. function Map:fromIso(x, y)
  175. h, tw, th = self.height, self.tileWidth, self.tileHeight
  176. return ((x-y)/th + h - 1)*tw/2, (x+y)/2
  177. end
  178. ---------------------------------------------------------------------------------------------------
  179. -- Turns a world location into an isometric location
  180. local ix, iy
  181. function Map:toIso(a, b)
  182. a, b = a or 0, b or 0
  183. h, tw, th = self.height, self.tileWidth, self.tileHeight
  184. ix = b - (h-1)*th/2 + (a*th)/tw
  185. iy = 2*b - ix
  186. return ix, iy
  187. end
  188. ---------------------------------------------------------------------------------------------------
  189. -- Sets the draw range
  190. function Map:setDrawRange(x,y,w,h)
  191. self.viewX, self.viewY, self.viewW, self.viewH = x, y, w, h
  192. self.viewPadding = 0
  193. self.viewScaling = 1
  194. end
  195. ---------------------------------------------------------------------------------------------------
  196. -- Automatically sets the draw range to fit the display
  197. function Map:autoDrawRange(tx, ty, scale, pad)
  198. tx, ty, scale, pad = tx or 0, ty or 0, scale or 1, pad or 0
  199. self.viewX = -tx
  200. self.viewY = -ty
  201. self.viewW = love.graphics.getWidth()
  202. self.viewH = love.graphics.getHeight()
  203. self.viewScaling = scale > 0.001 and scale or 0.001
  204. self.viewPadding = pad
  205. end
  206. ---------------------------------------------------------------------------------------------------
  207. -- Returns the normal draw range
  208. function Map:getDrawRange()
  209. return self.viewX - self.viewPadding, self.viewY - self.viewPadding,
  210. self.viewW/self.viewScaling + self.viewPadding*2,
  211. self.viewH/self.viewScaling + self.viewPadding*2
  212. end
  213. ----------------------------------------------------------------------------------------------------
  214. -- Private Functions
  215. ----------------------------------------------------------------------------------------------------
  216. -- This is an internal function used to update the map's _tileRange, _previousTileRange, and
  217. -- _specialRedraw
  218. local x1, y1, x2, y2, heightOffset, widthOffset, tr, ptr, layer
  219. function Map:_updateTileRange()
  220. -- Offset to make sure we can always draw the highest and widest tile
  221. heightOffset = self._highestTile - self.tileHeight
  222. widthOffset = self._widestTile - self.tileWidth
  223. -- Go through each layer
  224. for i = 1,#self.layerOrder do
  225. layer = self.layerOrder[i]
  226. -- If the layer is a TileLayer
  227. if layer.class == "TileLayer" then
  228. -- Get the draw range.
  229. x1 = self.viewX * layer.parallaxX - self.viewPadding + layer.offsetX
  230. y1 = self.viewY * layer.parallaxY - self.viewPadding + layer.offsetY
  231. x2 = self.viewW/self.viewScaling + self.viewPadding*2
  232. y2 = self.viewH/self.viewScaling + self.viewPadding*2
  233. -- Apply the offset
  234. x1 = x1 - self.offsetX - layer.offsetX
  235. y1 = y1 - self.offsetY - layer.offsetY
  236. -- Calculate the _tileRange for orthogonal tiles
  237. if self.orientation == "orthogonal" then
  238. -- Limit the drawing range. We must make sure we can draw the tiles that are bigger
  239. -- than the self's tileWidth and tileHeight.
  240. if x1 and y1 and x2 and y2 then
  241. x2 = ceil(x2/self.tileWidth)
  242. y2 = ceil((y2+heightOffset)/self.tileHeight)
  243. x1 = floor((x1-widthOffset)/self.tileWidth)
  244. y1 = floor(y1/self.tileHeight)
  245. -- Make sure that we stay within the boundry of the map
  246. x1 = x1 > 0 and x1 or 0
  247. y1 = y1 > 0 and y1 or 0
  248. x2 = x2 < self.width and x2 or self.width - 1
  249. y2 = y2 < self.height and y2 or self.height - 1
  250. else
  251. -- If the drawing range isn't defined then we draw all the tiles
  252. x1, y1, x2, y2 = 0, 0, self.width-1, self.height-1
  253. end
  254. -- Calculate the _tileRange for isometric tiles.
  255. else
  256. -- If the drawRange is set
  257. if x1 and y1 and x2 and y2 then
  258. x1, y1 = self:toIso(x1-self._widestTile,y1)
  259. x1, y1 = ceil(x1/self.tileHeight), ceil(y1/self.tileHeight)-1
  260. x2 = ceil((x2+self._widestTile)/self.tileWidth)
  261. y2 = ceil((y2+heightOffset)/self.tileHeight)
  262. -- else draw everything
  263. else
  264. x1 = 0
  265. y1 = 0
  266. x2 = self.width - 1
  267. y2 = self.height - 1
  268. end
  269. end
  270. -- Assign the new values to the tile range
  271. tr, ptr = layer._tileRange, layer._previousTileRange
  272. ptr[1], ptr[2], ptr[3], ptr[4] = tr[1], tr[2], tr[3], tr[4]
  273. tr[1], tr[2], tr[3], tr[4] = x1, y1, x2, y2
  274. -- If the tile range or useSpriteBatch is different than the last frame then we need to
  275. -- update its sprite batches.
  276. layer._redraw = self.useSpriteBatch ~= self._previousUseSpriteBatch or
  277. self._forceRedraw or
  278. tr[1] ~= ptr[1] or
  279. tr[2] ~= ptr[2] or
  280. tr[3] ~= ptr[3] or
  281. tr[4] ~= ptr[4]
  282. end
  283. end
  284. -- Set the previous useSpritebatch
  285. self._previousUseSpriteBatch = self.useSpriteBatch
  286. -- Reset the forced special redraw
  287. self._forceRedraw = false
  288. end
  289. ---------------------------------------------------------------------------------------------------
  290. -- Calling the map as a function will return the layer
  291. function Map:__call(layerName)
  292. return self.layers[layerName]
  293. end
  294. ---------------------------------------------------------------------------------------------------
  295. -- Returns the Map class
  296. return Map