parent
1286d93daa
commit
a3f90645fd
Binary file not shown.
After Width: | Height: | Size: 18 KiB |
@ -1,6 +1,6 @@ |
|||||||
--Config file for the game |
--Config file for the game |
||||||
function love.conf(t) |
function love.conf(t) |
||||||
t.window.width = 1600 |
t.window.width = 1600 |
||||||
t.window.height = 900 |
t.window.height = 960 |
||||||
t.window.title = "Game" |
t.window.title = "Game" |
||||||
end |
end |
||||||
|
@ -0,0 +1,159 @@ |
|||||||
|
---- Texture atlas complement for the Simple Tiled Implementation |
||||||
|
-- @copyright 2022 |
||||||
|
-- @author Eduardo Hernández coz.eduardo.hernandez@gmail.com |
||||||
|
-- @license MIT/X11 |
||||||
|
|
||||||
|
local module = {} |
||||||
|
|
||||||
|
--- Create a texture atlas |
||||||
|
-- @param files Array with filenames |
||||||
|
-- @param sort If "size" will sort by size, or if "id" will sort by id |
||||||
|
-- @param ids Array with ids of each file |
||||||
|
-- @param pow2 If true, will force a power of 2 size |
||||||
|
function module.Atlas( files, sort, ids, pow2 ) |
||||||
|
|
||||||
|
local function Node(x, y, w, h) |
||||||
|
return {x = x, y = y, w = w, h = h} |
||||||
|
end |
||||||
|
|
||||||
|
local function nextpow2( n ) |
||||||
|
local res = 1 |
||||||
|
while res <= n do |
||||||
|
res = res * 2 |
||||||
|
end |
||||||
|
return res |
||||||
|
end |
||||||
|
|
||||||
|
local function loadImgs() |
||||||
|
local images = {} |
||||||
|
for i = 1, #files do |
||||||
|
images[i] = {} |
||||||
|
--images[i].name = files[i] |
||||||
|
if ids then images[i].id = ids[i] end |
||||||
|
images[i].img = love.graphics.newImage( files[i] ) |
||||||
|
images[i].w = images[i].img:getWidth() |
||||||
|
images[i].h = images[i].img:getHeight() |
||||||
|
images[i].area = images[i].w * images[i].h |
||||||
|
end |
||||||
|
if sort == "size" or sort == "id" then |
||||||
|
table.sort( images, function( a, b ) return ( a.area > b.area ) end ) |
||||||
|
end |
||||||
|
return images |
||||||
|
end |
||||||
|
|
||||||
|
--TODO: understand this func |
||||||
|
local function add(root, id, w, h) |
||||||
|
if root.left or root.right then |
||||||
|
if root.left then |
||||||
|
local node = add(root.left, id, w, h) |
||||||
|
if node then return node end |
||||||
|
end |
||||||
|
if root.right then |
||||||
|
local node = add(root.right, id, w, h) |
||||||
|
if node then return node end |
||||||
|
end |
||||||
|
return nil |
||||||
|
end |
||||||
|
|
||||||
|
if w > root.w or h > root.h then return nil end |
||||||
|
|
||||||
|
local _w, _h = root.w - w, root.h - h |
||||||
|
|
||||||
|
if _w <= _h then |
||||||
|
root.left = Node(root.x + w, root.y, _w, h) |
||||||
|
root.right = Node(root.x, root.y + h, root.w, _h) |
||||||
|
else |
||||||
|
root.left = Node(root.x, root.y + h, w, _h) |
||||||
|
root.right = Node(root.x + w, root.y, _w, root.h) |
||||||
|
end |
||||||
|
|
||||||
|
root.w = w |
||||||
|
root.h = h |
||||||
|
root.id = id |
||||||
|
|
||||||
|
return root |
||||||
|
end |
||||||
|
|
||||||
|
local function unmap(root) |
||||||
|
if not root then return {} end |
||||||
|
|
||||||
|
local tree = {} |
||||||
|
if root.id then |
||||||
|
tree[root.id] = {} |
||||||
|
tree[root.id].x, tree[root.id].y = root.x, root.y |
||||||
|
end |
||||||
|
|
||||||
|
local left = unmap(root.left) |
||||||
|
local right = unmap(root.right) |
||||||
|
|
||||||
|
for k, v in pairs(left) do |
||||||
|
tree[k] = {} |
||||||
|
tree[k].x, tree[k].y = v.x, v.y |
||||||
|
end |
||||||
|
for k, v in pairs(right) do |
||||||
|
tree[k] = {} |
||||||
|
tree[k].x, tree[k].y = v.x, v.y |
||||||
|
end |
||||||
|
|
||||||
|
return tree |
||||||
|
end |
||||||
|
|
||||||
|
local function bake() |
||||||
|
local images = loadImgs() |
||||||
|
|
||||||
|
local root = {} |
||||||
|
local w, h = images[1].w, images[1].h |
||||||
|
|
||||||
|
if pow2 then |
||||||
|
if w % 1 == 0 then w = nextpow2(w) end |
||||||
|
if h % 1 == 0 then h = nextpow2(h) end |
||||||
|
end |
||||||
|
|
||||||
|
repeat |
||||||
|
local node |
||||||
|
|
||||||
|
root = Node(0, 0, w, h) |
||||||
|
|
||||||
|
for i = 1, #images do |
||||||
|
node = add(root, i, images[i].w, images[i].h) |
||||||
|
if not node then break end |
||||||
|
end |
||||||
|
|
||||||
|
if not node then |
||||||
|
if h <= w then |
||||||
|
if pow2 then h = h * 2 else h = h + 1 end |
||||||
|
else |
||||||
|
if pow2 then w = w * 2 else w = w + 1 end |
||||||
|
end |
||||||
|
else |
||||||
|
break |
||||||
|
end |
||||||
|
until false |
||||||
|
|
||||||
|
local limits = love.graphics.getSystemLimits() |
||||||
|
if w > limits.texturesize or h > limits.texturesize then |
||||||
|
return "Resulting texture is too large for this system" |
||||||
|
end |
||||||
|
|
||||||
|
local coords = unmap(root) |
||||||
|
local map = love.graphics.newCanvas(w, h) |
||||||
|
love.graphics.setCanvas( map ) |
||||||
|
-- love.graphics.clear() |
||||||
|
|
||||||
|
for i = 1, #images do |
||||||
|
love.graphics.draw(images[i].img, coords[i].x, coords[i].y) |
||||||
|
if ids then coords[i].id = images[i].id end |
||||||
|
end |
||||||
|
love.graphics.setCanvas() |
||||||
|
|
||||||
|
if sort == "ids" then |
||||||
|
table.sort( coords, function( a, b ) return ( a.id < b.id ) end ) |
||||||
|
end |
||||||
|
|
||||||
|
return { image = map, coords = coords } |
||||||
|
end |
||||||
|
|
||||||
|
return bake() |
||||||
|
end |
||||||
|
|
||||||
|
return module |
@ -0,0 +1,132 @@ |
|||||||
|
local lg = _G.love.graphics |
||||||
|
local graphics = { isCreated = lg and true or false } |
||||||
|
|
||||||
|
function graphics.newSpriteBatch(...) |
||||||
|
if graphics.isCreated then |
||||||
|
return lg.newSpriteBatch(...) |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
function graphics.newCanvas(...) |
||||||
|
if graphics.isCreated then |
||||||
|
return lg.newCanvas(...) |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
function graphics.newImage(...) |
||||||
|
if graphics.isCreated then |
||||||
|
return lg.newImage(...) |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
function graphics.newQuad(...) |
||||||
|
if graphics.isCreated then |
||||||
|
return lg.newQuad(...) |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
function graphics.getCanvas(...) |
||||||
|
if graphics.isCreated then |
||||||
|
return lg.getCanvas(...) |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
function graphics.setCanvas(...) |
||||||
|
if graphics.isCreated then |
||||||
|
return lg.setCanvas(...) |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
function graphics.clear(...) |
||||||
|
if graphics.isCreated then |
||||||
|
return lg.clear(...) |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
function graphics.push(...) |
||||||
|
if graphics.isCreated then |
||||||
|
return lg.push(...) |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
function graphics.origin(...) |
||||||
|
if graphics.isCreated then |
||||||
|
return lg.origin(...) |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
function graphics.scale(...) |
||||||
|
if graphics.isCreated then |
||||||
|
return lg.scale(...) |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
function graphics.translate(...) |
||||||
|
if graphics.isCreated then |
||||||
|
return lg.translate(...) |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
function graphics.pop(...) |
||||||
|
if graphics.isCreated then |
||||||
|
return lg.pop(...) |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
function graphics.draw(...) |
||||||
|
if graphics.isCreated then |
||||||
|
return lg.draw(...) |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
function graphics.rectangle(...) |
||||||
|
if graphics.isCreated then |
||||||
|
return lg.rectangle(...) |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
function graphics.getColor(...) |
||||||
|
if graphics.isCreated then |
||||||
|
return lg.getColor(...) |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
function graphics.setColor(...) |
||||||
|
if graphics.isCreated then |
||||||
|
return lg.setColor(...) |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
function graphics.line(...) |
||||||
|
if graphics.isCreated then |
||||||
|
return lg.line(...) |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
function graphics.polygon(...) |
||||||
|
if graphics.isCreated then |
||||||
|
return lg.polygon(...) |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
function graphics.points(...) |
||||||
|
if graphics.isCreated then |
||||||
|
return lg.points(...) |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
function graphics.getWidth() |
||||||
|
if graphics.isCreated then |
||||||
|
return lg.getWidth() |
||||||
|
end |
||||||
|
return 0 |
||||||
|
end |
||||||
|
|
||||||
|
function graphics.getHeight() |
||||||
|
if graphics.isCreated then |
||||||
|
return lg.getHeight() |
||||||
|
end |
||||||
|
return 0 |
||||||
|
end |
||||||
|
|
||||||
|
return graphics |
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,323 @@ |
|||||||
|
--- Box2D plugin for STI |
||||||
|
-- @module box2d |
||||||
|
-- @author Landon Manning |
||||||
|
-- @copyright 2019 |
||||||
|
-- @license MIT/X11 |
||||||
|
|
||||||
|
local love = _G.love |
||||||
|
local utils = require((...):gsub('plugins.box2d', 'utils')) |
||||||
|
local lg = require((...):gsub('plugins.box2d', 'graphics')) |
||||||
|
|
||||||
|
return { |
||||||
|
box2d_LICENSE = "MIT/X11", |
||||||
|
box2d_URL = "https://github.com/karai17/Simple-Tiled-Implementation", |
||||||
|
box2d_VERSION = "2.3.2.7", |
||||||
|
box2d_DESCRIPTION = "Box2D hooks for STI.", |
||||||
|
|
||||||
|
--- Initialize Box2D physics world. |
||||||
|
-- @param world The Box2D world to add objects to. |
||||||
|
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 addObjectToWorld(objshape, vertices, userdata, object) |
||||||
|
local shape |
||||||
|
|
||||||
|
if objshape == "polyline" then |
||||||
|
if #vertices == 4 then |
||||||
|
shape = love.physics.newEdgeShape(unpack(vertices)) |
||||||
|
else |
||||||
|
shape = love.physics.newChainShape(false, unpack(vertices)) |
||||||
|
end |
||||||
|
else |
||||||
|
shape = love.physics.newPolygonShape(unpack(vertices)) |
||||||
|
end |
||||||
|
|
||||||
|
local currentBody = body |
||||||
|
--dynamic are objects/players etc. |
||||||
|
if userdata.properties.dynamic == true then |
||||||
|
currentBody = love.physics.newBody(world, map.offsetx, map.offsety, 'dynamic') |
||||||
|
-- static means it shouldn't move. Things like walls/ground. |
||||||
|
elseif userdata.properties.static == true then |
||||||
|
currentBody = love.physics.newBody(world, map.offsetx, map.offsety, 'static') |
||||||
|
-- kinematic means that the object is static in the game world but effects other bodies |
||||||
|
elseif userdata.properties.kinematic == true then |
||||||
|
currentBody = love.physics.newBody(world, map.offsetx, map.offsety, 'kinematic') |
||||||
|
end |
||||||
|
|
||||||
|
local fixture = love.physics.newFixture(currentBody, shape) |
||||||
|
fixture:setUserData(userdata) |
||||||
|
|
||||||
|
-- Set some custom properties from userdata (or use default set by box2d) |
||||||
|
fixture:setFriction(userdata.properties.friction or 0.2) |
||||||
|
fixture:setRestitution(userdata.properties.restitution or 0.0) |
||||||
|
fixture:setSensor(userdata.properties.sensor or false) |
||||||
|
fixture:setFilterData( |
||||||
|
userdata.properties.categories or 1, |
||||||
|
userdata.properties.mask or 65535, |
||||||
|
userdata.properties.group or 0 |
||||||
|
) |
||||||
|
|
||||||
|
local obj = { |
||||||
|
object = object, |
||||||
|
body = currentBody, |
||||||
|
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) + map.offsetx, |
||||||
|
y = (object.dy or object.y) + map.offsety, |
||||||
|
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 |
||||||
|
} |
||||||
|
|
||||||
|
o.r = object.rotation or 0 |
||||||
|
if o.shape == "rectangle" then |
||||||
|
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 t = {} |
||||||
|
|
||||||
|
-- This fixes a height issue |
||||||
|
o.y = o.y + map.tiles[object.gid].offset.y |
||||||
|
oy = o.h |
||||||
|
|
||||||
|
for _, tt in ipairs(tileset.tiles) do |
||||||
|
if tt.id == lid then |
||||||
|
t = tt |
||||||
|
break |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
if t.objectGroup then |
||||||
|
for _, obj in ipairs(t.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 |
||||||
|
vertex.x, vertex.y = utils.rotate_vertex(map, 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 = utils.convert_ellipse_to_polygon(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 |
||||||
|
-- Recalculate collision polygons inside tiles |
||||||
|
if tile then |
||||||
|
local cos = math.cos(math.rad(o.r)) |
||||||
|
local sin = math.sin(math.rad(o.r)) |
||||||
|
for _, vertex in ipairs(o.polygon) do |
||||||
|
vertex.x = vertex.x + o.x |
||||||
|
vertex.y = vertex.y + o.y |
||||||
|
vertex.x, vertex.y = utils.rotate_vertex(map, vertex, o.x, o.y, cos, sin) |
||||||
|
end |
||||||
|
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 == "polyline" then |
||||||
|
local vertices = getPolygonVertices(o) |
||||||
|
addObjectToWorld(o.shape, vertices, userdata, tile or object) |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
for _, tile in pairs(map.tiles) do |
||||||
|
if map.tileInstances[tile.gid] then |
||||||
|
for _, instance in ipairs(map.tileInstances[tile.gid]) do |
||||||
|
-- Every object in every instance of a tile |
||||||
|
if tile.objectGroup then |
||||||
|
for _, object in ipairs(tile.objectGroup.objects) do |
||||||
|
if object.properties.collidable == true then |
||||||
|
object = utils.deepCopy(object) |
||||||
|
object.dx = instance.x + object.x |
||||||
|
object.dy = instance.y + object.y |
||||||
|
calculateObjectPosition(object, instance) |
||||||
|
end |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
-- Every instance of a tile |
||||||
|
if tile.properties.collidable == true then |
||||||
|
local object = { |
||||||
|
shape = "rectangle", |
||||||
|
x = instance.x, |
||||||
|
y = instance.y, |
||||||
|
width = map.tilewidth, |
||||||
|
height = map.tileheight, |
||||||
|
properties = tile.properties |
||||||
|
} |
||||||
|
|
||||||
|
calculateObjectPosition(object, instance) |
||||||
|
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 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 |
||||||
|
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. |
||||||
|
-- @param tx Translate on X |
||||||
|
-- @param ty Translate on Y |
||||||
|
-- @param sx Scale on X |
||||||
|
-- @param sy Scale on Y |
||||||
|
box2d_draw = function(map, tx, ty, sx, sy) |
||||||
|
local collision = map.box2d_collision |
||||||
|
|
||||||
|
lg.push() |
||||||
|
lg.scale(sx or 1, sy or sx or 1) |
||||||
|
lg.translate(math.floor(tx or 0), math.floor(ty or 0)) |
||||||
|
|
||||||
|
for _, obj in ipairs(collision) do |
||||||
|
local points = {obj.body:getWorldPoints(obj.shape:getPoints())} |
||||||
|
local shape_type = obj.shape:getType() |
||||||
|
|
||||||
|
if shape_type == "edge" or shape_type == "chain" then |
||||||
|
love.graphics.line(points) |
||||||
|
elseif shape_type == "polygon" then |
||||||
|
love.graphics.polygon("line", points) |
||||||
|
else |
||||||
|
error("sti box2d plugin does not support "..shape_type.." shapes") |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
lg.pop() |
||||||
|
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 |
||||||
|
-- @field dynamic set to true, can be used on any Tile or Object |
||||||
|
-- @field friction can be used to define the friction of any Object |
||||||
|
-- @field restitution can be used to define the restitution of any Object |
||||||
|
-- @field categories can be used to set the filter Category of any Object |
||||||
|
-- @field mask can be used to set the filter Mask of any Object |
||||||
|
-- @field group can be used to set the filter Group of any Object |
@ -0,0 +1,193 @@ |
|||||||
|
--- Bump.lua plugin for STI |
||||||
|
-- @module bump.lua |
||||||
|
-- @author David Serrano (BobbyJones|FrenchFryLord) |
||||||
|
-- @copyright 2019 |
||||||
|
-- @license MIT/X11 |
||||||
|
|
||||||
|
local lg = require((...):gsub('plugins.bump', 'graphics')) |
||||||
|
|
||||||
|
return { |
||||||
|
bump_LICENSE = "MIT/X11", |
||||||
|
bump_URL = "https://github.com/karai17/Simple-Tiled-Implementation", |
||||||
|
bump_VERSION = "3.1.7.1", |
||||||
|
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 |
||||||
|
|
||||||
|
if map.tileInstances[gid] then |
||||||
|
for _, instance in ipairs(map.tileInstances[gid]) do |
||||||
|
-- Every object in every instance of a tile |
||||||
|
if tile.objectGroup then |
||||||
|
for _, object in ipairs(tile.objectGroup.objects) do |
||||||
|
if object.properties.collidable == true then |
||||||
|
local t = { |
||||||
|
name = object.name, |
||||||
|
type = object.type, |
||||||
|
x = instance.x + map.offsetx + object.x, |
||||||
|
y = instance.y + map.offsety + object.y, |
||||||
|
width = object.width, |
||||||
|
height = object.height, |
||||||
|
layer = instance.layer, |
||||||
|
properties = object.properties |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
world:add(t, t.x, t.y, t.width, t.height) |
||||||
|
table.insert(collidables, t) |
||||||
|
end |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
-- Every instance of a tile |
||||||
|
if tile.properties and tile.properties.collidable == true then |
||||||
|
local t = { |
||||||
|
x = instance.x + map.offsetx, |
||||||
|
y = instance.y + map.offsety, |
||||||
|
width = map.tilewidth, |
||||||
|
height = map.tileheight, |
||||||
|
layer = instance.layer, |
||||||
|
type = tile.type, |
||||||
|
properties = tile.properties |
||||||
|
} |
||||||
|
|
||||||
|
world:add(t, t.x, t.y, t.width, t.height) |
||||||
|
table.insert(collidables, t) |
||||||
|
end |
||||||
|
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 |
||||||
|
|
||||||
|
if tile.objectGroup then |
||||||
|
for _, object in ipairs(tile.objectGroup.objects) do |
||||||
|
if object.properties.collidable == true then |
||||||
|
local t = { |
||||||
|
name = object.name, |
||||||
|
type = object.type, |
||||||
|
x = ((x-1) * map.tilewidth + tile.offset.x + map.offsetx) + object.x, |
||||||
|
y = ((y-1) * map.tileheight + tile.offset.y + map.offsety) + object.y, |
||||||
|
width = object.width, |
||||||
|
height = object.height, |
||||||
|
layer = layer, |
||||||
|
properties = object.properties |
||||||
|
} |
||||||
|
|
||||||
|
world:add(t, t.x, t.y, t.width, t.height) |
||||||
|
table.insert(collidables, t) |
||||||
|
end |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
|
||||||
|
local t = { |
||||||
|
x = (x-1) * map.tilewidth + tile.offset.x + map.offsetx, |
||||||
|
y = (y-1) * map.tileheight + tile.offset.y + map.offsety, |
||||||
|
width = tile.width, |
||||||
|
height = tile.height, |
||||||
|
layer = layer, |
||||||
|
type = tile.type, |
||||||
|
properties = tile.properties |
||||||
|
} |
||||||
|
|
||||||
|
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.collidable == true or obj.properties.collidable == true then |
||||||
|
if obj.shape == "rectangle" then |
||||||
|
local t = { |
||||||
|
name = obj.name, |
||||||
|
type = obj.type, |
||||||
|
x = obj.x + map.offsetx, |
||||||
|
y = obj.y + map.offsety, |
||||||
|
width = obj.width, |
||||||
|
height = obj.height, |
||||||
|
layer = layer, |
||||||
|
properties = obj.properties |
||||||
|
} |
||||||
|
|
||||||
|
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_world = world |
||||||
|
map.bump_collidables = collidables |
||||||
|
end, |
||||||
|
|
||||||
|
--- Remove layer |
||||||
|
-- @param index to layer to be removed |
||||||
|
bump_removeLayer = function(map, index) |
||||||
|
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 |
||||||
|
map.bump_world:remove(obj) |
||||||
|
table.remove(collidables, i) |
||||||
|
end |
||||||
|
end |
||||||
|
end, |
||||||
|
|
||||||
|
--- Draw bump collisions world. |
||||||
|
-- @param world bump world holding the tiles geometry |
||||||
|
-- @param tx Translate on X |
||||||
|
-- @param ty Translate on Y |
||||||
|
-- @param sx Scale on X |
||||||
|
-- @param sy Scale on Y |
||||||
|
bump_draw = function(map, tx, ty, sx, sy) |
||||||
|
lg.push() |
||||||
|
lg.scale(sx or 1, sy or sx or 1) |
||||||
|
lg.translate(math.floor(tx or 0), math.floor(ty or 0)) |
||||||
|
|
||||||
|
local items = map.bump_world:getItems() |
||||||
|
for _, item in ipairs(items) do |
||||||
|
lg.rectangle("line", map.bump_world:getRect(item)) |
||||||
|
end |
||||||
|
|
||||||
|
lg.pop() |
||||||
|
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 |
@ -0,0 +1,217 @@ |
|||||||
|
-- Some utility functions that shouldn't be exposed. |
||||||
|
local utils = {} |
||||||
|
|
||||||
|
-- https://github.com/stevedonovan/Penlight/blob/master/lua/pl/path.lua#L286 |
||||||
|
function utils.format_path(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 k |
||||||
|
|
||||||
|
repeat -- /./ -> / |
||||||
|
path,k = path:gsub(np_pat2,'/',1) |
||||||
|
until k == 0 |
||||||
|
|
||||||
|
repeat -- A/../ -> (empty) |
||||||
|
path,k = path:gsub(np_pat1,'',1) |
||||||
|
until k == 0 |
||||||
|
|
||||||
|
if path == '' then path = '.' end |
||||||
|
|
||||||
|
return path |
||||||
|
end |
||||||
|
|
||||||
|
-- Compensation for scale/rotation shift |
||||||
|
function utils.compensate(tile, tileX, tileY, tileW, tileH) |
||||||
|
local compx = 0 |
||||||
|
local compy = 0 |
||||||
|
|
||||||
|
if tile.sx < 0 then compx = tileW end |
||||||
|
if tile.sy < 0 then compy = tileH end |
||||||
|
|
||||||
|
if tile.r > 0 then |
||||||
|
tileX = tileX + tileH - compy |
||||||
|
tileY = tileY + tileH + compx - tileW |
||||||
|
elseif tile.r < 0 then |
||||||
|
tileX = tileX + compy |
||||||
|
tileY = tileY - compx + tileH |
||||||
|
else |
||||||
|
tileX = tileX + compx |
||||||
|
tileY = tileY + compy |
||||||
|
end |
||||||
|
|
||||||
|
return tileX, tileY |
||||||
|
end |
||||||
|
|
||||||
|
-- Cache images in main STI module |
||||||
|
function utils.cache_image(sti, path, image) |
||||||
|
image = image or love.graphics.newImage(path) |
||||||
|
image:setFilter("nearest", "nearest") |
||||||
|
sti.cache[path] = image |
||||||
|
end |
||||||
|
|
||||||
|
-- We just don't know. |
||||||
|
function utils.get_tiles(imageW, tileW, margin, spacing) |
||||||
|
imageW = imageW - margin |
||||||
|
local n = 0 |
||||||
|
|
||||||
|
while imageW >= tileW do |
||||||
|
imageW = imageW - tileW |
||||||
|
if n ~= 0 then imageW = imageW - spacing end |
||||||
|
if imageW >= 0 then n = n + 1 end |
||||||
|
end |
||||||
|
|
||||||
|
return n |
||||||
|
end |
||||||
|
|
||||||
|
-- Decompress tile layer data |
||||||
|
function utils.get_decompressed_data(data) |
||||||
|
local ffi = require "ffi" |
||||||
|
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 |
||||||
|
|
||||||
|
-- Convert a Tiled ellipse object to a LOVE polygon |
||||||
|
function utils.convert_ellipse_to_polygon(x, y, w, h, max_segments) |
||||||
|
local ceil = math.ceil |
||||||
|
local cos = math.cos |
||||||
|
local sin = math.sin |
||||||
|
|
||||||
|
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, ceil(segments/4-1), ceil(segments/4) } |
||||||
|
|
||||||
|
local m |
||||||
|
if love and 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 + cos(angle) * w / 2 |
||||||
|
local py = y + h / 2 + 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 + cos(angle) * w / 2 |
||||||
|
local py = y + h / 2 + sin(angle) * h / 2 |
||||||
|
|
||||||
|
table.insert(vertices, { x = px, y = py }) |
||||||
|
end |
||||||
|
|
||||||
|
return vertices |
||||||
|
end |
||||||
|
|
||||||
|
function utils.rotate_vertex(map, vertex, x, y, cos, sin, oy) |
||||||
|
if map.orientation == "isometric" then |
||||||
|
x, y = utils.convert_isometric_to_screen(map, x, y) |
||||||
|
vertex.x, vertex.y = utils.convert_isometric_to_screen(map, vertex.x, vertex.y) |
||||||
|
end |
||||||
|
|
||||||
|
vertex.x = vertex.x - x |
||||||
|
vertex.y = vertex.y - y |
||||||
|
|
||||||
|
return |
||||||
|
x + cos * vertex.x - sin * vertex.y, |
||||||
|
y + sin * vertex.x + cos * vertex.y - (oy or 0) |
||||||
|
end |
||||||
|
|
||||||
|
--- Project isometric position to cartesian position |
||||||
|
function utils.convert_isometric_to_screen(map, x, y) |
||||||
|
local mapW = map.width |
||||||
|
local tileW = map.tilewidth |
||||||
|
local tileH = map.tileheight |
||||||
|
local tileX = x / tileH |
||||||
|
local tileY = y / tileH |
||||||
|
local offsetX = mapW * tileW / 2 |
||||||
|
|
||||||
|
return |
||||||
|
(tileX - tileY) * tileW / 2 + offsetX, |
||||||
|
(tileX + tileY) * tileH / 2 |
||||||
|
end |
||||||
|
|
||||||
|
function utils.hex_to_color(hex) |
||||||
|
if hex:sub(1, 1) == "#" then |
||||||
|
hex = hex:sub(2) |
||||||
|
end |
||||||
|
|
||||||
|
return { |
||||||
|
r = tonumber(hex:sub(1, 2), 16) / 255, |
||||||
|
g = tonumber(hex:sub(3, 4), 16) / 255, |
||||||
|
b = tonumber(hex:sub(5, 6), 16) / 255 |
||||||
|
} |
||||||
|
end |
||||||
|
|
||||||
|
function utils.pixel_function(_, _, r, g, b, a) |
||||||
|
local mask = utils._TC |
||||||
|
|
||||||
|
if r == mask.r and |
||||||
|
g == mask.g and |
||||||
|
b == mask.b then |
||||||
|
return r, g, b, 0 |
||||||
|
end |
||||||
|
|
||||||
|
return r, g, b, a |
||||||
|
end |
||||||
|
|
||||||
|
function utils.fix_transparent_color(tileset, path) |
||||||
|
local image_data = love.image.newImageData(path) |
||||||
|
tileset.image = love.graphics.newImage(image_data) |
||||||
|
|
||||||
|
if tileset.transparentcolor then |
||||||
|
utils._TC = utils.hex_to_color(tileset.transparentcolor) |
||||||
|
|
||||||
|
image_data:mapPixel(utils.pixel_function) |
||||||
|
tileset.image = love.graphics.newImage(image_data) |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
function utils.deepCopy(t) |
||||||
|
local copy = {} |
||||||
|
for k,v in pairs(t) do |
||||||
|
if type(v) == "table" then |
||||||
|
v = utils.deepCopy(v) |
||||||
|
end |
||||||
|
copy[k] = v |
||||||
|
end |
||||||
|
return copy |
||||||
|
end |
||||||
|
|
||||||
|
return utils |
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,58 @@ |
|||||||
|
<?xml version="1.0" encoding="UTF-8"?> |
||||||
|
<map version="1.10" tiledversion="1.10.2" orientation="orthogonal" renderorder="right-down" width="25" height="15" tilewidth="64" tileheight="64" infinite="0" nextlayerid="5" nextobjectid="24"> |
||||||
|
<editorsettings> |
||||||
|
<export target="map.lua" format="lua"/> |
||||||
|
</editorsettings> |
||||||
|
<tileset firstgid="1" name="floor-tiles" tilewidth="64" tileheight="64" tilecount="54" columns="9"> |
||||||
|
<image source="../../../../../../../../../../Downloads/tileset.png" width="576" height="384"/> |
||||||
|
</tileset> |
||||||
|
<layer id="1" name="Floor + Outer Walls" width="25" height="15"> |
||||||
|
<data encoding="csv"> |
||||||
|
4,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,6, |
||||||
|
13,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,15, |
||||||
|
13,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,15, |
||||||
|
13,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,15, |
||||||
|
13,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,15, |
||||||
|
13,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,15, |
||||||
|
13,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,15, |
||||||
|
13,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,15, |
||||||
|
13,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,15, |
||||||
|
13,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,15, |
||||||
|
13,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,15, |
||||||
|
13,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,15, |
||||||
|
13,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,15, |
||||||
|
13,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,15, |
||||||
|
22,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,24 |
||||||
|
</data> |
||||||
|
</layer> |
||||||
|
<layer id="2" name="Inner Walls" width="25" height="15"> |
||||||
|
<data encoding="csv"> |
||||||
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, |
||||||
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,9,0,0, |
||||||
|
0,0,0,0,0,0,0,0,7,8,8,8,9,0,0,0,0,0,0,0,0,16,18,0,0, |
||||||
|
0,0,0,0,0,0,0,0,25,26,26,26,27,0,0,0,0,0,0,0,0,25,27,0,0, |
||||||
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, |
||||||
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, |
||||||
|
0,0,0,0,7,8,9,0,0,7,8,8,8,8,8,9,0,0,0,0,0,0,0,0,0, |
||||||
|
0,0,7,8,17,17,18,0,0,16,17,17,17,17,17,18,0,0,0,0,0,0,0,0,0, |
||||||
|
0,0,16,17,17,26,26,8,8,17,26,26,26,26,26,27,0,0,0,0,0,0,0,0,0, |
||||||
|
0,0,16,17,18,0,0,16,17,18,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, |
||||||
|
0,0,16,17,18,0,0,25,26,27,0,0,0,0,0,0,0,0,7,8,8,8,9,0,0, |
||||||
|
0,0,16,17,27,0,0,0,0,0,0,0,7,8,9,0,0,0,16,17,17,17,18,0,0, |
||||||
|
0,0,25,27,0,0,0,0,0,0,0,0,16,17,18,0,0,0,16,17,17,17,18,0,0, |
||||||
|
0,0,0,0,0,0,0,0,0,0,0,0,25,26,27,0,0,0,25,26,26,26,27,0,0, |
||||||
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 |
||||||
|
</data> |
||||||
|
</layer> |
||||||
|
<objectgroup id="4" name="Walls" visible="0"> |
||||||
|
<object id="10" x="512" y="128" width="320" height="128"/> |
||||||
|
<object id="11" x="1344" y="64" width="128" height="192"/> |
||||||
|
<object id="12" x="1152" y="640" width="320" height="256"/> |
||||||
|
<object id="13" x="768" y="704" width="192" height="192"/> |
||||||
|
<object id="19" x="576" y="384" width="448" height="192"/> |
||||||
|
<object id="20" x="448" y="512" width="192" height="192"/> |
||||||
|
<object id="21" x="256" y="384" width="192" height="192"/> |
||||||
|
<object id="22" x="128" y="448" width="192" height="320"/> |
||||||
|
<object id="23" x="128" y="768" width="128" height="64"/> |
||||||
|
</objectgroup> |
||||||
|
</map> |
Loading…
Reference in new issue