TileLayer.lua 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442
  1. -- Copyright (c) 2011-2012 Casey Baxter
  2. -- See LICENSE file for details
  3. ---------------------------------------------------------------------------------------------------
  4. -- -= TileLayer =-
  5. ---------------------------------------------------------------------------------------------------
  6. -- Setup
  7. --local PATH = (...):gsub("[\\/]", ""):match(".+%.") or ''
  8. local math = math
  9. local type = type
  10. local love = love
  11. --local Grid = require(PATH .. "Grid")
  12. local TileLayer = {}
  13. TileLayer.class = "TileLayer"
  14. TileLayer.__index = TileLayer
  15. --TileLayer.__call = Grid.__call
  16. ----------------------------------------------------------------------------------------------------
  17. -- GRID INTERFACE
  18. function TileLayer:get(x,y)
  19. return self.cells[x] and self.cells[x][y]
  20. end
  21. TileLayer.__call = TileLayer.get
  22. function TileLayer:set(x,y,value)
  23. if not self.cells[x] then self.cells[x] = {} end
  24. self.cells[x][y] = value
  25. end
  26. function TileLayer:clear()
  27. self.cells = {}
  28. end
  29. function TileLayer:rectangle(startX, startY, width, height, includeNil)
  30. local x, y = startX, startY
  31. return function()
  32. while y <= startY + height do
  33. while x <= startX + width do
  34. x = x+1
  35. if self(x-1,y) ~= nil or includeNil then
  36. return x-1, y, self(x-1,y)
  37. end
  38. end
  39. x = startX
  40. y = y+1
  41. end
  42. return nil
  43. end
  44. end
  45. ----------------------------------------------------------------------------------------------------
  46. -- Returns a new TileLayer
  47. function TileLayer.init(o)
  48. local tl = setmetatable(o, TileLayer)
  49. tl.cells = {}
  50. tl.parallaxX = tl.properties.parallaxx or 1 -- The horizontal speed of the parallax. 1 is normal
  51. tl.parallaxY = tl.properties.parallaxy or 1 -- The vertical speed of the parallax. 1 is normal
  52. --tl.offsetX = 0 -- Drawing offset X
  53. --tl.offsetY = 0 -- Drawing offset Y
  54. -- Private:
  55. tl._redraw = true -- If true then the layer needs to redraw tis sprite batches.
  56. tl._tileRange = {0,0,0,0} -- Keeps the drawn tile range for the layer
  57. tl._previousTileRange = {0,0,0,0} -- Previous _tileRange
  58. tl._batches = {} -- Keeps track of the sprite batches for each tileset
  59. tl._flippedTiles = {} -- Stores the flipped tile locations.
  60. -- 1 = flipped X, 2 = flipped Y, 3 = both
  61. tl._afterTileFunction = nil -- A function that handles drawing things after individual
  62. -- tiles. This will not work with SpriteBatches.
  63. tl._afterTileArgs = nil -- Starting arguments for the after tile function
  64. tl._previousUseSpriteBatch = false -- The previous useSpriteBatch. If this is different then we
  65. -- need to force a special redraw
  66. return tl
  67. end
  68. ----------------------------------------------------------------------------------------------------
  69. -- Clears the tile layer of its after tile function.
  70. function TileLayer:clearAfterTileFunction()
  71. self._afterTileFunction = nil
  72. self._afterTileArgs = nil
  73. end
  74. ----------------------------------------------------------------------------------------------------
  75. -- Sets a function that will be called after every tile. funct(layer, x, y, drawX, drawY ...)
  76. function TileLayer:setAfterTileFunction(funct, ...)
  77. self._afterTileFunction = funct
  78. local args = {...}
  79. if #args > 0 then self._afterTileArgs = args end
  80. end
  81. ----------------------------------------------------------------------------------------------------
  82. -- These are used in TileLayer:draw() but since that function is called so often we'll define them
  83. -- outside to prevent them from being created and destroyed all the time.
  84. local map, tile, tiles, postDraw, useSpriteBatch, tile, width, height
  85. local at, drawX, drawY, flipX, flipY, r, g, b, a, halfW, halfH, rot
  86. local x1, y1, x2, y2
  87. -- Draws the TileLayer.
  88. function TileLayer:draw() --FIXME: holycow!
  89. -- Early exit of the layer is not visible
  90. if not self.visible then return end
  91. -- We access these a lot so we'll shorted them a bit.
  92. map, tiles = self.map, self.map.tiles
  93. postDraw = self.postDraw
  94. -- If useSpriteBatch was changed then we need to force the sprite batches to redraw.
  95. if self.useSpriteBatch ~= self._previousUseSpriteBatch then map:forceRedraw() end
  96. -- Set the previous useSpriteBatch
  97. self._previousUseSpriteBatch = self.useSpriteBatch
  98. -- If useSpriteBatch is set for this layer then use that, otherwise use the map's setting.
  99. useSpriteBatch = self.useSpriteBatch ~= nil and self.useSpriteBatch or map.useSpriteBatch
  100. -- We'll blend the set alpha in with the current alpha
  101. r,g,b,a = love.graphics.getColor()
  102. love.graphics.setColor(r,g,b, a*self.opacity)
  103. -- Clear sprite batches if the screen has changed.
  104. if self._redraw and useSpriteBatch then
  105. for k,v in pairs(self._batches) do
  106. v:clear()
  107. end
  108. end
  109. -- Get the tile range
  110. x1, y1, x2, y2 = self._tileRange[1], self._tileRange[2], self._tileRange[3], self._tileRange[4]
  111. -- Translate for the parallax
  112. if self.parallaxX ~= 1 or self.parallaxY ~= 1 then
  113. love.graphics.push()
  114. love.graphics.translate(math.floor(map.viewX - map.viewX*self.parallaxX),
  115. math.floor(map.viewY - map.viewY*self.parallaxY))
  116. end
  117. -- Only draw if we're not using sprite batches or we need to update the sprite batches.
  118. if self._redraw or not useSpriteBatch then
  119. -- Bind the sprite batches
  120. if useSpriteBatch then
  121. --for k, batch in pairs(self._batches) do
  122. -- batch:bind()
  123. --end
  124. end
  125. -- Orthogonal tiles
  126. if map.orientation == "orthogonal" then
  127. -- Go through each tile
  128. for x,y,tile in self:rectangle(x1,y1,x2,y2) do
  129. -- Get the half-width and half-height
  130. halfW, halfH = tile.width*0.5, tile.height*0.5
  131. -- Draw the tile from the bottom left corner
  132. drawX, drawY = (x)*map.tileWidth, (y+1)*map.tileHeight
  133. -- Apply the offset
  134. drawX = drawX - map.offsetX - self.offsetX
  135. drawY = drawY - map.offsetY - self.offsetY
  136. -- Get the flipped tiles
  137. local ft = self._flippedTiles[x]
  138. ft = ft and ft[y]
  139. if ft then
  140. rot = (ft % 2) == 1 and true or false
  141. flipY = (ft % 4) >= 2 and -1 or 1
  142. flipX = ft >= 4 and -1 or 1
  143. if rot then flipX, flipY = -flipY, flipX end
  144. else
  145. rot, flipX, flipY = false, 1, 1
  146. end
  147. -- If we are using spritebatches
  148. if useSpriteBatch then
  149. -- If we dont have a spritebatch for the current tile's tileset then make one
  150. if not self._batches[tile.tileset] then
  151. self._batches[tile.tileset] = love.graphics.newSpriteBatch(
  152. tile.tileset.image, map.width * map.height)
  153. --self._batches[tile.tileset]:bind()
  154. end
  155. -- Add the quad to the spritebatch
  156. self._batches[tile.tileset]:add(tile.quad, drawX + halfW,
  157. drawY - halfH,
  158. rot and math.pi*1.5 or 0,
  159. flipX, flipY, halfW, halfH)
  160. -- If we are not using spritebatches
  161. else
  162. -- Draw the tile
  163. tile:draw(drawX + halfW,
  164. drawY - halfH,
  165. rot and math.pi*1.5 or 0,
  166. flipX, flipY, halfW, halfH)
  167. -- Call the after tile function
  168. if self._afterTileFunction then
  169. if self._afterTileArgs then
  170. self._afterTileFunction(self, x, y, drawX, drawY,
  171. unpack(self._afterTileArgs))
  172. else
  173. self._afterTileFunction(self, x, y, drawX, drawY)
  174. end
  175. end
  176. end
  177. end
  178. end
  179. -- Isometric tiles
  180. if map.orientation == "isometric" then
  181. local x,y
  182. -- Get the starting x drawing location
  183. local draw_start = map.height * map.tileWidth/2
  184. -- Draw each tile starting from the top left tile. Make sure we have enough
  185. -- room to draw the widest and tallest tile in the map.
  186. for down=0,y2 do
  187. for layer=0,1 do
  188. for right=0,x2 do
  189. x = x1 + right + down + layer - 1
  190. y = y1 - right + down - 1
  191. -- If there is a tile row
  192. tile = self(x,y)
  193. if tile then
  194. -- Check and see if the tile is flipped
  195. local ft = self.__flippedTiles[x]
  196. ft = ft and ft[y]
  197. if ft then
  198. rot = (ft % 2) == 1 and true or false
  199. flipY = (ft % 4) >= 2 and -1 or 1
  200. flipX = ft >= 4 and -1 or 1
  201. if rot then flipX, flipY = -flipY, flipX end
  202. else
  203. rot, flipX, flipY = false, 1, 1
  204. end
  205. -- Get the tile
  206. --tile = self(x,y)
  207. -- If the tile exists then draw the tile
  208. --if tile then
  209. -- Get the half-width and half-height
  210. halfW, halfH = tile.width*0.5, tile.height*0.5
  211. -- Get the tile draw location
  212. drawX = math.floor(draw_start + map.tileWidth/2 * (x - y-2))
  213. drawY = math.floor(map.tileHeight/2 * (x + y+2))
  214. -- Apply the offset
  215. drawX = drawX - map.offsetX - self.offsetX
  216. drawY = drawY - map.offsetY - self.offsetY
  217. -- Using sprite batches
  218. if useSpriteBatch then
  219. -- If we dont have a spritebatch for the current tile's tileset
  220. -- then make one
  221. if not self._batches[tile.tileset] then
  222. self._batches[tile.tileset] = love.graphics.newSpriteBatch(
  223. tile.tileset.image,
  224. map.width * map.height)
  225. -- Bind the sprite batch
  226. self._batches[tile.tileset]:bind()
  227. end
  228. -- Add the tile to the sprite batch.
  229. self._batches[tile.tileset]:add(tile.quad, drawX + halfW +
  230. (rot and halfW or 0),
  231. drawY-halfH+(rot and halfW or 0),
  232. rot and math.pi*1.5 or 0,
  233. flipX, flipY, halfW, halfH)
  234. -- Not using sprite batches
  235. else
  236. tile:draw(drawX + halfW + (rot and halfW or 0),
  237. drawY - halfH + (rot and halfW or 0),
  238. rot and math.pi*1.5 or 0,
  239. flipX, flipY, halfW, halfH)
  240. -- Call the after tile function
  241. if self._afterTileFunction then
  242. if self._afterTileArgs then
  243. self._afterTileFunction(self, x, y, drawX, drawY,
  244. unpack(self._afterTileArgs))
  245. else
  246. self._afterTileFunction(self, x, y, drawX, drawY)
  247. end
  248. end
  249. end
  250. --end
  251. end
  252. end
  253. end
  254. end
  255. end
  256. -- Unbind the sprite batches
  257. if useSpriteBatch then
  258. --for k, batch in pairs(self._batches) do
  259. -- batch:unbind()
  260. --end
  261. end
  262. end
  263. -- We finished redrawing
  264. self._redraw = false
  265. -- If sprite batches are turned on then render them
  266. if useSpriteBatch then
  267. for k, batch in pairs(self._batches) do
  268. love.graphics.draw(batch)
  269. end
  270. end
  271. -- If we applied a translation for our parallax then remove it
  272. if self.parallaxX ~= 1 or self.parallaxY ~= 1 then
  273. love.graphics.pop()
  274. end
  275. -- Change the color back
  276. love.graphics.setColor(r,g,b,a)
  277. end
  278. ----------------------------------------------------------------------------------------------------
  279. -- This copies a tile so that you can paste it in another spot. The pasted tile will keep the
  280. -- rotation and flipped status. You can copy and paste between layers.
  281. local flippedVal = 2^29
  282. function TileLayer:tileCopy(x,y)
  283. if not self(x,y) then
  284. self.map._tileClipboard = 0
  285. else
  286. local ft = self._flippedTiles[x]
  287. self.map._tileClipboard = self(x,y).id + ((ft and ft[y]) or 0) * flippedVal
  288. end
  289. end
  290. ----------------------------------------------------------------------------------------------------
  291. -- Paste a copied tile.
  292. function TileLayer:tilePaste(x,y)
  293. self._redraw = true
  294. if not self.map._tileClipboard then
  295. error("TileLayer:tilePaste() - A tile must be copied with tileCopy() before pasting")
  296. end
  297. local clip = self.map._tileClipboard
  298. if clip / flippedVal > 0 then
  299. local fts = self._flippedTiles
  300. if not fts[x] then fts[x] = {} end
  301. fts[x][y] = math.floor(clip / flippedVal)
  302. end
  303. self:set(x, y, self.map.tiles[clip % flippedVal])
  304. end
  305. ----------------------------------------------------------------------------------------------------
  306. -- Flip the tile's X. If doFlip is not specified then the flip is toggled.
  307. function TileLayer:tileFlipX(x, y, doFlip)
  308. local fts = self._flippedTiles
  309. if not fts[x] then fts[x] = {} end
  310. self._redraw = true
  311. local flip = fts[x][y] or 0
  312. if doFlip ~= false and flip < 4 then
  313. flip = flip + 4
  314. elseif doFlip ~= true and flip >= 4 then
  315. flip = flip - 4
  316. end
  317. fts[x][y] = flip ~= 0 and flip or nil
  318. end
  319. ----------------------------------------------------------------------------------------------------
  320. -- Flip the tile's Y. If doFlip is not specified then the flip is toggled.
  321. function TileLayer:tileFlipY(x, y, doFlip)
  322. local fts = self._flippedTiles
  323. if not fts[x] then fts[x] = {} end
  324. self.redraw = true
  325. local flip = fts[x][y] or 0
  326. if doFlip ~= false and flip % 4 < 2 then
  327. flip = flip + 2
  328. elseif doFlip ~= true and flip % 4 >= 2 then
  329. flip = flip - 2
  330. end
  331. fts[x][y] = flip ~= 0 and flip or nil
  332. end
  333. ----------------------------------------------------------------------------------------------------
  334. -- Rotate the tile.
  335. function TileLayer:tileRotate(x, y, rot)
  336. local fts = self._flippedTiles
  337. if not fts[x] then fts[x] = {} end
  338. local flip = fts[x][y] or 0
  339. if rot then flip = rot % 8
  340. elseif flip == 0 then flip = 5
  341. elseif flip == 1 then flip = 4
  342. elseif flip == 2 then flip = 1
  343. elseif flip == 3 then flip = 0
  344. elseif flip == 4 then flip = 7
  345. elseif flip == 5 then flip = 6
  346. elseif flip == 6 then flip = 3
  347. elseif flip == 7 then flip = 2
  348. end
  349. fts[x][y] = flip ~= 0 and flip or 0
  350. end
  351. ----------------------------------------------------------------------------------------------------
  352. -- Private
  353. ----------------------------------------------------------------------------------------------------
  354. local FLIPPED = 0x20000000
  355. function TileLayer:_populate(t)
  356. self:clear()
  357. local map = self.map
  358. local width, height = map.width, map.height
  359. local tiles = map.tiles
  360. local tid
  361. for x = 0, width - 1 do
  362. for y = 0, height - 1 do
  363. tid = t[width * y + x + 1]
  364. if tid then
  365. if tid >= FLIPPED then
  366. local fts = self._flippedTiles
  367. if not fts[x] then fts[x] = {} end
  368. fts[x][y] = math.floor(tid / FLIPPED)
  369. tid = tid % FLIPPED
  370. end
  371. self:set(x, y, tiles[tid])
  372. end
  373. end
  374. end
  375. end
  376. return TileLayer