diff --git a/src/data/maps/test.lua b/src/data/maps/test.lua index cb38674..4180ae3 100644 --- a/src/data/maps/test.lua +++ b/src/data/maps/test.lua @@ -16,7 +16,8 @@ return { { name = "test", firstgid = 1, - filename = "../tilesets/test.tsx" + filename = "../tilesets/test.tsx", + exportfilename = "../tilesets/test.lua" } }, layers = { diff --git a/src/data/tilesets/test.lua b/src/data/tilesets/test.lua index 0f3f9d3..8db211e 100644 --- a/src/data/tilesets/test.lua +++ b/src/data/tilesets/test.lua @@ -27,5 +27,12 @@ return { properties = {}, wangsets = {}, tilecount = 36, - tiles = {} + tiles = { + { + id = 0, + properties = { + ["collidable"] = true + } + } + } } diff --git a/src/data/tilesets/test.tsx b/src/data/tilesets/test.tsx index bae548a..0fac7ec 100644 --- a/src/data/tilesets/test.tsx +++ b/src/data/tilesets/test.tsx @@ -1,4 +1,12 @@ + + + + + + + + diff --git a/src/lib/sti/plugins/bump.lua b/src/lib/sti/plugins/bump.lua index 1d4b828..905f5f2 100644 --- a/src/lib/sti/plugins/bump.lua +++ b/src/lib/sti/plugins/bump.lua @@ -6,6 +6,30 @@ local lg = require((...):gsub('plugins.bump', 'graphics')) +local function getKeys(t) + local keys = {} + for k in pairs(t) do + table.insert(keys, k) + end + return keys +end + +-- The highest 4 bits in 32bit tile ids are used for marking if that tile is +-- flipped, so to get the real id, you first need to remove those bits using +-- this mask. +local bit = require("bit") +local GID_MASK = bit.bnot(bit.lshift(15, 28)) + +local function findTileFromTilesets(tilesets, gid) + for _, tileset in ipairs(tilesets) do + for _, tile in ipairs(tileset.tiles) do + if tileset.firstgid + tile.id == gid then + return tile + end + end + end +end + return { bump_LICENSE = "MIT/X11", bump_URL = "https://github.com/karai17/Simple-Tiled-Implementation", @@ -18,44 +42,24 @@ return { 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 + for _, gid in ipairs(getKeys(map.tileInstances)) do + local id = bit.band(gid, GID_MASK) + local tile = findTileFromTilesets(map.tilesets, 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 + 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 = { - x = instance.x + map.offsetx, - y = instance.y + map.offsety, - width = map.tilewidth, - height = map.tileheight, + 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, - type = tile.type, - properties = tile.properties + properties = object.properties } world:add(t, t.x, t.y, t.width, t.height) @@ -63,6 +67,39 @@ return { end end end + + -- Every instance of a tile + if tile.properties and tile.properties.collidable == true then + local tileProperties = map.tiles[gid] + local x = instance.x + map.offsetx + local y = instance.y + map.offsety + local sx = tileProperties.sx + local sy = tileProperties.sy + + -- Width and height can only be positive in bump, to get around this + -- For negative scaling just move the position back instead + if sx < 1 then + sx = -sx + x = x - map.tilewidth * sx + end + if sy < 1 then + sy = -sy + x = x - map.tileheight * sy + end + + local t = { + x = x, + y = y, + width = map.tilewidth * sx, + height = map.tileheight * sy, + 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 diff --git a/src/states/main.lua b/src/states/main.lua index a037c9c..ecae079 100644 --- a/src/states/main.lua +++ b/src/states/main.lua @@ -13,7 +13,8 @@ function MainState:enter(_, host_socket) }}, controllable_player = {filter = {"controllable"}}, sprite = {filter = {"sprite"}}, - bolt = {filter={"pos", "vel", "bolt"}} + bolt = {filter={"pos", "vel", "bolt"}}, + collider = {filter={"pos", "collider"}} } local systems = { @@ -21,9 +22,13 @@ function MainState:enter(_, host_socket) require("systems.map"), require("systems.player"), require("systems.sprite"), - require("systems.screen-scaler"), + require("systems.screen-scaler") } + if not love.filesystem.isFused() then + table.insert(systems, require("systems.debug")) + end + if host_socket then table.insert(systems, require("systems.multiplayer")) end @@ -106,6 +111,7 @@ function MainState:draw() -- Draw UI on top local w, h = self.downscaled_canvas:getDimensions() ScreenScaler:start(1000, h/w * 1000) + love.graphics.setColor(1, 1, 1) love.graphics.print("Hello World!") ScreenScaler:finish() diff --git a/src/systems/debug.lua b/src/systems/debug.lua new file mode 100644 index 0000000..7952057 --- /dev/null +++ b/src/systems/debug.lua @@ -0,0 +1,45 @@ +local Debug = {} +local rgb = require("helpers.rgb") + +local DRAW_GRID = false +local GRID_COLOR = rgb(30, 30, 30) + +local DRAW_COLLIDERS = true +local COLLIDER_COLOR = rgb(200, 20, 200) + +function Debug:drawColliders() + local physics = self.pool:getSystem(require("systems.physics")) + love.graphics.setColor(COLLIDER_COLOR) + + local bump = physics.bump + local items = bump:getItems() + for _, item in ipairs(items) do + love.graphics.rectangle("line", bump:getRect(item)) + end +end + +function Debug:drawGrid() + local map = self.pool:getSystem(require("systems.map")) + if not map.map then return end + + local scaler = self.pool:getSystem(require("systems.screen-scaler")) + local w, h = scaler:getDimensions() + love.graphics.setColor(GRID_COLOR) + for x=0, w, map.map.tilewidth do + love.graphics.line(x, 0, x, h) + end + for y=0, h, map.map.tileheight do + love.graphics.line(0, y, w, y) + end +end + +function Debug:draw() + if DRAW_GRID then + self:drawGrid() + end + if DRAW_COLLIDERS then + self:drawColliders() + end +end + +return Debug diff --git a/src/systems/map.lua b/src/systems/map.lua index febc0ea..2b0889c 100644 --- a/src/systems/map.lua +++ b/src/systems/map.lua @@ -1,12 +1,8 @@ -local rgb = require("helpers.rgb") -local sti = require("lib.sti") local Map = {} - -local DEBUG_GRID = true -local DEBUG_GRID_COLOR = rgb(30, 30, 30) +local sti = require("lib.sti") function Map:init() - self.map = sti("data/maps/test.lua") + self.map = sti("data/maps/test.lua", { "bump" }) self.pool:emit("onMapSwitch", self.map) end @@ -15,16 +11,6 @@ function Map:update(dt) end function Map:draw() - if DEBUG_GRID and self.map then - local w, h = love.graphics.getDimensions() - love.graphics.setColor(DEBUG_GRID_COLOR) - for x=0, w, self.map.tilewidth do - love.graphics.line(x, 0, x, h) - end - for y=0, h, self.map.tileheight do - love.graphics.line(0, y, w, y) - end - end love.graphics.setColor(1, 1, 1) self.map:draw() end diff --git a/src/systems/physics.lua b/src/systems/physics.lua index 2cf43c1..1dde92a 100644 --- a/src/systems/physics.lua +++ b/src/systems/physics.lua @@ -1,4 +1,53 @@ local Physics = {} +local bump = require("lib.bump") +local Vec = require("lib.brinevector") + +-- TODO: Tweak bump world `cellSize` at runtime, +-- when the map switches + +function Physics:init() + self.bump = bump.newWorld() +end + +function Physics:onMapSwitch(map) + map:bump_init(self.bump) +end + +local function getColliderBounds(e) + local x = e.pos.x + e.collider[1] + local y = e.pos.y + e.collider[2] + local w = math.abs(e.collider[3] - e.collider[1]) + local h = math.abs(e.collider[4] - e.collider[2]) + return x, y, w, h +end + +function Physics:addToGroup(group, e) + if group == "collider" then + self.bump:add(e, getColliderBounds(e)) + end +end + +function Physics:removeFromGroup(group, e) + if group == "collider" then + self.bump:remove(e) + end +end + +local function collisionFilter(entity, other) + if entity.vel or other.vel then + return "slide" + end +end + +function Physics:resolveCollisions(e, dt) + local targetPos = e.pos + e.vel * dt + local ox = e.collider[1] + local oy = e.collider[2] + local x, y = self.bump:move(e, targetPos.x+ox, targetPos.y+oy, collisionFilter) + + local step = Vec(x-ox, y-oy) - e.pos + e.vel = step / dt +end function Physics:update(dt) for _, e in ipairs(self.pool.groups.physical.entities) do @@ -9,6 +58,10 @@ function Physics:update(dt) e.vel = e.vel * (1 - math.min(e.friction, 1)) ^ dt end + if self.pool.groups.collider.hasEntity[e] then + self:resolveCollisions(e, dt) + end + e.pos = e.pos + e.vel * dt end end diff --git a/src/systems/player.lua b/src/systems/player.lua index a69a74d..d9a542e 100644 --- a/src/systems/player.lua +++ b/src/systems/player.lua @@ -115,7 +115,8 @@ function Player:tryShootinBolt(player) vel = player.aim_dir * player.bolt_speed, friction = player.bolt_friction, bolt = true, - sprite = {} + sprite = {}, + collider = {-4, -4, 4, 4} } end end @@ -136,7 +137,8 @@ function Player:spawnPlayer() bolt_count = 1, bolt_speed = 500, bolt_cooldown = 0.2, - bolt_friction = 0.9 + bolt_friction = 0.9, + collider = {-6, -6, 6, 6} } end diff --git a/src/systems/screen-scaler.lua b/src/systems/screen-scaler.lua index 3044fd8..635b01a 100644 --- a/src/systems/screen-scaler.lua +++ b/src/systems/screen-scaler.lua @@ -44,9 +44,8 @@ function ScreenScaler:getMousePosition() end function ScreenScaler:getDimensions() - if self.scale then - local w, h = love.graphics.getDimensions() - return w*self.scale, h*self.scale + if self.current_width and self.current_height then + return self.current_width, self.current_height else return love.graphics.getDimensions() end @@ -57,6 +56,8 @@ function ScreenScaler:overrideScaling(width, height) self.scale = math.min(sw / width, sh / height) self.offset_x = (sw - width * self.scale)/2 self.offset_y = (sh - height * self.scale)/2 + self.current_width = width + self.current_height = height end function ScreenScaler:start(p1, p2) @@ -72,6 +73,8 @@ function ScreenScaler:start(p1, p2) end local sw, sh = love.graphics.getDimensions() + self.current_width = width + self.current_height = height self.scale = math.min(sw / width, sh / height) self.offset_x = (sw - width * self.scale)/2 self.offset_y = (sh - height * self.scale)/2 @@ -90,6 +93,7 @@ function ScreenScaler:finish() love.graphics.pop() if self.canvas then love.graphics.setCanvas() + love.graphics.setColor(1, 1, 1) love.graphics.draw(self.canvas, self.offset_x, self.offset_y, 0, self.scale) else self:hideBorders() diff --git a/src/systems/sprite.lua b/src/systems/sprite.lua index 5945f58..1a46871 100644 --- a/src/systems/sprite.lua +++ b/src/systems/sprite.lua @@ -2,7 +2,7 @@ local Sprite = {} function Sprite:draw() for _, e in ipairs(self.pool.groups.sprite.entities) do - love.graphics.setColor(1, 1, 1) + love.graphics.setColor(1, 1, 1) love.graphics.circle("fill", e.pos.x, e.pos.y, 10) end end