diff --git a/.luarc.json b/.luarc.json index fc3c669..d5e5e33 100644 --- a/.luarc.json +++ b/.luarc.json @@ -4,6 +4,9 @@ "param-type-mismatch", "cast-local-type" ], + "Lua.diagnostics.globals": [ + "pprint" + ], "Lua.runtime.version": "LuaJIT", "Lua.workspace.checkThirdParty": false, "Lua.workspace.library": [ diff --git a/src/data/init.lua b/src/data/init.lua index b1e4ab4..ea08293 100644 --- a/src/data/init.lua +++ b/src/data/init.lua @@ -9,26 +9,31 @@ love.graphics.setDefaultFilter("nearest", "nearest") -- TODO: Maybe add a texture atlas library for packing frame data -- TODO: For production maybe use another type of loader? (https://github.com/elloramir/packer, https://github.com/EngineerSmith/Runtime-TextureAtlas) local function loadAsepriteSprite(filename) - local sprite = {} local ase = aseLoader(filename) - sprite.width = ase.header.width - sprite.height = ase.header.height - sprite.variants = {} - local LAYER_CHUNK = 0x2004 local CEL_CHUNK = 0x2005 local TAG_CHUNK = 0x2018 + local SLICE_CHUNK = 0x2022 + local USER_CHUNK = 0x2020 local frames = {} local tags = {} local layers = {} + local slices = {} love.graphics.push("transform") love.graphics.origin() + local width = ase.header.width + local height = ase.header.height for i, ase_frame in ipairs(ase.header.frames) do - for _, chunk in ipairs(ase_frame.chunks) do + for chunk_i, chunk in ipairs(ase_frame.chunks) do + local user_data = ase_frame.chunks[chunk_i+1] + if user_data and user_data.type ~= USER_CHUNK then + user_data = nil + end + if chunk.type == CEL_CHUNK then local cel = chunk.data local buffer = love.data.decompress("data", "zlib", cel.data) @@ -37,7 +42,7 @@ local function loadAsepriteSprite(filename) local frame = frames[i] if not frame then frame = { - image = love.graphics.newCanvas(sprite.width, sprite.height), + image = love.graphics.newCanvas(width, height), duration = ase_frame.frame_duration / 1000 } frame.image:setFilter("nearest", "nearest") @@ -61,12 +66,29 @@ local function loadAsepriteSprite(filename) end elseif chunk.type == LAYER_CHUNK then table.insert(layers, chunk.data) + elseif chunk.type == SLICE_CHUNK then + local slice_key = chunk.data.keys[1] + table.insert(slices, { + name = chunk.data.name, + x = slice_key.x, + y = slice_key.y, + width = slice_key.width, + height = slice_key.height, + user_data = user_data and user_data.data.text + }) end end end love.graphics.push() + local sprite = { + width = width, + height = height, + slices = slices, + variants = {} + } + for _, tag in pairs(tags) do local variant = {} sprite.variants[tag.name] = variant @@ -76,7 +98,7 @@ local function loadAsepriteSprite(filename) end if not sprite.variants.default then - sprite.variants.default = {frames[1]} + sprite.variants.default = frames end return sprite diff --git a/src/data/maps/main-menu.lua b/src/data/maps/main-menu.lua new file mode 100644 index 0000000..5cb3547 --- /dev/null +++ b/src/data/maps/main-menu.lua @@ -0,0 +1,179 @@ +return { + version = "1.9", + luaversion = "5.1", + tiledversion = "1.9.0", + class = "", + orientation = "orthogonal", + renderorder = "right-down", + width = 16, + height = 9, + tilewidth = 16, + tileheight = 16, + nextlayerid = 5, + nextobjectid = 13, + properties = {}, + tilesets = { + { + name = "roguelike-dungeon", + firstgid = 1, + filename = "../tilesets/roguelike-dungeon.tsx", + exportfilename = "../tilesets/roguelike-dungeon.lua" + } + }, + layers = { + { + type = "tilelayer", + x = 0, + y = 0, + width = 16, + height = 9, + id = 1, + name = "ground", + class = "", + visible = true, + opacity = 1, + offsetx = 0, + offsety = 0, + parallaxx = 1, + parallaxy = 1, + properties = {}, + encoding = "lua", + data = { + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68 + } + }, + { + type = "tilelayer", + x = 0, + y = 0, + width = 16, + height = 9, + id = 3, + name = "decorations", + class = "", + visible = true, + opacity = 1, + offsetx = 0, + offsety = 0, + parallaxx = 1, + parallaxy = 1, + properties = {}, + encoding = "lua", + data = { + 0, 0, 66, 0, 66, 0, 0, 0, 0, 0, 0, 0, 0, 96, 0, 0, + 0, 0, 66, 66, 66, 66, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 66, 66, 66, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 66, 66, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 66, 66, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 96, 0, 66, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 119, 119, 119, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 119, 0, 0, 0, 0, 0, 0, 0, 0 + } + }, + { + type = "objectgroup", + draworder = "topdown", + id = 2, + name = "metadata", + class = "", + visible = true, + opacity = 1, + offsetx = 0, + offsety = 0, + parallaxx = 1, + parallaxy = 1, + properties = {}, + objects = { + { + id = 2, + name = "Spawnpoint", + class = "", + shape = "point", + x = 140.167, + y = 90.1667, + width = 0, + height = 0, + rotation = 0, + visible = true, + properties = {} + }, + { + id = 6, + name = "title", + class = "", + shape = "rectangle", + x = 128, + y = 16, + width = 96, + height = 48, + rotation = 0, + visible = true, + properties = { + ["sprite"] = "title" + } + }, + { + id = 7, + name = "Host", + class = "button", + shape = "rectangle", + x = 16, + y = 16, + width = 96, + height = 32, + rotation = 0, + visible = true, + properties = {} + }, + { + id = 8, + name = "Join", + class = "button", + shape = "rectangle", + x = 16, + y = 56, + width = 96, + height = 32, + rotation = 0, + visible = true, + properties = {} + }, + { + id = 9, + name = "Quit", + class = "button", + shape = "rectangle", + x = 16, + y = 96, + width = 96, + height = 32, + rotation = 0, + visible = true, + properties = {} + }, + { + id = 12, + name = "controls", + class = "", + shape = "point", + x = 225, + y = 111.667, + width = 0, + height = 0, + rotation = 0, + visible = true, + properties = {} + } + } + } + } +} diff --git a/src/data/maps/main-menu.tmx b/src/data/maps/main-menu.tmx new file mode 100644 index 0000000..666792c --- /dev/null +++ b/src/data/maps/main-menu.tmx @@ -0,0 +1,49 @@ + + + + + + + + +68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68, +68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68, +68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68, +68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68, +68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68, +68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68, +68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68, +68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68, +68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68 + + + + +0,0,66,0,66,0,0,0,0,0,0,0,0,96,0,0, +0,0,66,66,66,66,0,0,0,0,0,0,0,0,0,0, +0,0,0,66,66,66,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,66,66,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,66,66,0, +0,0,0,0,0,0,0,0,0,0,0,0,96,0,66,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,119,119,119,0,0,0,0,0,0, +0,0,0,0,0,0,0,119,0,0,0,0,0,0,0,0 + + + + + + + + + + + + + + + + + + + diff --git a/src/data/sprites/title.aseprite b/src/data/sprites/title.aseprite new file mode 100644 index 0000000..2531528 Binary files /dev/null and b/src/data/sprites/title.aseprite differ diff --git a/src/data/ui/prompts/dark/a.png b/src/data/ui/prompts/dark/a.png new file mode 100644 index 0000000..3ae68ca Binary files /dev/null and b/src/data/ui/prompts/dark/a.png differ diff --git a/src/data/ui/prompts/dark/d.png b/src/data/ui/prompts/dark/d.png new file mode 100644 index 0000000..c238b13 Binary files /dev/null and b/src/data/ui/prompts/dark/d.png differ diff --git a/src/data/ui/prompts/dark/s.png b/src/data/ui/prompts/dark/s.png new file mode 100644 index 0000000..500206c Binary files /dev/null and b/src/data/ui/prompts/dark/s.png differ diff --git a/src/data/ui/prompts/dark/spacebar.png b/src/data/ui/prompts/dark/spacebar.png new file mode 100644 index 0000000..40925b2 Binary files /dev/null and b/src/data/ui/prompts/dark/spacebar.png differ diff --git a/src/data/ui/prompts/dark/w.png b/src/data/ui/prompts/dark/w.png new file mode 100644 index 0000000..46ee011 Binary files /dev/null and b/src/data/ui/prompts/dark/w.png differ diff --git a/src/groups.lua b/src/groups.lua index 6069bb5..4d1c163 100644 --- a/src/groups.lua +++ b/src/groups.lua @@ -7,6 +7,7 @@ return { }}, controllable_player = {filter = {"controllable"}}, sprite = {filter = {"pos", "sprite"}}, + image = {filter = {"pos", "image"}}, bolt = {filter={"pos", "vel", "bolt"}}, collider = {filter={"collider"}}, ui_button = {filter={"pos", "size", "text"}} diff --git a/src/lib/ase-loader.lua b/src/lib/ase-loader.lua index cc32868..4eae08d 100644 --- a/src/lib/ase-loader.lua +++ b/src/lib/ase-loader.lua @@ -312,9 +312,10 @@ local function grab_user_data(data) user_data.flags = read_num(data, DWORD) - if user_data.flags == 1 then + if bit.band(user_data.flags, 1) > 0 then user_data.text = read_string(data) - elseif user_data.flags == 2 then + end + if bit.band(user_data.flags, 2) > 0 then user_data.colors = read_num(data, BYTE * 4) end @@ -342,6 +343,8 @@ local function grab_chunk(data) chunk.data = grab_slice(data) elseif chunk.type == 0x2020 then chunk.data = grab_user_data(data) + else + error(("Uknown chunk type: 0x%x"):format(chunk.type)) end return chunk diff --git a/src/lib/sti/init.lua b/src/lib/sti/init.lua index 50aec91..8ab07c1 100644 --- a/src/lib/sti/init.lua +++ b/src/lib/sti/init.lua @@ -22,6 +22,8 @@ local lg = require(cwd .. "graphics") local Map = {} Map.__index = Map +local DRAW_OBJECT_LAYERS = false + local function new(map, plugins, ox, oy) local dir = "" @@ -895,6 +897,8 @@ end --- Default draw function for Object Layers -- @param layer The Object Layer to draw function Map:drawObjectLayer(layer) + if not DRAW_OBJECT_LAYERS then return end + if type(layer) == "string" or type(layer) == "number" then layer = self.layers[layer] end @@ -954,7 +958,7 @@ function Map:drawObjectLayer(layer) elseif object.shape == "polyline" then drawShape(object.polyline, "polyline") elseif object.shape == "point" then - -- lg.points(object.x, object.y) + lg.points(object.x, object.y) end end end diff --git a/src/main.lua b/src/main.lua index 257cc57..1d2e839 100644 --- a/src/main.lua +++ b/src/main.lua @@ -1,6 +1,7 @@ local Gamestate = require("lib.hump.gamestate") local binser = require("lib.binser") local Vec = require("lib.brinevector") +pprint = require("lib.pprint") binser.registerStruct("brinevector", function(v) return v.x, v.y end, diff --git a/src/states/main-menu.lua b/src/states/main-menu.lua index 4017f8f..3a9d2ba 100644 --- a/src/states/main-menu.lua +++ b/src/states/main-menu.lua @@ -16,10 +16,19 @@ local function splitat(str, char) return str:sub(1, index-1), str:sub(index+1) end +local function findByName(list, name) + for _, o in ipairs(list) do + if o.name:lower() == name:lower() then + return o + end + end +end + function MainMenu:init() local systems = { require("systems.physics"), require("systems.bolt"), + require("systems.map"), require("systems.ui"), require("systems.player"), require("systems.sprite"), @@ -31,7 +40,10 @@ function MainMenu:init() self.ecs = nata.new{ available_groups = require("groups"), - systems = systems + systems = systems, + data = { + map = "data/maps/main-menu.lua" + } } self.host_socket = enet.host_create("*:0") @@ -40,18 +52,21 @@ function MainMenu:init() self.hosting = false self.connecting = false + local map = self.ecs:getSystem(require("systems.map")).map + local pprint = require("lib.pprint") + local metadata_layer = findByName(map.layers, "metadata") + do + local spawnpoint = findByName(metadata_layer.objects, "spawnpoint") local PlayerSystem = self.ecs:getSystem(require("systems.player")) - local player = PlayerSystem:spawnPlayer(Vec(192, 48)) + local player = PlayerSystem:spawnPlayer(Vec(spawnpoint.x, spawnpoint.y)) player.sprite.flip = true player.controllable = true end do - local tileSize = 16 - local screenW, screenH = love.graphics.getDimensions() - self.screenWidth = 16 * tileSize - self.screenHeight = 16 * screenH/screenW * tileSize + self.screenWidth = map.width * map.tilewidth + self.screenHeight = map.height * map.tileheight local border = 2.5 local w = self.screenWidth @@ -62,6 +77,38 @@ function MainMenu:init() self.ecs:queue{ collider = {0, h-border, w, h} } end + do + local controls_obj = findByName(metadata_layer.objects, "controls") + local pos = Vec(controls_obj.x, controls_obj.y) + + local prompts = data.ui.prompts.dark + self.ecs:queue{ + pos = pos + Vec(0, -16), + image = prompts["w"], + collider = {-8, -8, 8, 8} + } + self.ecs:queue{ + pos = pos, + image = prompts["a"], + collider = {-8, -8, 8, 8} + } + self.ecs:queue{ + pos = pos + Vec(-16, 0), + image = prompts["s"], + collider = {-8, -8, 8, 8} + } + self.ecs:queue{ + pos = pos + Vec(16, 0), + image = prompts["d"], + collider = {-8, -8, 8, 8} + } + self.ecs:queue{ + pos = pos + Vec(0, 16), + image = prompts["spacebar"], + collider = {-24, -8, 24, 8} + } + end + do local host_btn = self.ecs:queue{ pos = Vec(16, 16), @@ -113,19 +160,6 @@ function MainMenu:draw() ScreenScaler:start(self.screenWidth, self.screenHeight) self.ecs:emit("draw") ScreenScaler:finish() - - -- self.ui:textbox(self.addr_textbox, w/2-250, h/2-30, 280, 60) - -- if self.ui:button("Copy my address", w/2-50, h/2-100, 300, 60) then - -- local _, port = splitat(self.host_socket:get_socket_address(), ":") - -- local address = getPublicIP() .. ":" .. port - -- love.system.setClipboardText(address) - -- end - -- if self.ui:button("Connect", w/2+50, h/2-30, 200, 60) then - -- self.connecting = true - -- self.host_socket:connect(self.addr_textbox.text) - -- end - - -- self.ui:postDraw() end return MainMenu diff --git a/src/states/main.lua b/src/states/main.lua index 487b775..09c6c8d 100644 --- a/src/states/main.lua +++ b/src/states/main.lua @@ -25,14 +25,15 @@ function MainState:enter(_, host_socket) available_groups = require("groups"), systems = systems, data = { - host_socket = host_socket + host_socket = host_socket, + map = "data/maps/playground.lua" } } self.downscaled_canvas = nil - self:refreshDownscaledCanvas() + self:refreshScaler() self.ecs:on("onMapSwitch", function(map) - self:refreshDownscaledCanvas(map) + self:refreshScaler(map) end) @@ -42,14 +43,10 @@ function MainState:enter(_, host_socket) player.controllable = true end -function MainState:refreshDownscaledCanvas(map) +function MainState:refreshScaler(map) if not map then local Map = self.ecs:getSystem(require("systems.map")) map = Map.map - if not map then - self.downscaled_canvas = nil - return - end end self.screenWidth = map.width * map.tilewidth diff --git a/src/systems/map.lua b/src/systems/map.lua index 7206043..28a5fe6 100644 --- a/src/systems/map.lua +++ b/src/systems/map.lua @@ -1,27 +1,54 @@ local Map = {} local sti = require("lib.sti") -local Vector = require("lib.brinevector") +local Vec = require("lib.brinevector") function Map:init() - self.map = sti("data/maps/playground.lua", { "bump" }) - self.pool:emit("onMapSwitch", self.map) + if self.pool.data.map then + self.map = sti(self.pool.data.map, { "bump" }) + + local spriteSystem = self.pool:getSystem(require("systems.sprite")) + if spriteSystem then + for _, layer in ipairs(self.map.layers) do + if layer.type == "objectgroup" then + for _, obj in ipairs(layer.objects) do + local props = obj.properties + if props.sprite then + local x = obj.x + obj.width/2 + local y = obj.y + obj.height/2 + self.pool:queue{ + sprite = { name = props.sprite }, + pos = Vec(x, y) + } + end + end + end + end + end + + + self.pool:emit("onMapSwitch", self.map) + end end function Map:update(dt) - self.map:update(dt) + if self.map then + self.map:update(dt) + end end function Map:listSpawnpoints() local points = {} for _, object in ipairs(self.map.layers.spawnpoints.objects) do - table.insert(points, Vector(object.x, object.y)) + table.insert(points, Vec(object.x, object.y)) end return points end function Map:draw() - love.graphics.setColor(1, 1, 1) - self.map:draw() + if self.map then + love.graphics.setColor(1, 1, 1) + self.map:draw() + end end return Map diff --git a/src/systems/sprite.lua b/src/systems/sprite.lua index f49cfc9..ffa604e 100644 --- a/src/systems/sprite.lua +++ b/src/systems/sprite.lua @@ -1,14 +1,31 @@ local data = require("data") local pprint = require("lib.pprint") +local Vec = require("lib.brinevector") local Sprite = {} -Sprite.required_groups = {"sprite"} +Sprite.required_groups = {"sprite", "image"} function Sprite:addToGroup(group, e) - if group ~= "sprite" then return end + if group == "sprite" then + local sprite = data.sprites[e.sprite.name] + assert(sprite, ("Attempt to draw unknown sprite: %s"):format(e.sprite.name)) - local sprite = data.sprites[e.sprite.name] - assert(sprite, ("Attempt to draw unknown sprite: %s"):format(e.sprite.name)) + for _, slice in ipairs(sprite.slices) do + if slice.user_data and slice.user_data:find("collider") then + self.pool:queue{ + pos = e.pos - Vec(sprite.width, sprite.height)/2, + collider = { + slice.x, + slice.y, + slice.x + slice.width, + slice.y + slice.height + } + } + end + end + elseif group == "image" then + assert(e.image:typeOf("Drawable"), ("Expected sprite to be a drawable or aseprite sprite, got: %s"):format(e.image)) + end end function Sprite:update(dt) @@ -40,9 +57,10 @@ function Sprite:draw() if not e.hidden then local variant = sprite.variants[e.sprite.variant or "default"] - if variant and e.sprite.frame then + local frame_idx = e.sprite.frame or 1 + if variant and frame_idx then local rotation = nil - local frame = variant[e.sprite.frame] + local frame = variant[frame_idx] if not frame then frame = variant[1] end @@ -66,6 +84,20 @@ function Sprite:draw() end end end + + for _, e in ipairs(self.pool.groups.image.entities) do + if not e.hidden then + local width, height = e.image:getDimensions() + love.graphics.draw( + e.image, + e.pos.x, + e.pos.y, + 0, + 1, 1, + width/2, height/2 + ) + end + end end return Sprite