From 127063a061759c13759431acf36d4e78bae150ba Mon Sep 17 00:00:00 2001 From: Tim Anema Date: Tue, 26 Apr 2016 09:23:11 -0400 Subject: [PATCH] fixes for postshaders and most of the examples to use the new apis --- examples/postshaders.lua | 6 +- examples/simple_tiled_impl.lua | 2 +- examples/vendor/sti/.gitattributes | 22 - examples/vendor/sti/.gitignore | 215 --- examples/vendor/sti/CHANGELOG.md | 388 ++++++ examples/vendor/sti/LICENSE.md | 26 + examples/vendor/sti/README.md | 96 ++ examples/vendor/sti/framework/corona.lua | 56 - examples/vendor/sti/framework/love.lua | 62 - examples/vendor/sti/framework/pure.lua | 56 - examples/vendor/sti/init.lua | 103 +- examples/vendor/sti/map.lua | 1551 +++++++++++++--------- examples/vendor/sti/plugins/box2d.lua | 349 +++++ examples/vendor/sti/plugins/bump.lua | 103 ++ lib/body.lua | 5 +- lib/postshader.lua | 2 +- 16 files changed, 1930 insertions(+), 1112 deletions(-) delete mode 100755 examples/vendor/sti/.gitattributes delete mode 100755 examples/vendor/sti/.gitignore create mode 100755 examples/vendor/sti/CHANGELOG.md create mode 100755 examples/vendor/sti/LICENSE.md create mode 100755 examples/vendor/sti/README.md delete mode 100755 examples/vendor/sti/framework/corona.lua delete mode 100755 examples/vendor/sti/framework/love.lua delete mode 100755 examples/vendor/sti/framework/pure.lua create mode 100755 examples/vendor/sti/plugins/box2d.lua create mode 100755 examples/vendor/sti/plugins/bump.lua diff --git a/examples/postshaders.lua b/examples/postshaders.lua index e15f947..554ca06 100644 --- a/examples/postshaders.lua +++ b/examples/postshaders.lua @@ -9,12 +9,12 @@ function love.load() colorAberration = 0.0 -- load images image = love.graphics.newImage("examples/gfx/machine2.png") - quadScreen = love.graphics.newQuad(0, 0, love.window.getWidth() + 32, love.window.getHeight() + 24, 32, 24) + quadScreen = love.graphics.newQuad(0, 0, love.graphics.getWidth() + 32, love.graphics.getHeight() + 24, 32, 24) imgFloor = love.graphics.newImage("examples/gfx/floor.png") imgFloor:setWrap("repeat", "repeat") post_shader = PostShader() - render_buffer = love.graphics.newCanvas(love.window.getWidth(), love.window.getHeight()) + render_buffer = love.graphics.newCanvas(love.graphics.getWidth(), love.graphics.getHeight()) end function love.keypressed(k) @@ -92,9 +92,9 @@ end function love.draw() local w, h = love.graphics.getWidth(), love.graphics.getHeight() - render_buffer:clear() love.graphics.push() love.graphics.setCanvas(render_buffer) + love.graphics.clear() love.graphics.translate(x, y) love.graphics.scale(scale) diff --git a/examples/simple_tiled_impl.lua b/examples/simple_tiled_impl.lua index 1abc0f3..95005c2 100644 --- a/examples/simple_tiled_impl.lua +++ b/examples/simple_tiled_impl.lua @@ -17,7 +17,7 @@ function love.load() shadowBlur = 0.0 }) - map = sti.new("examples/gfx/map") + map = sti.new("examples/gfx/map.lua") image_normal = love.graphics.newImage("examples/gfx/border_NRM.png") -- create light diff --git a/examples/vendor/sti/.gitattributes b/examples/vendor/sti/.gitattributes deleted file mode 100755 index 412eeda..0000000 --- a/examples/vendor/sti/.gitattributes +++ /dev/null @@ -1,22 +0,0 @@ -# Auto detect text files and perform LF normalization -* text=auto - -# Custom for Visual Studio -*.cs diff=csharp -*.sln merge=union -*.csproj merge=union -*.vbproj merge=union -*.fsproj merge=union -*.dbproj merge=union - -# Standard to msysgit -*.doc diff=astextplain -*.DOC diff=astextplain -*.docx diff=astextplain -*.DOCX diff=astextplain -*.dot diff=astextplain -*.DOT diff=astextplain -*.pdf diff=astextplain -*.PDF diff=astextplain -*.rtf diff=astextplain -*.RTF diff=astextplain diff --git a/examples/vendor/sti/.gitignore b/examples/vendor/sti/.gitignore deleted file mode 100755 index b9d6bd9..0000000 --- a/examples/vendor/sti/.gitignore +++ /dev/null @@ -1,215 +0,0 @@ -################# -## Eclipse -################# - -*.pydevproject -.project -.metadata -bin/ -tmp/ -*.tmp -*.bak -*.swp -*~.nib -local.properties -.classpath -.settings/ -.loadpath - -# External tool builders -.externalToolBuilders/ - -# Locally stored "Eclipse launch configurations" -*.launch - -# CDT-specific -.cproject - -# PDT-specific -.buildpath - - -################# -## Visual Studio -################# - -## Ignore Visual Studio temporary files, build results, and -## files generated by popular Visual Studio add-ons. - -# User-specific files -*.suo -*.user -*.sln.docstates - -# Build results - -[Dd]ebug/ -[Rr]elease/ -x64/ -build/ -[Bb]in/ -[Oo]bj/ - -# MSTest test Results -[Tt]est[Rr]esult*/ -[Bb]uild[Ll]og.* - -*_i.c -*_p.c -*.ilk -*.meta -*.obj -*.pch -*.pdb -*.pgc -*.pgd -*.rsp -*.sbr -*.tlb -*.tli -*.tlh -*.tmp -*.tmp_proj -*.log -*.vspscc -*.vssscc -.builds -*.pidb -*.log -*.scc - -# Visual C++ cache files -ipch/ -*.aps -*.ncb -*.opensdf -*.sdf -*.cachefile - -# Visual Studio profiler -*.psess -*.vsp -*.vspx - -# Guidance Automation Toolkit -*.gpState - -# ReSharper is a .NET coding add-in -_ReSharper*/ -*.[Rr]e[Ss]harper - -# TeamCity is a build add-in -_TeamCity* - -# DotCover is a Code Coverage Tool -*.dotCover - -# NCrunch -*.ncrunch* -.*crunch*.local.xml - -# Installshield output folder -[Ee]xpress/ - -# DocProject is a documentation generator add-in -DocProject/buildhelp/ -DocProject/Help/*.HxT -DocProject/Help/*.HxC -DocProject/Help/*.hhc -DocProject/Help/*.hhk -DocProject/Help/*.hhp -DocProject/Help/Html2 -DocProject/Help/html - -# Click-Once directory -publish/ - -# Publish Web Output -*.Publish.xml -*.pubxml - -# NuGet Packages Directory -## TODO: If you have NuGet Package Restore enabled, uncomment the next line -#packages/ - -# Windows Azure Build Output -csx -*.build.csdef - -# Windows Store app package directory -AppPackages/ - -# Others -sql/ -*.Cache -ClientBin/ -[Ss]tyle[Cc]op.* -~$* -*~ -*.dbmdl -*.[Pp]ublish.xml -*.pfx -*.publishsettings - -# RIA/Silverlight projects -Generated_Code/ - -# Backup & report files from converting an old project file to a newer -# Visual Studio version. Backup files are not needed, because we have git ;-) -_UpgradeReport_Files/ -Backup*/ -UpgradeLog*.XML -UpgradeLog*.htm - -# SQL Server files -App_Data/*.mdf -App_Data/*.ldf - -############# -## Windows detritus -############# - -# Windows image file caches -Thumbs.db -ehthumbs.db - -# Folder config file -Desktop.ini - -# Recycle Bin used on file shares -$RECYCLE.BIN/ - -# Mac crap -.DS_Store - - -############# -## Python -############# - -*.py[co] - -# Packages -*.egg -*.egg-info -dist/ -build/ -eggs/ -parts/ -var/ -sdist/ -develop-eggs/ -.installed.cfg - -# Installer logs -pip-log.txt - -# Unit test / coverage reports -.coverage -.tox - -#Translations -*.mo - -#Mr Developer -.mr.developer.cfg diff --git a/examples/vendor/sti/CHANGELOG.md b/examples/vendor/sti/CHANGELOG.md new file mode 100755 index 0000000..62b30ab --- /dev/null +++ b/examples/vendor/sti/CHANGELOG.md @@ -0,0 +1,388 @@ +# Change Log + +## 2016-01-12: v0.14.1.12 + +* Added: Basic support for object layers in Bump plugin (thanks @premek) +* Changed: New line token from CRLF to LF +* Fixed: Sprite batches should now respect the map draw order + +## 2016-01-01: v0.14.1.11 + +* Fixed: Various bugs in the Box2D plugin (thanks @ChrisWeisiger) +* Fixed: Various bugs in the Bump plugin (thanks @bobbyjoness) + +## 2015-12-31: v0.14.1.10 + +* Fixed: Box2D plugin was not recognizing a tile's embedded object group + +## 2015-11-19: v0.14.1.9 + +* Changed: key in image cache to formatted path of image + +## 2015-11-16: v0.14.1.8 + +* Added: image cache to STI module [sponsored by Binary Cocoa] +* Added: STI:flush() to clear out image cache + +## 2015-11-15: v0.14.1.7 + +* Added: support for offsetting maps [sponsored by Binary Cocoa] +* Changed: Map.setDrawRange is more optimized via recycling tables +* Changed: render order now defaults to "right-down" + +## 2015-11-07: v0.14.1.6 + +* Fixed: tileset images not being properly filtered +* Fixed: bump.lua plugin missing world argument in draw + +## 2015-10-14: v0.14.1.5 + +* Added: bump.lua plugin (thanks @bobbyjoness) + +## 2015-10-12: v0.14.1.4 + +* Fixed: removing a layer now properly removes tile and object instances +* Fixed: box2d plugin now properly removes collision objects + +## 2015-10-09: v0.14.1.3 + +* Fixed: flipping animated tiles properly display +* Fixed: rotating animated tiles properly display +* Fixed: rotating tile objects properly display +* Fixed: box2d plugin properly creates rotated and flipped tile objects +* Fixed: box2d plugin no longer crashes when drawing a line with two vertices + +## 2015-10-07: v0.14.1.2 + +* Added: support for all render orders (rd, ru, ld, lu) +* Added: support for sensors in the box2d plugin (only works on individual tiles and objects; sensor = true) +* Changed: addCustomLayer's index argument is now optional and defaults to the end of the array +* Fixed: a crash when using Base64 (uncompressed) with LOVE 0.9.2 + +## 2015-10-03: v0.14.1.1 + +* Added: support for gzip compressed maps (requires LOVE 0.10.0+) + +## 2015-09-30: v0.14.1.0 + +* Added: support for Base64 compressed maps (requires LuaJIT) +* Added: support for zlib compressed maps (requires LOVE 0.10.0+) + +## 2015-09-28: v0.14.0.1 + +* Added: Support for all staggered types (x/y, even/odd, iso/hex) + +## 2015-09-27: v0.14.0.0 + +* Added: Hexagonal map support (thanks EntranceJew!) +* Added: Error message for compressed maps +* Fixed: box2d plugin threw an error in some cases (thanks maxha651!) + +## 2015-09-17: v0.13.1.4 + +* Changed: sanity checks now search for love.physics instead of love.physics.* + +## 2015-09-16: v0.13.1.3 + +* Changed: Improved documentation + +## 2015-09-16: v0.13.1.2 + +* Changed: Simplified plugins +* Changed: Namespaced the box2d plugin +* Removed: Non-LOVE frameworks (they didn't work) + +## 2015-09-16: v0.13.1.1 + +* Added: LDoc documentation +* Added: Plugin system where devs can extend STI +* Added: Reinstated the Box2D integration as a plugin + +## 2015-09-15: v0.13.1.0 + +* Added: Map:convertToCustomLayer() now returns the layer +* Changed: Tightened localization of some functions +* Removed: Box2D collision integration +* Removed: Unused functions + +## 2015-07-31: v0.12.3.0 + +* Added: Tiled version number to Map.tiledversion +* Added: Map.objects table indexed by unique object IDs +* Added: A better error message when trying to use Tile Collections +* Changed: Version number should now match Tiled's version number +* Changed: You must now add ".lua" in the filename of a new map as this is consistent with other libraries +* Changed: Renamed "pure" framework to "lua" (still doesn't work, though!) +* Changed: Map:setDrawRange no longer inverts tx and ty for you, do it yourself! +* Changed: Map:draw no longer accepts scale values, use love.graphics.scale! +* Fixed: A bug where tile objects were drawing an object border +* Removed: Corona framework file + +## 2015-03-22: v0.9.8 + +* Fixed: A bug where Tiles without a Properties list would crash + +## 2015-02-02: v0.9.7 + +* Added: userdata to Box2D fixtures +* Changed: changelog.txt -> CHANGELOG.md +* Changed: Flipping tiles now happens in both tile layers and object layers +* Fixed: A bug where tile objects were drawing oddly in some cases +* Fixed: A bug where circles would error if physics was disabled + +## 2015-01-28: v0.9.6 + +* Added: getLayerProperties(), getTileProperties(), and getObjectProperties() +* Fixed: A bug where flipped tiles crashed STI during initCollision() +* Fixed: Flipped collision tiles now have correct offset +* Removed: Reverted the change in v0.9.3 that filled in empty tiles with false + +## 2014-12-15: v0.9.5 + +* Fixed: A bug where tile collision objects were using the wrong size in some cases +* Fixed: A bug where flipped tiles weren't always creating collision objects + +## 2014-12-05: v0.9.4 + +* Changed: STI's canvas plays nicely with other libraries +* Changed: addCustomLayer() now returns a handle on the created layer + +## 2014-12-03: v0.9.3 + +* Added: Local Tile IDs to Tile objects +* Added: Terrain information +* Fixed: Some conversion functions +* Changed: Tile Layers now contain "false" instead of "nil" where there is no tile +* Changed: Added \_LICENSE, \_URL, \_VERSION, and \_DESCRIPTION properties to core STI object + +## 2014-09-29: v0.9.2 + +* Added: Support for drawing tiles in object layers +* Fixed: Incorrect calculation of some collision objects + +## 2014-09-26: v0.9.1 + +* Fixed: A crash when a collidable tile is initialized but not used +* Removed: Public access to formatPath(), rotateVertex(), and convertEllipseToPolygon() + +## 2014-09-24: v0.9.0 + +* Added: Animated tiles! (Thanks to Clean3d) +* Fixed: A crash when a collidable rectangle has no rotation value +* Fixed: Incorrect values given to orthogonal collision objects + +## 2014-09-24: v0.8.3 + +* Added: Map:convertScreenToTile() and Map:convertTileToScreen() +* Added: Map:convertScreenToIsometricTile() and Map:convertIsometricTileToScreen() +* Added: Map:convertScreenToStaggeredTile() and Map:convertStaggeredTileToScreen() +* Fixed: Map:removeLayer() now works properly +* Changed: Tile Objects now use the tile's collision map by default + +## 2014-09-22: v0.8.2 + +* Added: "collidable" property for objects, tiles, and layers + * if collidable is set to true in Tiled, STI will pick it up and set all appropriate entities to collidable objects +* Fixed: Physics module no long required if not needed. +* Fixed: Whitespace discrepencies +* Changed: Map:initWorldCollision() now supports a whole lot more + +## 2014-09-21: v0.8.1 + +* Added: README now lists minimum requirements +* Changed: README updated with new collision system +* Changed: Map:enableCollision() renamed to Map:initWorldCollision() +* Changed: Map:drawCollisionMap() renamed to Map:drawWorldCollision() +* Changed: Updated framework files (still no real Lua/Corona support) +* Changed: Tidied up collision code +* Removed: Map:getCollisionMap() + +## 2014-09-20: v0.8.0 + +* Added: Box2D collision via Map:enableCollision() +* Added: Map:convertEllipseToPolygon() + +## 2014-09-17: v0.7.6 + +* Added: Map:convertScreenToIsometric and Map:convertIsometricToScreen +* Added: Map:setObjectCoordinates +* Added: Map:rotateVertex +* Fixed: Adjusted map positioning for Isometric and Staggered maps +* Fixed: Object positioning in Isometric maps +* Removed: Temporary fix for Tiled 0.9.1 + +## 2014-08-05: v0.7.5 + +* Fixed: Properties offset by 1 +* Fixed: Drawing a single Layer can now use Layer's name/index + +## 2014-04-28: v0.7.4 + +* Fixed: Canvas resize type + +## 2014-04-18: v0.7.3 + +* Fixed: Canvas using wrong filter + +## 2014-04-08: v0.7.2 + +* Removed: Dependency for LuaJIT's bitwise operations + +## 2014-04-08: v0.7.1 + +* Added: Map:resize(w, h) +* Changed: Map:draw() now takes two optional arguments: ScaleX and ScaleY +* Changed: STI now draws to a Canvas before drawing to screen (fixes scaling oddities) + +## 2014-04-07: v0.7.0 + +* Added: Files for Corona and Pure Lua implementation +* Changed: Restructured sti.lua into several files +* Changed: Library is now LOVE agnostic and should allow for implementation of other frameworks + +## 2014-03-1 : v0.6.16 + +* Changed: Ellipses now use polygons instead of... Not polygons. + +## 2014-03-1 : v0.6.15 + +* Fixed: Tile spacing calculated properly in all cases + +## 2014-02-0 : v0.6.14 + +* Fixed: Tile properties ACTUALLY being added now! + +## 2014-01-2 : v0.6.13 + +* Added: Missing Tile Flag + +## 2014-01-2 : v0.6.12 + +* Added: drawCollisionMap() now supports Isometric and Staggered maps +* Changed: drawCollisionMap() now requires a collision map parameter +* Changed: setCollisionMap() renamed to getCollisionMap() +* Changed: getCollisionMap() now returns the collision map +* Fixed: Tile properties not being added +* Removed: Map.collision table removed + +## 2014-01-2 : v0.6.11 + +* Added: Descriptive error messages +* Fixed: Image filters for scaling + +## 2014-01-2 : v0.6.10 + +* Fixed: Optimized load time + +## 2014-01-25: v0.6.9 + +* Fixed: Parallax Scrolling + +## 2014-01-25: v0.6.8 + +* Changed: Revised and restructured code +* Changed: createCollisionMap() renamed to setCollisionMap() +* Changed: newCustomLayer() renamed to addCustomLayer() + +## 2014-01-24: v0.6.7 + +* Fixed: Number of tiles wasn't calculated properly + +## 2014-01-24: v0.6.6 + +* Fixed: Spacing wasn't calculated properly + +## 2014-01-24: v0.6.5 + +* Added: Staggered Maps + +## 2014-01-24: v0.6.4 + +* Added: Isometric Maps + +## 2014-01-20: v0.6.3 + +* Added: Tile Flags (flip/rotation) + +## 2014-01-20: v0.6.2 + +* Fixed: A scaling bug + +## 2014-01-19: v0.6.1 + +* Fixed: A bug causing the Collision Map to be nil + +## 2014-01-19: v0.6.0 + +* Added: Sprite Batches + +## 2014-01-19: v0.5.0 + +* Added: Draw Range optimization + +## 2014-01-18: v0.4.3 + +* Added: Layer draw offsets + +## 2014-01-17: v0.4.2 + +* Changed: Organized library a little better + +## 2014-01-17: v0.4.1 + +* Fixed: Tiles incorrectly offset +* Fixed: Drawing concave polygons + +## 2014-01-17: v0.4.0 + +* Added: Draw Object Layers + +## 2014-01-16: v0.3.3 + +* Added: Create new Custom Layer +* Added: Callbacks for all layers +* Added: Remove Layer +* Changed: Simplified sti.new() + +## 2014-01-16: v0.3.2 + +* Fixed: Crash if using Tiled 0.9.1 +* Changed: Map structure to remove "map" table + +## 2014-01-16: v0.3.1 + +* Added: Update callback to Custom Layers + +## 2014-01-16: v0.3.0 + +* Added: Support for converting layers to Custom Layers +* Changed: sti.new() no longer requires the file extension + +## 2014-01-15: v0.2.2 + +* Added: Support for basic collision layer + +## 2014-01-15: v0.2.1 + +* Added: Support for map instances +* Added: Name alias to layer indices +* Changed: Sandboxed map environment +* Changed: Data structures are more efficient +* Removed: Unnecessary update function + +Thanks to JarrettBillingsley for many of these changes + +## 2014-01-14: v0.2.0 + +* Fixed: Drawing Tile Offset +* Changed: Tile Layer data structure is more efficient +* Changed: Simplified Quad generation + +## 2014-01-14: v0.1.0 + +* Initial Commit +* Added: Orthogonal Map support +* Added: Draw Tile Layers +* Added: Draw Image Layers +* Added: Ignore Hidden Layers +* Added: Layer Opacity diff --git a/examples/vendor/sti/LICENSE.md b/examples/vendor/sti/LICENSE.md new file mode 100755 index 0000000..234f8f6 --- /dev/null +++ b/examples/vendor/sti/LICENSE.md @@ -0,0 +1,26 @@ +# Simple Tiled Implementation + +This code is licensed under the [**MIT/X11 Open Source License**][MIT]. + +Copyright (c) 2014 Landon Manning - LManning17@gmail.com - [LandonManning.com][LM] + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +[MIT]: http://www.opensource.org/licenses/mit-license.html +[LM]: http://LandonManning.com diff --git a/examples/vendor/sti/README.md b/examples/vendor/sti/README.md new file mode 100755 index 0000000..0b31e9a --- /dev/null +++ b/examples/vendor/sti/README.md @@ -0,0 +1,96 @@ +# Simple Tiled Implementation + +Simple Tiled Implementation is a [**Tiled**][Tiled] map loader and renderer designed for the **\*awesome\*** [**LÖVE**][LOVE] framework. Please read the [**documentation**][dox] to learn how it works! + +## Quick Example + +```lua +-- This example uses the default Box2D (love.physics) plugin!! + +local sti = require "sti" + +function love.load() + -- Grab window size + windowWidth = love.graphics.getWidth() + windowHeight = love.graphics.getHeight() + + -- Set world meter size (in pixels) + love.physics.setMeter(32) + + -- Load a map exported to Lua from Tiled + map = sti.new("assets/maps/map01.lua", { "box2d" }) + + -- Prepare physics world with horizontal and vertical gravity + world = love.physics.newWorld(0, 0) + + -- Prepare collision objects + map:box2d_init(world) + + -- Create a Custom Layer + map:addCustomLayer("Sprite Layer", 3) + + -- Add data to Custom Layer + local spriteLayer = map.layers["Sprite Layer"] + spriteLayer.sprites = { + player = { + image = love.graphics.newImage("assets/sprites/player.png"), + x = 64, + y = 64, + r = 0, + } + } + + -- Update callback for Custom Layer + function spriteLayer:update(dt) + for _, sprite in pairs(self.sprites) do + sprite.r = sprite.r + math.rad(90 * dt) + end + end + + -- Draw callback for Custom Layer + function spriteLayer:draw() + for _, sprite in pairs(self.sprites) do + local x = math.floor(sprite.x) + local y = math.floor(sprite.y) + local r = sprite.r + love.graphics.draw(sprite.image, x, y, r) + end + end +end + +function love.update(dt) + map:update(dt) +end + +function love.draw() + -- Translation would normally be based on a player's x/y + local translateX = 0 + local translateY = 0 + + -- Draw Range culls unnecessary tiles + map:setDrawRange(-translateX, -translateY, windowWidth, windowHeight) + + -- Draw the map and all objects within + map:draw() + + -- Draw Collision Map (useful for debugging) + love.graphics.setColor(255, 0, 0, 255) + map:box2d_draw() + + -- Reset color + love.graphics.setColor(255, 255, 255, 255) +end +``` + +## Requirements + +This library recommends LÖVE 0.9.2 or 0.10.0 and Tiled 0.14.1. If you are updating from an older version of Tiled, please re-export your Lua map files. + +## License + +This code is licensed under the [**MIT/X11 Open Source License**][MIT]. Check out the LICENSE file for more information. + +[Tiled]: http://www.mapeditor.org/ +[LOVE]: https://www.love2d.org/ +[dox]: http://karai17.github.io/Simple-Tiled-Implementation/ +[MIT]: http://www.opensource.org/licenses/mit-license.html diff --git a/examples/vendor/sti/framework/corona.lua b/examples/vendor/sti/framework/corona.lua deleted file mode 100755 index d469ffd..0000000 --- a/examples/vendor/sti/framework/corona.lua +++ /dev/null @@ -1,56 +0,0 @@ -local lf = love.filesystem -local lg = love.graphics -local lm = love.math -local lp = love.physics -local framework = {} - -framework.version = "Corona" - -function framework.load(file) - return assert(lf.load(file), "File not found: " .. file) -end - -function framework.newImage(path) - local image = lg.newImage(path) - image:setFilter("nearest", "nearest") - - return image -end - -function framework:newCanvas(w, h) - w = w or self.getWidth() - h = h or self.getHeight() - local canvas = lg.newCanvas(w, h) - canvas:setFilter("nearest", "nearest") - - return canvas -end - --- Graphics Calls -framework.clear = lg.clear -framework.draw = lg.draw -framework.getHeight = lg.getHeight -framework.getWidth = lg.getWidth -framework.line = lg.line -framework.newSpriteBatch = lg.newSpriteBatch -framework.newQuad = lg.newQuad -framework.polygon = lg.polygon -framework.rectangle = lg.rectangle -framework.setColor = lg.setColor -framework.setCanvas = lg.setCanvas -framework.origin = lg.origin -framework.pop = lg.pop -framework.push = lg.push - --- Math Calls -framework.isConvex = lm.isConvex -framework.triangulate = lm.triangulate - --- Physics Calls -framework.getMeter = lp.getMeter -framework.newBody = lp.newBody -framework.newChainShape = lp.newChainShape -framework.newFixture = lp.newFixture -framework.newPolygonShape = lp.newPolygonShape - -return framework diff --git a/examples/vendor/sti/framework/love.lua b/examples/vendor/sti/framework/love.lua deleted file mode 100755 index 7f49443..0000000 --- a/examples/vendor/sti/framework/love.lua +++ /dev/null @@ -1,62 +0,0 @@ -local lf = love.filesystem -local lg = love.graphics -local lm = love.math -local lp = love.physics -local framework = {} - -framework.version = "LOVE" - -assert(lf, "The love.filesystem module is required for this library.") -assert(lg, "The love.graphics module is required for this library.") -assert(lm, "The love.math module is required for this library.") - -function framework.load(file) - return assert(lf.load(file), "File not found: " .. file) -end - -function framework.newImage(path) - local image = lg.newImage(path) - image:setFilter("nearest", "nearest") - - return image -end - -function framework:newCanvas(w, h) - w = w or self.getWidth() - h = h or self.getHeight() - local canvas = lg.newCanvas(w, h) - canvas:setFilter("nearest", "nearest") - - return canvas -end - --- Graphics Calls -framework.draw = lg.draw -framework.getCanvas = lg.getCanvas -framework.getHeight = lg.getHeight -framework.getWidth = lg.getWidth -framework.line = lg.line -framework.newSpriteBatch = lg.newSpriteBatch -framework.newQuad = lg.newQuad -framework.polygon = lg.polygon -framework.rectangle = lg.rectangle -framework.setColor = lg.setColor -framework.setCanvas = lg.setCanvas -framework.origin = lg.origin -framework.pop = lg.pop -framework.push = lg.push - --- Math Calls -framework.isConvex = lm.isConvex -framework.triangulate = lm.triangulate - --- Physics Calls -if lp then - framework.getMeter = lp.getMeter - framework.newBody = lp.newBody - framework.newChainShape = lp.newChainShape - framework.newFixture = lp.newFixture - framework.newPolygonShape = lp.newPolygonShape -end - -return framework diff --git a/examples/vendor/sti/framework/pure.lua b/examples/vendor/sti/framework/pure.lua deleted file mode 100755 index 18bdf47..0000000 --- a/examples/vendor/sti/framework/pure.lua +++ /dev/null @@ -1,56 +0,0 @@ -local lf = love.filesystem -local lg = love.graphics -local lm = love.math -local lp = love.physics -local framework = {} - -framework.version = "Lua" - -function framework.load(file) - return assert(lf.load(file), "File not found: " .. file) -end - -function framework.newImage(path) - local image = lg.newImage(path) - image:setFilter("nearest", "nearest") - - return image -end - -function framework:newCanvas(w, h) - w = w or self.getWidth() - h = h or self.getHeight() - local canvas = lg.newCanvas(w, h) - canvas:setFilter("nearest", "nearest") - - return canvas -end - --- Graphics Calls -framework.clear = lg.clear -framework.draw = lg.draw -framework.getHeight = lg.getHeight -framework.getWidth = lg.getWidth -framework.line = lg.line -framework.newSpriteBatch = lg.newSpriteBatch -framework.newQuad = lg.newQuad -framework.polygon = lg.polygon -framework.rectangle = lg.rectangle -framework.setColor = lg.setColor -framework.setCanvas = lg.setCanvas -framework.origin = lg.origin -framework.pop = lg.pop -framework.push = lg.push - --- Math Calls -framework.isConvex = lm.isConvex -framework.triangulate = lm.triangulate - --- Physics Calls -framework.getMeter = lp.getMeter -framework.newBody = lp.newBody -framework.newChainShape = lp.newChainShape -framework.newFixture = lp.newFixture -framework.newPolygonShape = lp.newPolygonShape - -return framework diff --git a/examples/vendor/sti/init.lua b/examples/vendor/sti/init.lua index a1bc510..eac0ab1 100755 --- a/examples/vendor/sti/init.lua +++ b/examples/vendor/sti/init.lua @@ -1,90 +1,53 @@ ---[[ ------------------------------------------------------------------------------- -Simple Tiled Implementation is licensed under the MIT Open Source License. -(http://www.opensource.org/licenses/mit-license.html) ------------------------------------------------------------------------------- - -Copyright (c) 2014 Landon Manning - LManning17@gmail.com - LandonManning.com - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. -]]-- +--- Simple and fast Tiled map loader and renderer. +-- @module sti +-- @author Landon Manning +-- @copyright 2015 +-- @license MIT/X11 local STI = { - _LICENSE = "STI is distributed under the terms of the MIT license. See LICENSE.md.", - _URL = "https://github.com/karai17/Simple-Tiled-Implementation", - _VERSION = "0.9.4", - _DESCRIPTION = "Simple Tiled Implementation is a Tiled Map Editor library designed for the *awesome* LÖVE framework." + _LICENSE = "MIT/X11", + _URL = "https://github.com/karai17/Simple-Tiled-Implementation", + _VERSION = "0.14.1.12", + _DESCRIPTION = "Simple Tiled Implementation is a Tiled Map Editor library designed for the *awesome* LÖVE framework.", + cache = {} } -local path = ... .. "." -- lol -local Map = require(path .. "map") -local framework +local path = (...):gsub('%.init$', '') .. "." +local Map = require(path .. "map") -if love then - framework = require(path .. "framework.love") -elseif corona then -- I don't think this works - framework = require(path .. "framework.corona") -else - framework = require(path .. "framework.pure") -end +--- Instance a new map. +-- @param path Path to the map file. +-- @param plugins A list of plugins to load. +-- @param ox Offset of map on the X axis (in pixels) +-- @param oy Offset of map on the Y axis (in pixels) +-- @return table The loaded Map. +function STI.new(map, plugins, ox, oy) + -- Check for valid map type + local ext = map:sub(-4, -1) + assert(ext == ".lua", string.format( + "Invalid file type: %s. File must be of type: lua.", + ext + )) -function STI.new(map) - map = map .. ".lua" - -- Get path to map local path = map:reverse():find("[/\\]") or "" if path ~= "" then path = map:sub(1, 1 + (#map - path)) end - + -- Load map - map = framework.load(map) + map = love.filesystem.load(map) setfenv(map, {}) map = setmetatable(map(), {__index = Map}) - - map:init(path, framework) - + + map:init(STI, path, plugins, ox, oy) + return map end --- http://wiki.interfaceware.com/534.html -function string.split(s, d) - local t = {} - local i = 0 - local f - local match = '(.-)' .. d .. '()' - - if string.find(s, d) == nil then - return {s} - end - - for sub, j in string.gmatch(s, match) do - i = i + 1 - t[i] = sub - f = j - end - - if i ~= 0 then - t[i+1] = string.sub(s, f) - end - - return t +--- Flush image cache. +function STI:flush() + self.cache = {} end return STI diff --git a/examples/vendor/sti/map.lua b/examples/vendor/sti/map.lua index 0dfbe15..5ceee41 100755 --- a/examples/vendor/sti/map.lua +++ b/examples/vendor/sti/map.lua @@ -1,10 +1,14 @@ -local Map = {} -local framework +--- Map object +-- @module map + +local path = (...):gsub('%.[^%.]+$', '') .. "." +local pluginPath = string.gsub(path, "[.]", "/") .. "plugins/" +local Map = {} -- https://github.com/stevedonovan/Penlight/blob/master/lua/pl/path.lua#L286 local function formatPath(path) - local np_gen1,np_gen2 = '[^SEP]+SEP%.%.SEP?','SEP+%.?SEP' - local np_pat1, np_pat2 = np_gen1:gsub('SEP','/'), np_gen2:gsub('SEP','/') + local np_gen1,np_gen2 = '[^SEP]+SEP%.%.SEP?','SEP+%.?SEP' + local np_pat1, np_pat2 = np_gen1:gsub('SEP','/'), np_gen2:gsub('SEP','/') local k repeat -- /./ -> / @@ -20,97 +24,78 @@ local function formatPath(path) return path end -local function rotateVertex(v, x, y, cos, sin) - local vertex = { - x = v.x, - y = v.y, - } +-- Compensation for scale/rotation shift +local function compensate(tile, x, y, tw, th) + local tx = x + tile.offset.x + local ty = y + tile.offset.y + local origx = tx + local origy = ty + local compx = 0 + local compy = 0 - vertex.x = vertex.x - x - vertex.y = vertex.y - y + if tile.sx < 0 then compx = tw end + if tile.sy < 0 then compy = th end - local vx = cos * vertex.x - sin * vertex.y - local vy = sin * vertex.x + cos * vertex.y - - return vx + x, vy + y -end - -local function convertEllipseToPolygon(x, y, w, h, max_segments) - local function calc_segments(segments) - local function vdist(a, b) - local c = { - x = a.x - b.x, - y = a.y - b.y, - } - - return c.x * c.x + c.y * c.y - end - - segments = segments or 64 - local vertices = {} - - local v = { 1, 2, math.ceil(segments/4-1), math.ceil(segments/4) } - - local m - if framework.getMeter then - m = framework.getMeter() - else - m = self.tilewidth + self.tileheight / 2 - end - - for _, i in ipairs(v) do - local angle = (i / segments) * math.pi * 2 - local px = x + w / 2 + math.cos(angle) * w / 2 - local py = y + h / 2 + math.sin(angle) * h / 2 - - table.insert(vertices, { x = px / m, y = py / m }) - end - - local dist1 = vdist(vertices[1], vertices[2]) - local dist2 = vdist(vertices[3], vertices[4]) - - -- Box2D hard-coded threshold - if dist1 < 0.0025 or dist2 < 0.0025 then - return calc_segments(segments-2) - end - - return segments + if tile.r > 0 then + tx = tx + th - compy + ty = ty + th - tw + compx + elseif tile.r < 0 then + tx = tx + compy + ty = ty + th - compx + else + tx = tx + compx + ty = ty + compy end - local segments = calc_segments(max_segments) - local vertices = {} - - table.insert(vertices, { x = x + w / 2, y = y + h / 2 }) - - for i=0, segments do - local angle = (i / segments) * math.pi * 2 - local px = x + w / 2 + math.cos(angle) * w / 2 - local py = y + h / 2 + math.sin(angle) * h / 2 - - table.insert(vertices, { x = px, y = py }) - end - - return vertices + return tx, ty end -function Map:init(path, fw) - framework = fw +-- Cache images in main STI module +local function cache_image(sti, path) + local image = love.graphics.newImage(path) + image:setFilter("nearest", "nearest") + sti.cache[path] = image +end - self.canvas = framework:newCanvas() - self.tiles = {} - self.tileInstances = {} - self.drawRange = { +--- Instance a new map +-- @param path Path to the map file +-- @param plugins A list of plugins to load +-- @param ox Offset of map on the X axis (in pixels) +-- @param oy Offset of map on the Y axis (in pixels) +-- @return nil +function Map:init(STI, path, plugins, ox, oy) + if type(plugins) == "table" then + self:loadPlugins(plugins) + end + + self:resize() + self.objects = {} + self.tiles = {} + self.tileInstances = {} + self.drawRange = { sx = 1, sy = 1, ex = self.width, ey = self.height, } + self.offsetx = ox or 0 + self.offsety = oy or 0 + self.sti = STI -- Set tiles, images local gid = 1 for i, tileset in ipairs(self.tilesets) do - local image = formatPath(path .. tileset.image) - tileset.image = framework.newImage(image) + assert(tileset.image, "STI does not support Tile Collections.\nYou need to create a Texture Atlas.") + + -- Cache images + local formatted_path = formatPath(path .. tileset.image) + if not self.sti.cache[formatted_path] then + cache_image(self.sti, formatted_path) + end + + -- Pull images from cache + tileset.image = self.sti.cache[formatted_path] + gid = self:setTiles(i, tileset, gid) end @@ -120,214 +105,28 @@ function Map:init(path, fw) end end -function Map:initWorldCollision(world) - assert(framework.newBody, "To use the built-in collision system, please enable the physics module.") - - local body = framework.newBody(world) - local collision = { - body = body, - } - - local function addObjectToWorld(objshape, vertices) - local shape - - if objshape == "polyline" then - shape = framework.newChainShape(false, unpack(vertices)) - else - shape = framework.newPolygonShape(unpack(vertices)) - end - - local fixture = framework.newFixture(body, shape) - local obj = { - shape = shape, - fixture = fixture, - } - - table.insert(collision, obj) - end - - local function getPolygonVertices(object, tile, precalc) - local ox, oy = 0, 0 - - if not precalc then - ox = object.x - oy = object.y - end - - local vertices = {} - for _, vertex in ipairs(object.polygon) do - table.insert(vertices, tile.x + ox + vertex.x) - table.insert(vertices, tile.y + oy + vertex.y) - end - - return vertices - end - - local function calculateObjectPosition(object, tile) - local o = { - shape = object.shape, - x = object.x, - y = object.y, - w = object.width, - h = object.height, - polygon = object.polygon or object.polyline or object.ellipse or object.rectangle - } - local t = tile or { x=0, y=0 } - - if o.shape == "rectangle" then - o.r = object.rotation or 0 - local cos = math.cos(math.rad(o.r)) - local sin = math.sin(math.rad(o.r)) - - if object.gid then - local tileset = self.tiles[object.gid].tileset - local lid = object.gid - self.tilesets[tileset].firstgid - local tile = {} - - -- This fixes a height issue - o.y = o.y + self.tiles[object.gid].offset.y - - for _, t in ipairs(self.tilesets[tileset].tiles) do - if t.id == lid then - tile = t - break - end - end - - if tile.objectGroup then - for _, obj in ipairs(tile.objectGroup.objects) do - -- Every object in the tile - calculateObjectPosition(obj, object) - end - - return - else - o.w = self.tiles[object.gid].width - o.h = self.tiles[object.gid].height - end - end - - o.polygon = { - { x=o.x, y=o.y }, - { x=o.x + o.w, y=o.y }, - { x=o.x + o.w, y=o.y + o.h }, - { x=o.x, y=o.y + o.h }, - } - - for _, vertex in ipairs(o.polygon) do - if self.orientation == "isometric" then - vertex.x, vertex.y = self:convertIsometricToScreen(vertex.x, vertex.y) - end - - vertex.x, vertex.y = rotateVertex(vertex, o.x, o.y, cos, sin) - end - - local vertices = getPolygonVertices(o, t, true) - addObjectToWorld(o.shape, vertices) - elseif o.shape == "ellipse" then - if not o.polygon then - o.polygon = convertEllipseToPolygon(o.x, o.y, o.w, o.h) - end - local vertices = getPolygonVertices(o, t, true) - local triangles = framework.triangulate(vertices) - - for _, triangle in ipairs(triangles) do - addObjectToWorld(o.shape, triangle) - end - elseif o.shape == "polygon" then - local precalc = false - if not t.gid then precalc = true end - - local vertices = getPolygonVertices(o, t, precalc) - local triangles = framework.triangulate(vertices) - - for _, triangle in ipairs(triangles) do - addObjectToWorld(o.shape, triangle) - end - elseif o.shape == "polyline" then - local precalc = false - if not t.gid then precalc = true end - - local vertices = getPolygonVertices(o, t, precalc) - addObjectToWorld(o.shape, vertices) - end - end - - for _, tileset in ipairs(self.tilesets) do - for _, tile in ipairs(tileset.tiles) do - local gid = tileset.firstgid + tile.id - - if tile.objectGroup then - if self.tileInstances[gid] then - for _, instance in ipairs(self.tileInstances[gid]) do - for _, object in ipairs(tile.objectGroup.objects) do - -- Every object in every instance of a tile - calculateObjectPosition(object, instance) - end - end - end - elseif tile.properties.collidable == "true" then - for _, instance in ipairs(self.tileInstances[gid]) do - -- Every instance of a tile - local object = { - shape = "rectangle", - x = 0, - y = 0, - width = tileset.tilewidth, - height = tileset.tileheight, - } - - calculateObjectPosition(object, instance) +--- Load plugins +-- @param plugins A list of plugins to load +-- @return nil +function Map:loadPlugins(plugins) + for _, plugin in ipairs(plugins) do + local p = pluginPath .. plugin .. ".lua" + if love.filesystem.isFile(p) then + local file = love.filesystem.load(p)() + for k, func in pairs(file) do + if not self[k] then + self[k] = func end end end end - - for _, layer in ipairs(self.layers) do - if layer.properties.collidable == "true" then - -- Entire layer - if layer.type == "tilelayer" then - for y, tiles in ipairs(layer.data) do - for x, tile in pairs(tiles) do - local object = { - shape = "rectangle", - x = x * self.width + tile.offset.x, - y = y * self.height + tile.offset.y, - width = tile.width, - height = tile.height, - } - calculateObjectPosition(object) - end - end - elseif layer.type == "objectgroup" then - for _, object in ipairs(layer.objects) do - calculateObjectPosition(object) - end - elseif layer.type == "imagelayer" then - local object = { - shape = "rectangle", - x = layer.x or 0, - y = layer.y or 0, - width = layer.width, - height = layer.height, - } - calculateObjectPosition(object) - end - end - - if layer.type == "objectgroup" then - for _, object in ipairs(layer.objects) do - if object.properties.collidable == "true" then - -- Individual objects - calculateObjectPosition(object) - end - end - end - end - - return collision end +--- Create Tiles +-- @param index Index of the Tileset +-- @param tileset Tileset data +-- @param gid First Global ID in Tileset +-- @return number Next Tileset's first Global ID function Map:setTiles(index, tileset, gid) local function getTiles(i, t, m, s) i = i - m @@ -342,33 +141,34 @@ function Map:setTiles(index, tileset, gid) return n end - local quad = framework.newQuad - local mw = self.tilewidth - local iw = tileset.imagewidth - local ih = tileset.imageheight - local tw = tileset.tilewidth - local th = tileset.tileheight - local s = tileset.spacing - local m = tileset.margin - local w = getTiles(iw, tw, m, s) - local h = getTiles(ih, th, m, s) + local quad = love.graphics.newQuad + local mw = self.tilewidth + local iw = tileset.imagewidth + local ih = tileset.imageheight + local tw = tileset.tilewidth + local th = tileset.tileheight + local s = tileset.spacing + local m = tileset.margin + local w = getTiles(iw, tw, m, s) + local h = getTiles(ih, th, m, s) for y = 1, h do for x = 1, w do local id = gid - tileset.firstgid local qx = (x - 1) * tw + m + (x - 1) * s local qy = (y - 1) * th + m + (y - 1) * s - local properties - local terrain - local animation + local properties, terrain, animation, objectGroup for _, tile in pairs(tileset.tiles) do if tile.id == id then - properties = tile.properties - animation = tile.animation + properties = tile.properties + animation = tile.animation + objectGroup = tile.objectGroup + if tile.terrain then terrain = {} - for i=1,#tile.terrain do + + for i=1, #tile.terrain do terrain[i] = tileset.terrains[tile.terrain[i] + 1] end end @@ -376,21 +176,22 @@ function Map:setTiles(index, tileset, gid) end local tile = { - id = id, - gid = gid, - tileset = index, - quad = quad(qx, qy, tw, th, iw, ih), - properties = properties, + id = id, + gid = gid, + tileset = index, + quad = quad(qx, qy, tw, th, iw, ih), + properties = properties or {}, terrain = terrain, animation = animation, + objectGroup = objectGroup, frame = 1, time = 0, - width = tileset.tilewidth, - height = tileset.tileheight, - sx = 1, - sy = 1, - r = 0, - offset = { + width = tw, + height = th, + sx = 1, + sy = 1, + r = 0, + offset = { x = -mw + tileset.tileoffset.x, y = -th + tileset.tileoffset.y, }, @@ -401,16 +202,54 @@ function Map:setTiles(index, tileset, gid) end self.tiles[gid] = tile - gid = gid + 1 + gid = gid + 1 end end return gid end +--- Create Layers +-- @param layer Layer data +-- @param path (Optional) Path to an Image Layer's image +-- @return nil function Map:setLayer(layer, path) - layer.x = layer.x or 0 - layer.y = layer.y or 0 + if layer.encoding then + if layer.encoding == "base64" then + local ffi = assert(require "ffi", "Compressed maps require LuaJIT FFI.\nPlease Switch your interperator to LuaJIT or your Tile Layer Format to \"CSV\".") + local fd = love.filesystem.newFileData(layer.data, "data", "base64"):getString() + + local function getDecompressedData(data) + local d = {} + local decoded = ffi.cast("uint32_t*", data) + + for i=0, data:len() / ffi.sizeof("uint32_t") do + table.insert(d, tonumber(decoded[i])) + end + + return d + end + + if not layer.compression then + layer.data = getDecompressedData(fd) + else + assert(love.math.decompress, "zlib and gzip compression require LOVE 0.10.0+.\nPlease set your Tile Layer Format to \"Base64 (uncompressed)\" or \"CSV\".") + + if layer.compression == "zlib" then + local data = love.math.decompress(fd, "zlib") + layer.data = getDecompressedData(data) + end + + if layer.compression == "gzip" then + local data = love.math.decompress(fd, "gzip") + layer.data = getDecompressedData(data) + end + end + end + end + + layer.x = (layer.x or 0) + self.offsetx + layer.y = (layer.y or 0) + self.offsety layer.update = function(dt) return end if layer.type == "tilelayer" then @@ -418,6 +257,7 @@ function Map:setLayer(layer, path) self:setSpriteBatches(layer) layer.draw = function() self:drawTileLayer(layer) end elseif layer.type == "objectgroup" then + self:setObjectData(layer) self:setObjectCoordinates(layer) self:setObjectSpriteBatches(layer) layer.draw = function() self:drawObjectLayer(layer) end @@ -425,9 +265,13 @@ function Map:setLayer(layer, path) layer.draw = function() self:drawImageLayer(layer) end if layer.image ~= "" then - local image = formatPath(path..layer.image) - layer.image = framework.newImage(image) - layer.width = layer.image:getWidth() + local formatted_path = formatPath(path .. layer.image) + if not self.sti.cache[formatted_path] then + cache_image(self.sti, formatted_path) + end + + layer.image = self.sti.cache[formatted_path] + layer.width = layer.image:getWidth() layer.height = layer.image:getHeight() end end @@ -435,8 +279,11 @@ function Map:setLayer(layer, path) self.layers[layer.name] = layer end +--- Add Tiles to Tile Layer +-- @param layer The Tile Layer +-- @return nil function Map:setTileData(layer) - local i = 1 + local i = 1 local map = {} for y = 1, layer.height do @@ -445,74 +292,7 @@ function Map:setTileData(layer) local gid = layer.data[i] if gid > 0 then - local tile = self.tiles[gid] - - if tile then - map[y][x] = tile - else - local bit31 = 2147483648 - local bit30 = 1073741824 - local bit29 = 536870912 - local flipX = false - local flipY = false - local flipD = false - local realgid = gid - - if realgid >= bit31 then - realgid = realgid - bit31 - flipX = not flipX - end - - if realgid >= bit30 then - realgid = realgid - bit30 - flipY = not flipY - end - - if realgid >= bit29 then - realgid = realgid - bit29 - flipD = not flipD - end - - local tile = self.tiles[realgid] - local data = { - id = tile.id, - gid = tile.gid, - tileset = tile.tileset, - offset = tile.offset, - quad = tile.quad, - properties = tile.properties, - terrain = tile.terrain, - animation = tile.animation, - sx = tile.sx, - sy = tile.sy, - r = tile.r, - } - - if flipX then - if flipY then - data.sx = -1 - data.sy = -1 - elseif flipD then - data.r = math.rad(90) - else - data.sx = -1 - end - elseif flipY then - if flipD then - data.r = math.rad(-90) - else - data.sy = -1 - end - elseif flipD then - data.r = math.rad(90) - data.sy = -1 - end - - self.tiles[gid] = data - map[y][x] = self.tiles[gid] - end - else - map[y][x] = false + map[y][x] = self.tiles[gid] or self:setFlippedGID(gid) end i = i + 1 @@ -522,10 +302,96 @@ function Map:setTileData(layer) layer.data = map end +--- Add Objects to Layer +-- @param layer The Object Layer +-- @return nil +function Map:setObjectData(layer) + for _, object in ipairs(layer.objects) do + object.layer = layer + self.objects[object.id] = object + end +end + +--- Correct position and orientation of Objects in an Object Layer +-- @param layer The Object Layer +-- @return nil function Map:setObjectCoordinates(layer) + local function convertEllipseToPolygon(x, y, w, h, max_segments) + local function calc_segments(segments) + local function vdist(a, b) + local c = { + x = a.x - b.x, + y = a.y - b.y, + } + + return c.x * c.x + c.y * c.y + end + + segments = segments or 64 + local vertices = {} + + local v = { 1, 2, math.ceil(segments/4-1), math.ceil(segments/4) } + + local m + if love.physics then + m = love.physics.getMeter() + else + m = 32 + end + + for _, i in ipairs(v) do + local angle = (i / segments) * math.pi * 2 + local px = x + w / 2 + math.cos(angle) * w / 2 + local py = y + h / 2 + math.sin(angle) * h / 2 + + table.insert(vertices, { x = px / m, y = py / m }) + end + + local dist1 = vdist(vertices[1], vertices[2]) + local dist2 = vdist(vertices[3], vertices[4]) + + -- Box2D threshold + if dist1 < 0.0025 or dist2 < 0.0025 then + return calc_segments(segments-2) + end + + return segments + end + + local segments = calc_segments(max_segments) + local vertices = {} + + table.insert(vertices, { x = x + w / 2, y = y + h / 2 }) + + for i=0, segments do + local angle = (i / segments) * math.pi * 2 + local px = x + w / 2 + math.cos(angle) * w / 2 + local py = y + h / 2 + math.sin(angle) * h / 2 + + table.insert(vertices, { x = px, y = py }) + end + + return vertices + end + + local function rotateVertex(v, x, y, cos, sin) + local vertex = { + x = v.x, + y = v.y, + } + + vertex.x = vertex.x - x + vertex.y = vertex.y - y + + local vx = cos * vertex.x - sin * vertex.y + local vy = sin * vertex.x + cos * vertex.y + + return vx + x, vy + y + end + local function updateVertex(vertex, x, y, cos, sin) if self.orientation == "isometric" then - x, y = self:convertIsometricToScreen(x, y) + x, y = self:convertIsometricToScreen(x, y) vertex.x, vertex.y = self:convertIsometricToScreen(vertex.x, vertex.y) end @@ -533,22 +399,22 @@ function Map:setObjectCoordinates(layer) end for _, object in ipairs(layer.objects) do - local x = layer.x + object.x - local y = layer.y + object.y - local w = object.width - local h = object.height - local r = object.rotation + local x = layer.x + object.x + local y = layer.y + object.y + local w = object.width + local h = object.height + local r = object.rotation local cos = math.cos(math.rad(r)) local sin = math.sin(math.rad(r)) - if object.shape == "rectangle" then + if object.shape == "rectangle" and not object.gid then object.rectangle = {} local vertices = { - { x=x, y=y }, - { x=x + w, y=y }, - { x=x + w, y=y + h }, - { x=x, y=y + h }, + { x=x, y=y }, + { x=x + w, y=y }, + { x=x + w, y=y + h }, + { x=x, y=y + h }, } for _, vertex in ipairs(vertices) do @@ -557,7 +423,6 @@ function Map:setObjectCoordinates(layer) end elseif object.shape == "ellipse" then object.ellipse = {} - local vertices = convertEllipseToPolygon(x, y, w, h) for _, vertex in ipairs(vertices) do @@ -566,84 +431,142 @@ function Map:setObjectCoordinates(layer) end elseif object.shape == "polygon" then for _, vertex in ipairs(object.polygon) do - vertex.x = x + vertex.x - vertex.y = y + vertex.y + vertex.x = vertex.x + x + vertex.y = vertex.y + y vertex.x, vertex.y = updateVertex(vertex, x, y, cos, sin) end elseif object.shape == "polyline" then for _, vertex in ipairs(object.polyline) do - vertex.x = x + vertex.x - vertex.y = y + vertex.y + vertex.x = vertex.x + x + vertex.y = vertex.y + y vertex.x, vertex.y = updateVertex(vertex, x, y, cos, sin) end end end end +--- Batch Tiles in Tile Layer for improved draw speed +-- @param layer The Tile Layer +-- @return nil function Map:setSpriteBatches(layer) - local newBatch = framework.newSpriteBatch - local w = framework.getWidth() - local h = framework.getHeight() - local tw = self.tilewidth - local th = self.tileheight - local bw = math.ceil(w / tw) - local bh = math.ceil(h / th) + local newBatch = love.graphics.newSpriteBatch + local w = love.graphics.getWidth() + local h = love.graphics.getHeight() + local tw = self.tilewidth + local th = self.tileheight + local bw = math.ceil(w / tw) + local bh = math.ceil(h / th) + local sx = 1 + local sy = 1 + local ex = layer.width + local ey = layer.height + local ix = 1 + local iy = 1 + + -- Determine order to add tiles to sprite batch + -- Defaults to right-down + if self.renderorder == "right-up" then + sx, ex, ix = sx, ex, 1 + sy, ey, iy = ey, sy, -1 + elseif self.renderorder == "left-down" then + sx, ex, ix = ex, sx, -1 + sy, ey, iy = sy, ey, 1 + elseif self.renderorder == "left-up" then + sx, ex, ix = ex, sx, -1 + sy, ey, iy = ey, sy, -1 + end -- Minimum of 400 tiles per batch if bw < 20 then bw = 20 end if bh < 20 then bh = 20 end - local size = bw * bh - local batches = { - width = bw, - height = bh, - data = {}, + local size = bw * bh + local batches = { + width = bw, + height = bh, + data = {}, } - for y = 1, layer.height do + for y=sy, ey, iy do local by = math.ceil(y / bh) - for x = 1, layer.width do - local tile = layer.data[y][x] - local bx = math.ceil(x / bw) + for x=sx, ex, ix do + local tile = layer.data[y][x] + local bx = math.ceil(x / bw) local id if tile then - local ts = tile.tileset + local ts = tile.tileset local image = self.tilesets[tile.tileset].image - batches.data[ts] = batches.data[ts] or {} - batches.data[ts][by] = batches.data[ts][by] or {} + batches.data[ts] = batches.data[ts] or {} + batches.data[ts][by] = batches.data[ts][by] or {} batches.data[ts][by][bx] = batches.data[ts][by][bx] or newBatch(image, size) local batch = batches.data[ts][by][bx] local tx, ty if self.orientation == "orthogonal" then - tx = x * tw + tile.offset.x - ty = y * th + tile.offset.y - - -- Compensation for scale/rotation shift - if tile.sx < 0 then tx = tx + tw end - if tile.sy < 0 then ty = ty + th end - if tile.r > 0 then tx = tx + tw end - if tile.r < 0 then ty = ty + th end + tx, ty = compensate(tile, x*tw, y*th, tw, th) elseif self.orientation == "isometric" then tx = (x - y) * (tw / 2) + tile.offset.x + layer.width * tw / 2 ty = (x + y) * (th / 2) + tile.offset.y - elseif self.orientation == "staggered" then - if y % 2 == 0 then - tx = x * tw + tw / 2 + tile.offset.x - else - tx = x * tw + tile.offset.x - end + elseif self.orientation == "staggered" or self.orientation == "hexagonal" then + if self.staggeraxis == "y" then + if self.staggerindex == "odd" then + if y % 2 == 0 then + tx = x * tw + tw / 2 + (self.hexsidelength or 0) + tile.offset.x + else + tx = x * tw + (self.hexsidelength or 0) + tile.offset.x + end + else + if y % 2 == 0 then + tx = x * tw + (self.hexsidelength or 0) + tile.offset.x + else + tx = x * tw + tw / 2 + (self.hexsidelength or 0) + tile.offset.x + end + end - ty = y * th / 2 + tile.offset.y + th / 2 + if self.orientation == "hexagonal" then + ty = y * (th - (th - self.hexsidelength) / 2) + tile.offset.y + (th - (th - self.hexsidelength) / 2) + else + ty = y * th / 2 + tile.offset.y + th / 2 + end + else + if self.staggerindex == "odd" then + if x % 2 == 0 then + ty = y * th + th / 2 + (self.hexsidelength or 0) + tile.offset.y + else + ty = y * th + (self.hexsidelength or 0) + tile.offset.y + end + else + if x % 2 == 0 then + ty = y * th + (self.hexsidelength or 0) + tile.offset.y + else + ty = y * th + th / 2 + (self.hexsidelength or 0) + tile.offset.y + end + end + + if self.orientation == "hexagonal" then + tx = x * (tw - (tw - self.hexsidelength) / 2) + tile.offset.x + (tw - (tw - self.hexsidelength) / 2) + else + tx = x * tw / 2 + tile.offset.x + tw / 2 + end + end end id = batch:add(tile.quad, tx, ty, tile.r, tile.sx, tile.sy) self.tileInstances[tile.gid] = self.tileInstances[tile.gid] or {} - table.insert(self.tileInstances[tile.gid], { batch=batch, id=id, gid=tile.gid, x=tx, y=ty }) + table.insert(self.tileInstances[tile.gid], { + layer = layer, + batch = batch, + id = id, + gid = tile.gid, + x = tx, + y = ty, + r = tile.r, + oy = 0 + }) end end end @@ -651,43 +574,67 @@ function Map:setSpriteBatches(layer) layer.batches = batches end +--- Batch Tiles in Object Layer for improved draw speed +-- @param layer The Object Layer +-- @return nil function Map:setObjectSpriteBatches(layer) - local newBatch = framework.newSpriteBatch - local tw = self.tilewidth - local th = self.tileheight - local batches = { - } + local newBatch = love.graphics.newSpriteBatch + local tw = self.tilewidth + local th = self.tileheight + local batches = {} for _, object in ipairs(layer.objects) do if object.gid then - local tile = self.tiles[object.gid] - local ts = tile.tileset + local tile = self.tiles[object.gid] or self:setFlippedGID(object.gid) + local ts = tile.tileset local image = self.tilesets[tile.tileset].image batches[ts] = batches[ts] or newBatch(image, 100) local batch = batches[ts] - local tx = object.x + tw + tile.offset.x - local ty = object.y + tile.offset.y + local tx = object.x + tw + tile.offset.x + local ty = object.y + tile.offset.y + local tr = math.rad(object.rotation) + local oy = 0 -- Compensation for scale/rotation shift - if tile.sx < 0 then tx = tx + tw end - if tile.sy < 0 then ty = ty + th end - if tile.r > 0 then tx = tx + tw end - if tile.r < 0 then ty = ty + th end + if tile.sx == 1 and tile.sy == 1 then + if tr ~= 0 then + ty = ty + th + oy = th + end + else + if tile.sx < 0 then tx = tx + tw end + if tile.sy < 0 then ty = ty + th end + if tr > 0 then tx = tx + tw end + if tr < 0 then ty = ty + th end + end - id = batch:add(tile.quad, tx, ty, tile.r, tile.sx, tile.sy) + id = batch:add(tile.quad, tx, ty, tr, tile.sx, tile.sy, 0, oy) self.tileInstances[tile.gid] = self.tileInstances[tile.gid] or {} - table.insert(self.tileInstances[tile.gid], { batch=batch, id=id, gid=tile.gid, x=tx, y=ty }) + table.insert(self.tileInstances[tile.gid], { + layer = layer, + batch = batch, + id = id, + gid = tile.gid, + x = tx, + y = ty, + r = tr, + oy = oy + }) end end layer.batches = batches end +--- Only draw what is visible on screen for improved draw speed +-- @param tx Translate X axis (in pixels) +-- @param ty Translate Y axis (in pixels) +-- @param w Width of screen (in pixels) +-- @param h Height of screen (in pixels) +-- @return nil function Map:setDrawRange(tx, ty, w, h) - tx = -tx - ty = -ty local tw, th = self.tilewidth, self.tileheight local sx, sy, ex, ey @@ -701,27 +648,30 @@ function Map:setDrawRange(tx, ty, w, h) sy = math.ceil(((ty / (th / 2)) - (tx / (tw / 2))) / 2 - h / th) ex = math.ceil(sx + (h / th) + (w / tw)) ey = math.ceil(sy + (h / th) * 2 + (w / tw)) - elseif self.orientation == "staggered" then + elseif self.orientation == "staggered" or self.orientation == "hexagonal" then sx = math.ceil(tx / tw - 1) sy = math.ceil(ty / th) ex = math.ceil(sx + w / tw + 1) ey = math.ceil(sy + h / th * 2) end - self.drawRange = { - sx = sx, - sy = sy, - ex = ex, - ey = ey, - } + self.drawRange.sx = sx + self.drawRange.sy = sy + self.drawRange.ex = ex + self.drawRange.ey = ey end +--- Create a Custom Layer to place userdata in (such as player sprites) +-- @param name Name of Custom Layer +-- @param index Draw order within Layer stack +-- @return table Custom Layer function Map:addCustomLayer(name, index) + local index = index or #self.layers + 1 local layer = { - type = "customlayer", - name = name, - visible = true, - opacity = 1, + type = "customlayer", + name = name, + visible = true, + opacity = 1, properties = {}, } @@ -734,23 +684,31 @@ function Map:addCustomLayer(name, index) return layer end +--- Convert another Layer into a Custom Layer +-- @param index Index or name of Layer to convert +-- @return table Custom Layer function Map:convertToCustomLayer(index) local layer = assert(self.layers[index], "Layer not found: " .. index) - layer.type = "customlayer" - layer.x = nil - layer.y = nil - layer.width = nil - layer.height = nil - layer.encoding = nil - layer.data = nil - layer.objects = nil - layer.image = nil + layer.type = "customlayer" + layer.x = nil + layer.y = nil + layer.width = nil + layer.height = nil + layer.encoding = nil + layer.data = nil + layer.objects = nil + layer.image = nil function layer:draw() return end function layer:update(dt) return end + + return layer end +--- Remove a Layer from the Layer stack +-- @param index Index or name of Layer to convert +-- @return nil function Map:removeLayer(index) local layer = assert(self.layers[index], "Layer not found: " .. index) @@ -767,27 +725,51 @@ function Map:removeLayer(index) table.remove(self.layers, index) self.layers[name] = nil end + + -- Remove tile instances + if layer.batches then + for gid, tiles in pairs(self.tileInstances) do + for i=#tiles, 1, -1 do + local tile = tiles[i] + if tile.layer == layer then + table.remove(tiles, i) + end + end + end + end + + -- Remove objects + if layer.objects then + for i, object in pairs(self.objects) do + if object.layer == layer then + self.objects[i] = nil + end + end + end end +--- Animate Tiles and update every Layer +-- @param dt Delta Time +-- @return nil function Map:update(dt) - for gid, tile in pairs( self.tiles ) do - local update - local t + for gid, tile in pairs(self.tiles) do + local update = false if tile.animation then - update = false - tile.time = tile.time + dt * 1000 + while tile.time > tonumber(tile.animation[tile.frame].duration) do - tile.time = tile.time - tonumber(tile.animation[tile.frame].duration) + update = true + tile.time = tile.time - tonumber(tile.animation[tile.frame].duration) tile.frame = tile.frame + 1 + if tile.frame > #tile.animation then tile.frame = 1 end - update = true end - if update == true and self.tileInstances[gid] ~= nil then - for _, j in pairs(self.tileInstances[gid]) do - t = self.tiles[tile.animation[tile.frame].tileid + self.tilesets[tile.tileset].firstgid] - j.batch:set( j.id, t.quad, j.x, j.y, 0 ) + + if update and self.tileInstances[tile.gid] then + for _, j in pairs(self.tileInstances[tile.gid]) do + local t = self.tiles[tonumber(tile.animation[tile.frame].tileid) + self.tilesets[tile.tileset].firstgid] + j.batch:set(j.id, t.quad, j.x, j.y, j.r, tile.sx, tile.sy, 0, j.oy) end end end @@ -799,10 +781,17 @@ function Map:update(dt) end end -function Map:draw(sx, sy) - local current_canvas = framework.getCanvas() - framework.setCanvas(self.canvas) - self.canvas:clear() +--- Draw every Layer +-- @return nil +function Map:draw() + local current_canvas = love.graphics.getCanvas() + love.graphics.setCanvas(self.canvas) + if self.canvas.clear then + self.canvas:clear() + else + local r,g,b,a = love.graphics.getBackgroundColor() + love.graphics.clear(r,g,b,a,self.canvas) + end for _, layer in ipairs(self.layers) do if layer.visible and layer.opacity > 0 then @@ -810,20 +799,25 @@ function Map:draw(sx, sy) end end - framework.setCanvas(current_canvas) - - framework.push() - framework.origin() - framework.draw(self.canvas, 0, 0, 0, sx, sy) - framework.pop() + love.graphics.setCanvas(current_canvas) + love.graphics.push() + love.graphics.origin() + love.graphics.draw(self.canvas) + love.graphics.pop() end +--- Draw an individual Layer +-- @param layer The Layer to draw +-- @return nil function Map:drawLayer(layer) - framework.setColor(255, 255, 255, 255 * layer.opacity) + love.graphics.setColor(255, 255, 255, 255 * layer.opacity) layer:draw() - framework.setColor(255, 255, 255, 255) + love.graphics.setColor(255, 255, 255, 255) end +--- Default draw function for Tile Layers +-- @param layer The Tile Layer to draw +-- @return nil function Map:drawTileLayer(layer) if type(layer) == "string" or type(layer) == "number" then layer = self.layers[layer] @@ -837,17 +831,32 @@ function Map:drawTileLayer(layer) local sy = math.ceil((self.drawRange.sy - layer.y / self.tileheight - 1) / bh) local ex = math.ceil((self.drawRange.ex - layer.x / self.tilewidth + 1) / bw) local ey = math.ceil((self.drawRange.ey - layer.y / self.tileheight + 1) / bh) + local ix = 1 + local iy = 1 local mx = math.ceil(self.width / bw) local my = math.ceil(self.height / bh) - for by=sy, ey do - for bx=sx, ex do + -- Determine order to draw batches + -- Defaults to right-down + if self.renderorder == "right-up" then + sx, ex, ix = sx, ex, 1 + sy, ey, iy = ey, sy, -1 + elseif self.renderorder == "left-down" then + sx, ex, ix = ex, sx, -1 + sy, ey, iy = sy, ey, 1 + elseif self.renderorder == "left-up" then + sx, ex, ix = ex, sx, -1 + sy, ey, iy = ey, sy, -1 + end + + for by=sy, ey, iy do + for bx=sx, ex, ix do if bx >= 1 and bx <= mx and by >= 1 and by <= my then for _, batches in pairs(layer.batches.data) do local batch = batches[by] and batches[by][bx] if batch then - framework.draw(batch, math.floor(layer.x), math.floor(layer.y)) + love.graphics.draw(batch, math.floor(layer.x), math.floor(layer.y)) end end end @@ -855,6 +864,9 @@ function Map:drawTileLayer(layer) end end +--- Default draw function for Object Layers +-- @param layer The Object Layer to draw +-- @return nil function Map:drawObjectLayer(layer) if type(layer) == "string" or type(layer) == "number" then layer = self.layers[layer] @@ -862,10 +874,10 @@ function Map:drawObjectLayer(layer) assert(layer.type == "objectgroup", "Invalid layer type: " .. layer.type .. ". Layer must be of type: objectgroup") - local line = { 160, 160, 160, 255 * layer.opacity } - local fill = { 160, 160, 160, 255 * layer.opacity * 0.2 } - local shadow = { 0, 0, 0, 255 * layer.opacity } - local reset = { 255, 255, 255, 255 * layer.opacity } + local line = { 160, 160, 160, 255 * layer.opacity } + local fill = { 160, 160, 160, 255 * layer.opacity * 0.2 } + local shadow = { 0, 0, 0, 255 * layer.opacity } + local reset = { 255, 255, 255, 255 * layer.opacity } local function sortVertices(obj) local vertices = {{},{}} @@ -884,35 +896,35 @@ function Map:drawObjectLayer(layer) local vertices = sortVertices(obj) if shape == "polyline" then - framework.setColor(shadow) - framework.line(vertices[2]) - framework.setColor(line) - framework.line(vertices[1]) + love.graphics.setColor(shadow) + love.graphics.line(vertices[2]) + love.graphics.setColor(line) + love.graphics.line(vertices[1]) return elseif shape == "polygon" then - framework.setColor(fill) - if not framework.isConvex(vertices[1]) then - local triangles = framework.triangulate(vertices[1]) + love.graphics.setColor(fill) + if not love.math.isConvex(vertices[1]) then + local triangles = love.math.triangulate(vertices[1]) for _, triangle in ipairs(triangles) do - framework.polygon("fill", triangle) + love.graphics.polygon("fill", triangle) end else - framework.polygon("fill", vertices[1]) + love.graphics.polygon("fill", vertices[1]) end else - framework.setColor(fill) - framework.polygon("fill", vertices[1]) + love.graphics.setColor(fill) + love.graphics.polygon("fill", vertices[1]) end - framework.setColor(shadow) - framework.polygon("line", vertices[2]) - framework.setColor(line) - framework.polygon("line", vertices[1]) + love.graphics.setColor(shadow) + love.graphics.polygon("line", vertices[2]) + love.graphics.setColor(line) + love.graphics.polygon("line", vertices[1]) end for _, object in ipairs(layer.objects) do - if object.shape == "rectangle" then + if object.shape == "rectangle" and not object.gid then drawShape(object.rectangle, "rectangle") elseif object.shape == "ellipse" then drawShape(object.ellipse, "ellipse") @@ -923,12 +935,15 @@ function Map:drawObjectLayer(layer) end end - framework.setColor(reset) + love.graphics.setColor(reset) for _, batch in pairs(layer.batches) do - framework.draw(batch, 0, 0) + love.graphics.draw(batch, 0, 0) end end +--- Default draw function for Image Layers +-- @param layer The Image Layer to draw +-- @return nil function Map:drawImageLayer(layer) if type(layer) == "string" or type(layer) == "number" then layer = self.layers[layer] @@ -937,149 +952,437 @@ function Map:drawImageLayer(layer) assert(layer.type == "imagelayer", "Invalid layer type: " .. layer.type .. ". Layer must be of type: imagelayer") if layer.image ~= "" then - framework.draw(layer.image, layer.x, layer.y) - end -end - -function Map:drawWorldCollision(collision) - for _, obj in ipairs(collision) do - framework.polygon("line", collision.body:getWorldPoints(obj.shape:getPoints())) + love.graphics.draw(layer.image, layer.x, layer.y) end end +--- Resize the drawable area of the Map +-- @param w The new width of the drawable area (in pixels) +-- @param h The new Height of the drawable area (in pixels) +-- @return nil function Map:resize(w, h) - self.canvas = framework:newCanvas(w, h) + w = w or love.graphics.getWidth() + h = h or love.graphics.getHeight() + + self.canvas = love.graphics.newCanvas(w, h) + self.canvas:setFilter("nearest", "nearest") end +--- Create flipped or rotated Tiles based on bitop flags +-- @param gid The flagged Global ID +-- @return table Flipped Tile +function Map:setFlippedGID(gid) + local bit31 = 2147483648 + local bit30 = 1073741824 + local bit29 = 536870912 + local flipX = false + local flipY = false + local flipD = false + local realgid = gid + + if realgid >= bit31 then + realgid = realgid - bit31 + flipX = not flipX + end + + if realgid >= bit30 then + realgid = realgid - bit30 + flipY = not flipY + end + + if realgid >= bit29 then + realgid = realgid - bit29 + flipD = not flipD + end + + local tile = self.tiles[realgid] + local data = { + id = tile.id, + gid = gid, + tileset = tile.tileset, + frame = tile.frame, + time = tile.time, + width = tile.width, + height = tile.height, + offset = tile.offset, + quad = tile.quad, + properties = tile.properties, + terrain = tile.terrain, + animation = tile.animation, + sx = tile.sx, + sy = tile.sy, + r = tile.r, + } + + if flipX then + if flipY and flipD then + data.r = math.rad(-90) + data.sy = -1 + elseif flipY then + data.sx = -1 + data.sy = -1 + elseif flipD then + data.r = math.rad(90) + else + data.sx = -1 + end + elseif flipY then + if flipD then + data.r = math.rad(-90) + else + data.sy = -1 + end + elseif flipD then + data.r = math.rad(90) + data.sy = -1 + end + + self.tiles[gid] = data + + return self.tiles[gid] +end + +--- Get custom properties from Layer +-- @param layer The Layer +-- @return table List of properties +function Map:getLayerProperties(layer) + local l = self.layers[layer] + + if not l then return {} end + + return l.properties +end + +--- Get custom properties from Tile +-- @param layer The Layer that the Tile belongs to +-- @param x The X axis location of the Tile (in tiles) +-- @param y The Y axis location of the Tile (in tiles) +-- @return table List of properties +function Map:getTileProperties(layer, x, y) + local tile = self.layers[layer].data[y][x] + + if not tile then return {} end + + return tile.properties +end + +--- Get custom properties from Object +-- @param layer The Layer that the Object belongs to +-- @param object The index or name of the Object +-- @return table List of properties +function Map:getObjectProperties(layer, object) + local o = self.layers[layer].objects + + if type(object) == "number" then + o = o[object] + else + for _, v in ipairs(o) do + if v.name == object then + o = v + break + end + end + end + + if not o then return {} end + + return o.properties +end + +--- Project isometric position to orthoganal position +-- @param x The X axis location of the point (in pixels) +-- @param y The Y axis location of the point (in pixels) +-- @return number The X axis location of the point (in pixels) +-- @return number The Y axis location of the point (in pixels) function Map:convertIsometricToScreen(x, y) local mw = self.width - local tw, th = self.tilewidth, self.tileheight + local tw = self.tilewidth + local th = self.tileheight local ox = mw * tw / 2 - local sx = (x - y) + ox local sy = (x + y) / 2 return sx, sy end +--- Project orthoganal position to isometric position +-- @param x The X axis location of the point (in pixels) +-- @param y The Y axis location of the point (in pixels) +-- @return number The X axis location of the point (in pixels) +-- @return number The Y axis location of the point (in pixels) function Map:convertScreenToIsometric(x, y) - local mw, mh = self.width, self.height - local tw, th = self.tilewidth, self.tileheight + local mw = self.width + local mh = self.height + local tw = self.tilewidth + local th = self.tileheight local ox = mw * tw / 2 local oy = mh * th / 2 - local tx = (x / 2 + y) - ox / 2 local ty = (-x / 2 + y) + oy return tx, ty end -function Map:convertTileToScreen(x, y) - local tw, th = self.tilewidth, self.tileheight +--- Convert tile space to screen space +-- @param x The X axis location of the point (in tiles) +-- @param y The Y axis location of the point (in tiles) +-- @return number The X axis location of the point (in pixels) +-- @return number The Y axis location of the point (in pixels) +function Map:convertWorldToScreen(x,y) + if self.orientation == "orthogonal" then + local tw = self.tilewidth + local th = self.tileheight + local sx = x * tw + local sy = y * th - local sx = x * tw - local sy = y * th + return sx, sy + elseif self.orientation == "isometric" then + local mw = self.width + local tw = self.tilewidth + local th = self.tileheight + local ox = mw * tw / 2 + local sx = (x - y) * tw / 2 + ox + local sy = (x + y) * th / 2 - return sx, sy + return sx, sy + elseif self.orientation == "staggered" then + local tw = self.tilewidth + local th = self.tileheight + local sx = x * tw + math.abs(math.ceil(y) % 2) * (tw / 2) - (math.ceil(y) % 2 * tw/2) + local sy = y * (th / 2) + th/2 + + return sx, sy + end end -function Map:convertScreenToTile(x, y) - local tw, th = self.tilewidth, self.tileheight +--- Convert screen space to tile space +-- @param x The X axis location of the point (in pixels) +-- @param y The Y axis location of the point (in pixels) +-- @return number The X axis location of the point (in tiles) +-- @return number The Y axis location of the point (in tiles) +function Map:convertScreenToWorld(x,y) + if self.orientation == "orthogonal" then + local tw = self.tilewidth + local th = self.tileheight + local tx = x / tw + local ty = y / th - local tx = x / tw - local ty = y / th + return tx, ty + elseif self.orientation == "isometric" then + local mw = self.width + local tw = self.tilewidth + local th = self.tileheight + local ox = mw * tw / 2 + local tx = y / th + (x - ox) / tw + local ty = y / th - (x - ox) / tw - return tx, ty -end - -function Map:convertIsometricTileToScreen(x, y) - local mw = self.width - local tw, th = self.tilewidth, self.tileheight - local ox = mw * tw / 2 - - local sx = (x - y) * tw / 2 + ox - local sy = (x + y) * th / 2 - - return sx, sy -end - -function Map:convertScreenToIsometricTile(x, y) - local mw = self.width - local tw, th = self.tilewidth, self.tileheight - local ox = mw * tw / 2 - - local tx = y / th + (x - ox) / tw - local ty = y / th - (x - ox) / tw - - return tx, ty -end - -function Map:convertStaggeredTileToScreen(x, y) - local tw, th = self.tilewidth, self.tileheight - - local sx = x * tw + math.abs(math.ceil(y) % 2) * (tw / 2) - (math.ceil(y) % 2 * tw/2) - local sy = y * (th / 2) + th/2 - - return sx, sy -end - -function Map:convertScreenToStaggeredTile(x, y) - local function topLeft(x, y) - if (math.ceil(y) % 2) then - return x, y - 1 - else - return x - 1, y - 1 + return tx, ty + elseif self.orientation == "staggered" then + local function topLeft(x, y) + if (math.ceil(y) % 2) then + return x, y - 1 + else + return x - 1, y - 1 + end end - end - local function topRight(x, y) - if (math.ceil(y) % 2) then - return x + 1, y - 1 - else - return x, y - 1 + local function topRight(x, y) + if (math.ceil(y) % 2) then + return x + 1, y - 1 + else + return x, y - 1 + end end - end - local function bottomLeft(x, y) - if (math.ceil(y) % 2) then - return x, y + 1 - else - return x - 1, y + 1 + local function bottomLeft(x, y) + if (math.ceil(y) % 2) then + return x, y + 1 + else + return x - 1, y + 1 + end end - end - local function bottomRight(x, y) - if (math.ceil(y) % 2) then - return x + 1, y + 1 - else - return x, y + 1 + local function bottomRight(x, y) + if (math.ceil(y) % 2) then + return x + 1, y + 1 + else + return x, y + 1 + end end + + local tw = self.tilewidth + local th = self.tileheight + local hh = th / 2 + local ratio = th / tw + local tx = x / tw + local ty = y / th * 2 + local ctx = math.ceil(x / tw) + local cty = math.ceil(y / th) * 2 + local rx = x - ctx * tw + local ry = y - (cty / 2) * th + + if (hh - rx * ratio > ry) then + return topLeft(tx, ty) + elseif (-hh + rx * ratio > ry) then + return topRight(tx, ty) + elseif (hh + rx * ratio < ry) then + return bottomLeft(tx, ty) + elseif (hh * 3 - rx * ratio < ry) then + return bottomRight(tx, ty) + end + + return tx, ty end - - local tw, th = self.tilewidth, self.tileheight - local hh = th / 2 - local ratio = th / tw - - local tx = x / tw - local ty = y / th * 2 - - local ctx = math.ceil(x / tw) - local cty = math.ceil(y / th) * 2 - - local rx = x - ctx * tw - local ry = y - (cty / 2) * th - - if (hh - rx * ratio > ry) then - return topLeft(tx, ty) - elseif (-hh + rx * ratio > ry) then - return topRight(tx, ty) - elseif (hh + rx * ratio < ry) then - return bottomLeft(tx, ty) - elseif (hh * 3 - rx * ratio < ry) then - return bottomRight(tx, ty) - end - - return tx, ty end return Map + +--- A list of individual layers indexed both by draw order and name +-- @table Map.layers +-- @see TileLayer +-- @see ObjectLayer +-- @see ImageLayer +-- @see CustomLayer + +--- A list of individual tiles indexed by Global ID +-- @table Map.tiles +-- @see Tile +-- @see Map.tileInstances + +--- A list of tile instances indexed by Global ID +-- @table Map.tileInstances +-- @see TileInstance +-- @see Tile +-- @see Map.tiles + +--- A list of individual objects indexed by Global ID +-- @table Map.objects +-- @see Object + +--- @table TileLayer +-- @field name The name of the layer +-- @field x Position on the X axis (in pixels) +-- @field y Position on the Y axis (in pixels) +-- @field width Width of layer (in tiles) +-- @field height Height of layer (in tiles) +-- @field visible Toggle if layer is visible or hidden +-- @field opacity Opacity of layer +-- @field properties Custom properties +-- @field data A two dimensional table filled with individual tiles indexed by [y][x] (in tiles) +-- @field update Update function +-- @field draw Draw function +-- @see Map.layers +-- @see Tile + +--- @table ObjectLayer +-- @field name The name of the layer +-- @field x Position on the X axis (in pixels) +-- @field y Position on the Y axis (in pixels) +-- @field visible Toggle if layer is visible or hidden +-- @field opacity Opacity of layer +-- @field properties Custom properties +-- @field objects List of objects indexed by draw order +-- @field update Update function +-- @field draw Draw function +-- @see Map.layers +-- @see Object + +--- @table ImageLayer +-- @field name The name of the layer +-- @field x Position on the X axis (in pixels) +-- @field y Position on the Y axis (in pixels) +-- @field visible Toggle if layer is visible or hidden +-- @field opacity Opacity of layer +-- @field properties Custom properties +-- @field image Image to be drawn +-- @field update Update function +-- @field draw Draw function +-- @see Map.layers + +--- Custom Layers are used to place userdata such as sprites within the draw order of the map. +-- @table CustomLayer +-- @field name The name of the layer +-- @field x Position on the X axis (in pixels) +-- @field y Position on the Y axis (in pixels) +-- @field visible Toggle if layer is visible or hidden +-- @field opacity Opacity of layer +-- @field properties Custom properties +-- @field update Update function +-- @field draw Draw function +-- @see Map.layers +-- @usage +-- -- Create a Custom Layer +-- local spriteLayer = map:addCustomLayer("Sprite Layer", 3) +-- +-- -- Add data to Custom Layer +-- spriteLayer.sprites = { +-- player = { +-- image = love.graphics.newImage("assets/sprites/player.png"), +-- x = 64, +-- y = 64, +-- r = 0, +-- } +-- } +-- +-- -- Update callback for Custom Layer +-- function spriteLayer:update(dt) +-- for _, sprite in pairs(self.sprites) do +-- sprite.r = sprite.r + math.rad(90 * dt) +-- end +-- end +-- +-- -- Draw callback for Custom Layer +-- function spriteLayer:draw() +-- for _, sprite in pairs(self.sprites) do +-- local x = math.floor(sprite.x) +-- local y = math.floor(sprite.y) +-- local r = sprite.r +-- love.graphics.draw(sprite.image, x, y, r) +-- end +-- end + +--- @table Tile +-- @field id Local ID within Tileset +-- @field gid Global ID +-- @field tileset Tileset ID +-- @field quad Quad object +-- @field properties Custom properties +-- @field terrain Terrain data +-- @field animation Animation data +-- @field frame Current animation frame +-- @field time Time spent on current animation frame +-- @field width Width of tile +-- @field height Height of tile +-- @field sx Scale value on the X axis +-- @field sy Scale value on the Y axis +-- @field r Rotation of tile (in radians) +-- @field offset Offset drawing position +-- @field offset.x Offset value on the X axis +-- @field offset.y Offset value on the Y axis +-- @see Map.tiles + +--- @table TileInstance +-- @field batch Spritebatch the Tile Instance belongs to +-- @field id ID within the spritebatch +-- @field gid Global ID +-- @field x Position on the X axis (in pixels) +-- @field y Position on the Y axis (in pixels) +-- @see Map.tileInstances +-- @see Tile + +--- @table Object +-- @field id Global ID +-- @field name Name of object (non-unique) +-- @field shape Shape of object +-- @field x Position of object on X axis (in pixels) +-- @field y Position of object on Y axis (in pixels) +-- @field width Width of object (in pixels) +-- @field height Heigh tof object (in pixels) +-- @field rotation Rotation of object (in radians) +-- @field visible Toggle if object is visible or hidden +-- @field properties Custom properties +-- @field ellipse List of verticies of specific shape +-- @field rectangle List of verticies of specific shape +-- @field polygon List of verticies of specific shape +-- @field polyline List of verticies of specific shape +-- @see Map.objects diff --git a/examples/vendor/sti/plugins/box2d.lua b/examples/vendor/sti/plugins/box2d.lua new file mode 100755 index 0000000..1230059 --- /dev/null +++ b/examples/vendor/sti/plugins/box2d.lua @@ -0,0 +1,349 @@ +--- Box2D plugin for STI +-- @module box2d +-- @author Landon Manning +-- @copyright 2015 +-- @license MIT/X11 + +return { + box2d_LICENSE = "MIT/X11", + box2d_URL = "https://github.com/karai17/Simple-Tiled-Implementation", + box2d_VERSION = "2.3.2.1", + box2d_DESCRIPTION = "Box2D hooks for STI.", + + --- Initialize Box2D physics world. + -- @param world The Box2D world to add objects to. + -- @return nil + box2d_init = function(map, world) + assert(love.physics, "To use the Box2D plugin, please enable the love.physics module.") + + local body = love.physics.newBody(world, map.offsetx, map.offsety) + local collision = { + body = body, + } + + local function convertEllipseToPolygon(x, y, w, h, max_segments) + local function calc_segments(segments) + local function vdist(a, b) + local c = { + x = a.x - b.x, + y = a.y - b.y, + } + + return c.x * c.x + c.y * c.y + end + + segments = segments or 64 + local vertices = {} + + local v = { 1, 2, math.ceil(segments/4-1), math.ceil(segments/4) } + + local m + if love.physics then + m = love.physics.getMeter() + else + m = 32 + end + + for _, i in ipairs(v) do + local angle = (i / segments) * math.pi * 2 + local px = x + w / 2 + math.cos(angle) * w / 2 + local py = y + h / 2 + math.sin(angle) * h / 2 + + table.insert(vertices, { x = px / m, y = py / m }) + end + + local dist1 = vdist(vertices[1], vertices[2]) + local dist2 = vdist(vertices[3], vertices[4]) + + -- Box2D threshold + if dist1 < 0.0025 or dist2 < 0.0025 then + return calc_segments(segments-2) + end + + return segments + end + + local segments = calc_segments(max_segments) + local vertices = {} + + table.insert(vertices, { x = x + w / 2, y = y + h / 2 }) + + for i=0, segments do + local angle = (i / segments) * math.pi * 2 + local px = x + w / 2 + math.cos(angle) * w / 2 + local py = y + h / 2 + math.sin(angle) * h / 2 + + table.insert(vertices, { x = px, y = py }) + end + + return vertices + end + + local function rotateVertex(v, x, y, cos, sin, oy) + oy = oy or 0 + + local vertex = { + x = v.x, + y = v.y - oy, + } + + vertex.x = vertex.x - x + vertex.y = vertex.y - y + + local vx = cos * vertex.x - sin * vertex.y + local vy = sin * vertex.x + cos * vertex.y + + return vx + x, vy + y + oy + end + + local function addObjectToWorld(objshape, vertices, userdata, object) + local shape + + if objshape == "polyline" then + shape = love.physics.newChainShape(false, unpack(vertices)) + else + shape = love.physics.newPolygonShape(unpack(vertices)) + end + + local fixture = love.physics.newFixture(body, shape) + + fixture:setUserData(userdata) + + if userdata.properties.sensor == "true" then + fixture:setSensor(true) + end + + local obj = { + object = object, + shape = shape, + fixture = fixture, + } + + table.insert(collision, obj) + end + + local function getPolygonVertices(object) + local vertices = {} + for _, vertex in ipairs(object.polygon) do + table.insert(vertices, vertex.x) + table.insert(vertices, vertex.y) + end + + return vertices + end + + local function calculateObjectPosition(object, tile) + local o = { + shape = object.shape, + x = object.dx or object.x, + y = object.dy or object.y, + w = object.width, + h = object.height, + polygon = object.polygon or object.polyline or object.ellipse or object.rectangle + } + + local userdata = { + object = o, + properties = object.properties + } + + if o.shape == "rectangle" then + o.r = object.rotation or 0 + local cos = math.cos(math.rad(o.r)) + local sin = math.sin(math.rad(o.r)) + local oy = 0 + + if object.gid then + local tileset = map.tilesets[map.tiles[object.gid].tileset] + local lid = object.gid - tileset.firstgid + local tile = {} + + -- This fixes a height issue + o.y = o.y + map.tiles[object.gid].offset.y + oy = tileset.tileheight + + for _, t in ipairs(tileset.tiles) do + if t.id == lid then + tile = t + break + end + end + + if tile.objectGroup then + for _, obj in ipairs(tile.objectGroup.objects) do + -- Every object in the tile + calculateObjectPosition(obj, object) + end + + return + else + o.w = map.tiles[object.gid].width + o.h = map.tiles[object.gid].height + end + end + + o.polygon = { + { x=o.x+0, y=o.y+0 }, + { x=o.x+o.w, y=o.y+0 }, + { x=o.x+o.w, y=o.y+o.h }, + { x=o.x+0, y=o.y+o.h }, + } + + for _, vertex in ipairs(o.polygon) do + if map.orientation == "isometric" then + vertex.x, vertex.y = map:convertIsometricToScreen(vertex.x, vertex.y) + end + + vertex.x, vertex.y = rotateVertex(vertex, o.x, o.y, cos, sin, oy) + end + + local vertices = getPolygonVertices(o) + addObjectToWorld(o.shape, vertices, userdata, tile or object) + elseif o.shape == "ellipse" then + if not o.polygon then + o.polygon = convertEllipseToPolygon(o.x, o.y, o.w, o.h) + end + local vertices = getPolygonVertices(o) + local triangles = love.math.triangulate(vertices) + + for _, triangle in ipairs(triangles) do + addObjectToWorld(o.shape, triangle, userdata, tile or object) + end + elseif o.shape == "polygon" then + local vertices = getPolygonVertices(o) + local triangles = love.math.triangulate(vertices) + + for _, triangle in ipairs(triangles) do + addObjectToWorld(o.shape, triangle, userdata, tile or object) + end + elseif o.shape == "polyline" then + local vertices = getPolygonVertices(o) + addObjectToWorld(o.shape, vertices, userdata, tile or object) + end + end + + for _, tile in pairs(map.tiles) do + local tileset = map.tilesets[tile.tileset] + + -- Every object in every instance of a tile + if tile.objectGroup then + if map.tileInstances[tile.gid] then + for _, instance in ipairs(map.tileInstances[tile.gid]) do + for _, object in ipairs(tile.objectGroup.objects) do + object.dx = object.x + instance.x + object.dy = object.y + instance.y + calculateObjectPosition(object, instance) + end + end + end + + -- Every instance of a tile + elseif tile.properties and tile.properties.collidable == "true" and map.tileInstances[tile.gid] then + for _, instance in ipairs(map.tileInstances[tile.gid]) do + local object = { + shape = "rectangle", + x = instance.x, + y = instance.y, + width = tileset.tilewidth, + height = tileset.tileheight, + properties = tile.properties + } + + calculateObjectPosition(object, instance) + end + end + end + + for _, layer in ipairs(map.layers) do + -- Entire layer + if layer.properties.collidable == "true" then + if layer.type == "tilelayer" then + for gid, tiles in pairs(map.tileInstances) do + local tile = map.tiles[gid] + local tileset = map.tilesets[tile.tileset] + + for _, instance in ipairs(tiles) do + if instance.layer == layer then + local object = { + shape = "rectangle", + x = instance.x, + y = instance.y, + width = tileset.tilewidth, + height = tileset.tileheight, + properties = tile.properties + } + + calculateObjectPosition(object, instance) + end + end + end + elseif layer.type == "objectgroup" then + for _, object in ipairs(layer.objects) do + calculateObjectPosition(object) + end + elseif layer.type == "imagelayer" then + local object = { + shape = "rectangle", + x = layer.x or 0, + y = layer.y or 0, + width = layer.width, + height = layer.height, + properties = layer.properties + } + + calculateObjectPosition(object) + end + end + + -- Individual objects + if layer.type == "objectgroup" then + for _, object in ipairs(layer.objects) do + if object.properties.collidable == "true" then + calculateObjectPosition(object) + end + end + end + end + + map.box2d_collision = collision + end, + + --- Remove Box2D fixtures and shapes from world. + -- @param index The index or name of the layer being removed + -- @return nil + box2d_removeLayer = function(map, index) + local layer = assert(map.layers[index], "Layer not found: " .. index) + local collision = map.box2d_collision + + -- Remove collision objects + for i=#collision, 1, -1 do + local obj = collision[i] + + if obj.object.layer == layer then + obj.fixture:destroy() + table.remove(collision, i) + end + end + end, + + --- Draw Box2D physics world. + -- @return nil + box2d_draw = function(map) + local collision = map.box2d_collision + + for _, obj in ipairs(collision) do + local points = {collision.body:getWorldPoints(obj.shape:getPoints())} + + if #points == 4 then + love.graphics.line(points) + else + love.graphics.polygon("line", points) + end + end + end, +} + +--- Custom Properties in Tiled are used to tell this plugin what to do. +-- @table Properties +-- @field collidable set to "true", can be used on any Layer, Tile, or Object +-- @field sensor set to "true", can be used on any Tile or Object that is also collidable diff --git a/examples/vendor/sti/plugins/bump.lua b/examples/vendor/sti/plugins/bump.lua new file mode 100755 index 0000000..b06fe1b --- /dev/null +++ b/examples/vendor/sti/plugins/bump.lua @@ -0,0 +1,103 @@ +--- Bump.lua plugin for STI +-- @module bump.lua +-- @author David Serrano (BobbyJones|FrenchFryLord) +-- @copyright 2016 +-- @license MIT/X11 + +return { + + bump_LICENSE = "MIT/X11", + bump_URL = "https://github.com/karai17/Simple-Tiled-Implementation", + bump_VERSION = "3.1.5.2", + bump_DESCRIPTION = "Bump hooks for STI.", + + + --- Adds each collidable tile to the Bump world. + -- @param world The Bump world to add objects to. + -- @return collidables table containing the handles to the objects in the Bump world. + bump_init = function(map, world) + + local collidables = {} + + for _, tileset in ipairs(map.tilesets) do + for _, tile in ipairs(tileset.tiles) do + local gid = tileset.firstgid + tile.id + -- Every object in every instance of a tile + if tile.properties and tile.properties.collidable == "true" and map.tileInstances[gid] then + for _, instance in ipairs(map.tileInstances[gid]) do + local t = {properties = tile.properties, x = instance.x + map.offsetx, y = instance.y + map.offsety, width = map.tilewidth, height = map.tileheight, layer = instance.layer } + world:add(t, t.x,t.y, t.width,t.height) + table.insert(collidables,t) + end + end + end + end + + for _, layer in ipairs(map.layers) do + -- Entire layer + if layer.properties.collidable == "true" then + if layer.type == "tilelayer" then + for y, tiles in ipairs(layer.data) do + for x, tile in pairs(tiles) do + local t = {properties = tile.properties, x = x * map.tilewidth + tile.offset.x + map.offsetx, y = y * map.tileheight + tile.offset.y + map.offsety, width = tile.width, height = tile.height, layer = layer } + world:add(t, t.x,t.y, t.width,t.height ) + table.insert(collidables,t) + end + end + elseif layer.type == "imagelayer" then + world:add(layer, layer.x,layer.y, layer.width,layer.height) + table.insert(collidables,layer) + end + end + -- individual collidable objects in a layer that is not "collidable" + -- or whole collidable objects layer + if layer.type == "objectgroup" then + for _, obj in ipairs(layer.objects) do + if (layer.properties and layer.properties.collidable == "true") + or (obj.properties and obj.properties.collidable == "true") then + if obj.shape == "rectangle" then + local t = {properties = obj.properties, x = obj.x, y = obj.y, width = obj.width, height = obj.height, type = obj.type, name = obj.name, id = obj.id, gid = obj.gid, layer = layer } + if obj.gid then t.y = t.y - obj.height end + world:add(t, t.x,t.y, t.width,t.height ) + table.insert(collidables,t) + end -- TODO implement other object shapes? + end + end + end + + end + map.bump_collidables = collidables + end, + + --- Remove layer + -- @params index to layer to be removed + -- @params world bump world the holds the tiles + -- @return nil + bump_removeLayer = function(map, index, world) + local layer = assert(map.layers[index], "Layer not found: " .. index) + local collidables = map.bump_collidables + + -- Remove collision objects + for i=#collidables, 1, -1 do + local obj = collidables[i] + + if obj.layer == layer + and ( + layer.properties.collidable == "true" + or obj.properties.collidable == "true" + ) then + world:remove(obj) + table.remove(collidables, i) + end + end + end, + + --- Draw bump collisions world. + -- @params world bump world holding the tiles geometry + -- @return nil + bump_draw = function(map, world) + for k,collidable in pairs(map.bump_collidables) do + love.graphics.rectangle("line",world:getRect(collidable)) + end + end +} diff --git a/lib/body.lua b/lib/body.lua index 8cd19ec..52a12c8 100644 --- a/lib/body.lua +++ b/lib/body.lua @@ -525,8 +525,9 @@ function body:setShadowType(type, ...) {0.0, self.height, 0.0, 1.0} } if not self.shadowMesh then - self.shadowMesh = love.graphics.newMesh(self.shadowVert, self.img, "fan") - self.shadowMesh:setVertexColors(true) + self.shadowMesh = love.graphics.newMesh(self.shadowVert) + self.shadowMesh:setTexture(self.img) + self.shadowMesh:setAttributeEnabled("VertexColor", true) end else self.width = 64 diff --git a/lib/postshader.lua b/lib/postshader.lua index 0f7bd4b..efcc6d5 100644 --- a/lib/postshader.lua +++ b/lib/postshader.lua @@ -94,7 +94,7 @@ function post_shader:drawBloom(canvas, args) util.process(self.back_buffer, {shader = shaders['blurh'][1]}) util.process(self.back_buffer, {shader = shaders['contrast'][1]}) util.process(canvas, {shader = shaders['contrast'][1]}) - util.drawCanvasToCanvas(self.back_buffer, canvas, {blendmode = "additive", color = {255, 255, 255, (args[2] or 0.25) * 255}}) + util.drawCanvasToCanvas(self.back_buffer, canvas, {blendmode = "add", color = {255, 255, 255, (args[2] or 0.25) * 255}}) end function post_shader:drawBlur(canvas, args)