1
0

improve button in main menu

This commit is contained in:
Rokas Puzonas 2022-08-13 20:37:25 +00:00
parent 7f26575942
commit 6e754155dc
19 changed files with 775 additions and 72 deletions

BIN
src/data/audio/switch-5.ogg Normal file

Binary file not shown.

BIN
src/data/audio/switch-7.ogg Normal file

Binary file not shown.

Binary file not shown.

View File

@ -1,6 +1,10 @@
local cargo = require("lib.cargo")
local aseLoader = require("lib.ase-loader")
local pprint = require("lib.pprint")
local slicy = require("lib.slicy")
local ripple = require("lib.ripple")
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)
@ -84,5 +88,11 @@ return cargo.init{
loaders = {
aseprite = loadAsepriteSprite,
ase = loadAsepriteSprite,
["9.png"] = function(filename)
return slicy.load(filename)
end,
ogg = function(filename)
return ripple.newSound(love.audio.newSource(filename, 'static'))
end
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.0 KiB

13
src/groups.lua Normal file
View File

@ -0,0 +1,13 @@
return {
physical = {filter = {"pos", "vel"}},
player = {filter = {
"pos", "acc", "speed", "bolts"
-- "bolt_count",
-- "bolt_cooldown", "bolt_speed", "bolt_friction"
}},
controllable_player = {filter = {"controllable"}},
sprite = {filter = {"pos", "sprite"}},
bolt = {filter={"pos", "vel", "bolt"}},
collider = {filter={"collider"}},
ui_button = {filter={"pos", "size", "text"}}
}

View File

@ -1,3 +1,3 @@
return function(r, g, b)
return {r/255, g/255, b/255}
return {r/255, g/255, b/255, 1}
end

View File

@ -259,6 +259,25 @@ function Pool:_init(options, ...)
self.data = options.data or {}
local groups = options.groups or {}
local systems = options.systems or {nata.oop()}
if options.available_groups then
for _, system in ipairs(systems) do
if type(system.required_groups) == "table" then
for _, group_name in ipairs(system.required_groups) do
local group = options.available_groups[group_name]
if group and not self.groups[group_name] then
self.groups[group_name] = {
filter = group.filter,
sort = group.sort,
entities = {},
hasEntity = {},
}
end
end
end
end
end
for groupName, groupOptions in pairs(groups) do
self.groups[groupName] = {
filter = groupOptions.filter,

497
src/lib/slicy.lua Normal file
View File

@ -0,0 +1,497 @@
local M = {}
--[[
file format is as follows:
* extension: *.9.png, same as patchy and (afaik) behaves the same
* original author never documented it anywhere
* actual image has a 1 pixel border on all sides
* pixels in the border can either be black with opacity > 0 ("set") or not
* set pixels serve as metadata for how to slice the image into 9 patches
* first and last set pixels on the top and left define the interval for the "edge" portions of the image
* image "edge" will be scaled in 1 dimension to accomodate variable size
* first and last set pixels on the bottom and right define the "content window"
* content window defines inner padding so that content doesn't touch the borders
* can be different from the "edge" definitions
* getContentRegion() returns the content region bounds for a given size
]]
---@class PatchedImage
---@field patches table
---@field contentPadding table
---@field x number
---@field y number
---@field width number
---@field height number
local PatchedImage = {}
local PatchedImageMt = {__index = PatchedImage}
local debugDraw = false
local debugLog = false
local DEBUG_DRAW_SEP_WIDTH = 1
local function dbg(...)
if debugLog then
print(...)
end
end
local function firstBlackPixel(imgData, axis, axisoffset, reversed)
local lim, getpixel
if axis == "row" then
lim = imgData:getWidth() - 1
getpixel = function(idx)
return imgData:getPixel(idx, axisoffset)
end
elseif axis == "col" then
lim = imgData:getHeight() - 1
getpixel = function(idx)
return imgData:getPixel(axisoffset, idx)
end
else
return nil, "argument 2: expected either 'row' or 'col', got " .. tostring(axis)
end
dbg("looking for black pixel in axis", axis, axisoffset)
local startidx, endidx, step
if reversed then
-- start at last valid position, down to 0
startidx, endidx, step = lim, 0, -1
else
-- go upwards instead
startidx, endidx, step = 0, lim, 1
end
for idx = startidx, endidx, step do
local r, g, b, a = getpixel(idx)
-- black non-transparent pixel
if r + g + b == 0 and a > 0 then
dbg("black pixel found at idx", idx)
return idx
end
end
return nil, "no black pixel found"
end
local function setCorners(p, rawdata, horizontalEdgeSegment, verticalEdgeSegment)
dbg("slicing corners")
-- -1 from file format border, -1 from excluding the pixel itself
local brCornerWidth = rawdata:getWidth() - horizontalEdgeSegment[2] - 2
local brCornerHeight = rawdata:getHeight() - verticalEdgeSegment[2] - 2
local tlCorner = love.image.newImageData(horizontalEdgeSegment[1] - 1, verticalEdgeSegment[1] - 1)
tlCorner:paste(
rawdata,
0, 0,
-- skip metadata column and row
1, 1,
tlCorner:getDimensions()
)
p.patches[1][1] = love.graphics.newImage(tlCorner, {})
tlCorner:release()
dbg("top left corner:", p.patches[1][1]:getDimensions())
local trCorner = love.image.newImageData(brCornerWidth, verticalEdgeSegment[1] - 1)
trCorner:paste(
rawdata,
0, 0,
horizontalEdgeSegment[2] + 1, 1,
trCorner:getDimensions()
)
p.patches[1][3] = love.graphics.newImage(trCorner, {})
trCorner:release()
dbg("top right corner:", p.patches[1][3]:getDimensions())
local blCorner = love.image.newImageData(horizontalEdgeSegment[1] - 1, brCornerHeight)
blCorner:paste(
rawdata,
0, 0,
1, verticalEdgeSegment[2] + 1,
blCorner:getDimensions()
)
p.patches[3][1] = love.graphics.newImage(blCorner, {})
blCorner:release()
dbg("bottom left corner:", p.patches[3][1]:getDimensions())
local brCorner = love.image.newImageData(brCornerWidth, brCornerHeight)
brCorner:paste(
rawdata,
0, 0,
horizontalEdgeSegment[2] + 1, verticalEdgeSegment[2] + 1,
brCorner:getDimensions()
)
p.patches[3][3] = love.graphics.newImage(brCorner, {})
brCorner:release()
dbg("bottom right corner:", p.patches[3][3]:getDimensions())
end
local function setMiddle(p, rawdata, horizontalEdgeSegment, verticalEdgeSegment)
dbg("slicing middle")
local w = horizontalEdgeSegment[2] - horizontalEdgeSegment[1] + 1
local h = verticalEdgeSegment[2] - verticalEdgeSegment[1] + 1
local middle = love.image.newImageData(w, h)
middle:paste(
rawdata,
0, 0,
horizontalEdgeSegment[1], verticalEdgeSegment[1],
w, h
)
p.patches[2][2] = love.graphics.newImage(middle, {})
middle:release()
dbg("middle:", p.patches[2][2]:getDimensions())
end
local function setEdges(p, rawdata, horizontalEdgeSegment, verticalEdgeSegment)
dbg("slicing edges")
local hlen = horizontalEdgeSegment[2] - horizontalEdgeSegment[1] + 1
local vlen = verticalEdgeSegment[2] - verticalEdgeSegment[1] + 1
local top = love.image.newImageData(hlen, verticalEdgeSegment[1] - 1)
top:paste(
rawdata,
0, 0,
-- 1 to skip over metadata row
horizontalEdgeSegment[1], 1,
top:getDimensions()
)
p.patches[1][2] = love.graphics.newImage(top, {})
top:release()
dbg("top:", p.patches[1][2]:getDimensions())
-- -2 because of 2 distinct -1s, see comments in setCorners
local bottom = love.image.newImageData(hlen, rawdata:getHeight() - verticalEdgeSegment[2] - 2)
bottom:paste(
rawdata,
0, 0,
horizontalEdgeSegment[1], verticalEdgeSegment[2] + 1,
bottom:getDimensions()
)
p.patches[3][2] = love.graphics.newImage(bottom, {})
bottom:release()
dbg("bottom:", p.patches[3][2]:getDimensions())
local left = love.image.newImageData(horizontalEdgeSegment[1] - 1, vlen)
left:paste(
rawdata,
0, 0,
1, verticalEdgeSegment[1],
left:getDimensions()
)
p.patches[2][1] = love.graphics.newImage(left, {})
left:release()
dbg("left:", p.patches[2][1]:getDimensions())
local right = love.image.newImageData(rawdata:getWidth() - horizontalEdgeSegment[2] - 2, vlen)
right:paste(
rawdata,
0, 0,
horizontalEdgeSegment[2] + 1, verticalEdgeSegment[1],
right:getDimensions()
)
p.patches[2][3] = love.graphics.newImage(right, {})
dbg("right:", p.patches[2][3]:getDimensions())
end
---Load a 9-slice image
---@param arg string|love.ImageData filename or raw image data to use for 9-slicing
---@return nil
---@return string
function M.load(arg)
local rawdata
local release = false
local p = {}
if type(arg) == "string" then
dbg("loading sliced image from:", arg)
rawdata = love.image.newImageData(arg, {})
release = true
elseif arg.type and arg:type() == "ImageData" then
rawdata = arg
else
return nil, "expected string or ImageData, got "..tostring(arg)
end
local horizontalEdgeSegment = {
assert(firstBlackPixel(rawdata, "row", 0, false)),
assert(firstBlackPixel(rawdata, "row", 0, true))
}
local verticalEdgeSegment = {
assert(firstBlackPixel(rawdata, "col", 0, false)),
assert(firstBlackPixel(rawdata, "col", 0, true))
}
local horizontalContentPadding = {
assert(firstBlackPixel(rawdata, "row", rawdata:getHeight() - 1, false)) - 1,
rawdata:getWidth() - assert(firstBlackPixel(rawdata, "row", rawdata:getHeight() - 1, true)) - 1
}
local verticalContentPadding = {
assert(firstBlackPixel(rawdata, "col", rawdata:getWidth() - 1, false)) - 1,
rawdata:getHeight() - assert(firstBlackPixel(rawdata, "col", rawdata:getWidth() - 1, true)) - 1
}
-- TODO check for valid value ranges in content padding
p.contentPadding = {
left = horizontalContentPadding[1],
right = horizontalContentPadding[2],
up = verticalContentPadding[1],
down = verticalContentPadding[2]
}
dbg("padding (u,d,l,r):", p.contentPadding.up, p.contentPadding.down, p.contentPadding.left, p.contentPadding.right)
p.patches = {{}, {}, {}}
setCorners(p, rawdata, horizontalEdgeSegment, verticalEdgeSegment)
setMiddle(p, rawdata, horizontalEdgeSegment, verticalEdgeSegment)
setEdges(p, rawdata, horizontalEdgeSegment, verticalEdgeSegment)
p.x, p.y = 0, 0
p.width = p.patches[1][1]:getWidth() + p.patches[1][3]:getWidth()
p.height = p.patches[1][1]:getHeight() + p.patches[3][1]:getHeight()
if release then
rawdata:release()
end
setmetatable(p, PatchedImageMt)
return p
end
-- THIS REUSES THE IMAGE OBJECTS FROM THE ORIGINAL
function PatchedImage:clone()
local c = {}
for i = 1, 3 do
c.patches[i] = {}
for j = 1, 3 do
c.patches[i][j] = self.patches[i][j]
end
end
c.contentPadding = {
left = self.contentPadding.left,
right = self.contentPadding.right,
up = self.contentPadding.up,
down = self.contentPadding.down
}
c.x, c.y = self.x, self.y
c.width, c.height = self.width, self.height
setmetatable(c, PatchedImageMt)
return c
end
---Resize image to given dimensions
---@param w number new width
---@param h number new height
function PatchedImage:resize(w, h)
self.width = self:clampWidth(assert(w))
self.height = self:clampHeight(assert(h))
end
---Move image to given position
---@param x number
---@param y number
function PatchedImage:move(x, y)
self.x = assert(x)
self.y = assert(y)
end
function PatchedImage:getX()
return self.x
end
function PatchedImage:getY()
return self.y
end
function PatchedImage:getPosition()
return self.x, self.y
end
function PatchedImage:getWidth()
return self.width
end
function PatchedImage:getHeight()
return self.height
end
function PatchedImage:getDimensions()
return self.width, self.height
end
---Draws a patch with an optional background debug rect
---@param p love.Image
---@param x number
---@param y number
---@param sx number?
---@param sy number?
local function drawPatch(p, x, y, sx, sy)
sx = sx or 1
sy = sy or 1
if debugDraw then
love.graphics.rectangle("fill", x, y, p:getWidth() * sx, p:getHeight() * sy)
end
love.graphics.draw(p, x, y, 0, sx, sy)
end
function PatchedImage:draw(x, y, w, h)
if x then
self.x = x
else
x = self.x
end
if y then
self.y = y
else
y = self.y
end
if w then
self.width = w
else
w = self.width
end
if h then
self.height = h
else
h = self.height
end
local debugSpacing = debugDraw and DEBUG_DRAW_SEP_WIDTH or 0
local horizontalEdgeLen = w - self.patches[1][1]:getWidth() - self.patches[1][3]:getWidth()
local verticalEdgeLen = h - self.patches[1][1]:getHeight() - self.patches[3][1]:getHeight()
horizontalEdgeLen = math.max(horizontalEdgeLen, 0)
verticalEdgeLen = math.max(verticalEdgeLen, 0)
local horizontalEdgeScale = horizontalEdgeLen / self.patches[1][2]:getWidth()
local verticalEdgeScale = verticalEdgeLen / self.patches[2][1]:getHeight()
-- middle
drawPatch(
self.patches[2][2],
x + debugSpacing + self.patches[1][1]:getWidth(),
y + debugSpacing + self.patches[1][1]:getHeight(),
horizontalEdgeScale, verticalEdgeScale
)
-- edges
-- top
drawPatch(
self.patches[1][2],
x + debugSpacing + self.patches[1][1]:getWidth(),
y,
horizontalEdgeScale, 1
)
-- left
drawPatch(
self.patches[2][1],
x,
y + debugSpacing + self.patches[1][1]:getHeight(),
1, verticalEdgeScale
)
-- right
drawPatch(
self.patches[2][3],
x + 2*debugSpacing + self.patches[1][1]:getWidth() + horizontalEdgeLen,
y + debugSpacing + self.patches[1][1]:getHeight(),
1, verticalEdgeScale
)
-- bottom
drawPatch(
self.patches[3][2],
x + debugSpacing + self.patches[1][1]:getWidth(),
y + 2*debugSpacing + self.patches[1][1]:getHeight() + verticalEdgeLen,
horizontalEdgeScale, 1
)
-- corners
-- top left
drawPatch(self.patches[1][1], x, y)
-- top right
drawPatch(
self.patches[1][3],
x + 2*debugSpacing + self.patches[1][1]:getWidth() + horizontalEdgeLen,
y
)
-- bottom left
drawPatch(
self.patches[3][1],
x,
y + 2*debugSpacing + self.patches[1][1]:getHeight() + verticalEdgeLen
)
-- bottom right
drawPatch(
self.patches[3][3],
x + 2*debugSpacing + self.patches[1][1]:getWidth() + horizontalEdgeLen,
y + 2*debugSpacing + self.patches[1][1]:getHeight() + verticalEdgeLen
)
if debugDraw then
local rw = 2*debugSpacing
- self.contentPadding.left - self.contentPadding.right
+ self.patches[1][1]:getWidth() + horizontalEdgeLen + self.patches[1][3]:getWidth()
local rh = 2*debugSpacing
- self.contentPadding.up - self.contentPadding.down
+ self.patches[1][1]:getHeight() + verticalEdgeLen + self.patches[3][1]:getHeight()
local r, g, b, a = love.graphics.getColor()
local lw = love.graphics.getLineWidth()
love.graphics.setColor(1, 0, 0)
love.graphics.setLineWidth(1)
love.graphics.rectangle(
"line",
x + self.contentPadding.left + 0.5, y + self.contentPadding.up + 0.5,
rw, rh
)
love.graphics.setColor(r, g, b, a)
love.graphics.setLineWidth(lw)
end
end
function PatchedImage:getContentWindow(x, y, w, h)
x = x or self.x
y = y or self.y
w = w or self.width
h = h or self.height
x = x + self.contentPadding.left
y = y + self.contentPadding.up
w = self:clampWidth(w) - self.contentPadding.left - self.contentPadding.right
h = self:clampHeight(h) - self.contentPadding.up - self.contentPadding.down
if debugDraw then
w = w + 2*DEBUG_DRAW_SEP_WIDTH
h = h + 2*DEBUG_DRAW_SEP_WIDTH
end
return x, y, w, h
end
function PatchedImage:clampWidth(w)
return math.max(w, self.patches[1][1]:getWidth() + self.patches[1][3]:getWidth())
end
function PatchedImage:clampHeight(h)
return math.max(h, self.patches[1][1]:getHeight() + self.patches[3][1]:getHeight())
end
function M.setDebug(t)
debugDraw = t.draw
debugLog = t.log
end
function M.isDebugLogging()
return debugLog
end
function M.isDebugDrawing()
return debugDraw
end
return M

View File

@ -12,6 +12,6 @@ function love.load()
love.keyboard.setKeyRepeat(true)
math.randomseed(love.timer.getTime())
Gamestate.switch(require("states.main"))
Gamestate.switch(require("states.main-menu"))
Gamestate.registerEvents()
end

View File

@ -1,11 +1,10 @@
local MainMenu = {}
local UI = require("ui")
local rgb = require("helpers.rgb")
local darken = require("helpers.darken")
local lighten = require("helpers.lighten")
local Gamestate = require("lib.hump.gamestate")
local http = require("socket.http")
local enet = require("enet")
local nata = require("lib.nata")
local Vec = require("lib.brinevector")
local data = require("data")
local function getPublicIP()
local ip = http.request("https://ipinfo.io/ip")
@ -18,53 +17,112 @@ local function splitat(str, char)
end
function MainMenu:init()
self.ui = UI.new{
font = love.graphics.newFont(32),
text_color = rgb(255, 255, 255),
bg_color = rgb(60, 60, 60),
bg_hover_color = {lighten(rgb(60, 60, 60))},
bg_pressed_color = {darken(rgb(60, 60, 60))},
local systems = {
require("systems.physics"),
require("systems.bolt"),
require("systems.ui"),
require("systems.player"),
require("systems.sprite"),
require("systems.screen-scaler"),
}
if not love.filesystem.isFused() then
table.insert(systems, require("systems.debug"))
end
self.addr_textbox = { text = "" }
self.ecs = nata.new{
available_groups = require("groups"),
systems = systems
}
self.host_socket = enet.host_create("*:0")
self.peer_socket = nil
self.hosting = false
self.connecting = false
do
local PlayerSystem = self.ecs:getSystem(require("systems.player"))
local player = PlayerSystem:spawnPlayer(Vec(192, 48))
player.sprite.flip = true
player.controllable = true
end
do
local tileSize = 16
local screenW, screenH = love.graphics.getDimensions()
local w = 16 * tileSize
local h = 16 * screenH/screenW * tileSize
self.canvas = love.graphics.newCanvas(w, h)
local border = 2.5
self.ecs:queue{ collider = {0, 0, w, border} }
self.ecs:queue{ collider = {0, 0, border, h} }
self.ecs:queue{ collider = {w-border, 0, w, h} }
self.ecs:queue{ collider = {0, h-border, w, h} }
end
do
local host_btn = self.ecs:queue{
pos = Vec(16, 16),
size = Vec(96, 32),
text = "Host"
}
local join_btn = self.ecs:queue{
pos = Vec(16, 48+8),
size = Vec(96, 32),
text = "Join"
}
local quit_btn = self.ecs:queue{
pos = Vec(16, 96),
size = Vec(96, 32),
text = "Quit"
}
self.ecs:on("on_btn_pressed", function(btn)
if btn == quit_btn then
love.event.quit()
end
end)
end
end
function MainMenu:update()
function MainMenu:update(dt)
self.ecs:flush()
self.ecs:emit("update", dt)
local event = self.host_socket:service()
if event and event.type == "connect" then
Gamestate.switch(require("states.main"), self.host_socket)
end
end
function MainMenu:keypressed(...)
self.ui:keypressed(...)
end
function MainMenu:mousemoved(x, y, dx, dy, istouch)
local ScreenScaler = self.ecs:getSystem(require("systems.screen-scaler"))
if not ScreenScaler:isInBounds(x, y) then return end
function MainMenu:textinput(...)
self.ui:textinput(...)
x, y = ScreenScaler:getPosition(x, y)
dx = (ScreenScaler.scale or 1) * dx
dy = (ScreenScaler.scale or 1) * dy
self.ecs:emit("mousemoved", x, y, dx, dy, istouch)
end
function MainMenu:draw()
local w, h = love.graphics.getDimensions()
local ScreenScaler = self.ecs:getSystem(require("systems.screen-scaler"))
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
ScreenScaler:start(self.canvas)
self.ecs:emit("draw")
ScreenScaler:finish()
self.ui:postDraw()
-- 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

View File

@ -4,19 +4,6 @@ local nata = require("lib.nata")
local data = require("data")
function MainState:enter(_, host_socket)
local groups = {
physical = {filter = {"pos", "vel"}},
player = {filter = {
"pos", "acc", "speed", "bolts"
-- "bolt_count",
-- "bolt_cooldown", "bolt_speed", "bolt_friction"
}},
controllable_player = {filter = {"controllable"}},
sprite = {filter = {"pos", "sprite"}},
bolt = {filter={"pos", "vel", "bolt"}},
collider = {filter={"pos", "collider"}}
}
local systems = {
require("systems.physics"),
require("systems.map"),
@ -35,7 +22,7 @@ function MainState:enter(_, host_socket)
end
self.ecs = nata.new{
groups = groups,
available_groups = require("groups"),
systems = systems,
data = {
host_socket = host_socket
@ -47,7 +34,12 @@ function MainState:enter(_, host_socket)
self.ecs:on("onMapSwitch", function(map)
self:refreshDownscaledCanvas(map)
end)
love.graphics.setNewFont(48)
-- Spawn in the entity that the local player will be using
local PlayerSystem = self.ecs:getSystem(require("systems.player"))
local player = PlayerSystem:spawnPlayer()
player.controllable = true
end
function MainState:refreshDownscaledCanvas(map)
@ -104,20 +96,9 @@ end
function MainState:draw()
local ScreenScaler = self.ecs:getSystem(require("systems.screen-scaler"))
-- Draw the game
ScreenScaler:start(self.downscaled_canvas)
self.ecs:emit("draw")
ScreenScaler:finish()
-- 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()
-- Override scaling factors from UI, with scalers for the game
ScreenScaler:overrideScaling(self.downscaled_canvas:getDimensions())
end
return MainState

View File

@ -5,6 +5,8 @@ local Vec = require("lib.brinevector")
local BOLT_COLLIDER = {-3, -3, 3, 3}
Bolt.required_groups = {"bolt"}
function Bolt:update(dt)
for _, bolt in ipairs(self.pool.groups.bolt.entities) do
if bolt.vel.length < 20 and not bolt.pickupable then

View File

@ -5,6 +5,8 @@ local Vec = require("lib.brinevector")
-- TODO: Tweak bump world `cellSize` at runtime, when the map switches
Physics.required_groups = {"physical", "collider"}
function Physics:init()
self.bump = bump.newWorld()
self.boundCollisionFilter = function(...)
@ -17,10 +19,14 @@ function Physics:onMapSwitch(map)
end
local function getColliderBounds(e)
local x = e.pos.x + e.collider[1]
local y = e.pos.y + e.collider[2]
local x = e.collider[1]
local y = e.collider[2]
local w = math.abs(e.collider[3] - e.collider[1])
local h = math.abs(e.collider[4] - e.collider[2])
if e.pos then
x = x + e.pos.x
y = y + e.pos.y
end
return x, y, w, h
end
@ -163,4 +169,8 @@ function Physics:project(from, direction, distance)
return key_points
end
function Physics:queryRect(pos, size, filter)
return self.bump:queryRect(pos.x, pos.y, size.x, size.y, filter)
end
return Physics

View File

@ -16,11 +16,7 @@ local function getDirection(up_key, down_key, left_key, right_key)
return Vec(dx, dy).normalized
end
function Player:init()
-- Spawn in the entity that the local player will be using
local player = self:spawnPlayer()
player.controllable = true
end
Player.required_groups = {"player", "controllable_player", "bolt"}
function Player:getMoveDirection()
return getDirection(
@ -201,14 +197,16 @@ function Player:resetMap()
end
end
function Player:spawnPlayer()
local map = self.pool:getSystem(require("systems.map"))
local spawnpoints = map:listSpawnpoints()
local players = self.pool.groups.player.entities
local spawnpoint = spawnpoints[#players % #spawnpoints + 1]
function Player:spawnPlayer(pos)
if not pos then
local map = self.pool:getSystem(require("systems.map"))
local spawnpoints = map:listSpawnpoints()
local players = self.pool.groups.player.entities
pos = spawnpoints[#players % #spawnpoints + 1]
end
local player = self.pool:queue{
pos = spawnpoint,
pos = pos,
vel = Vec(0, 0),
acc = Vec(),

View File

@ -78,6 +78,7 @@ function ScreenScaler:start(p1, p2)
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.active = true
love.graphics.push()
if self.canvas then
@ -99,6 +100,7 @@ function ScreenScaler:finish()
self:hideBorders()
end
self.canvas = nil
self.active = false
end
return ScreenScaler

View File

@ -2,10 +2,12 @@ local data = require("data")
local pprint = require("lib.pprint")
local Sprite = {}
function Sprite:addToWorld(group, e)
Sprite.required_groups = {"sprite"}
function Sprite:addToGroup(group, e)
if group ~= "sprite" then return end
local sprite = data.sprites[e.sprite.variant]
local sprite = data.sprites[e.sprite.name]
assert(sprite, ("Attempt to draw unknown sprite: %s"):format(e.sprite.name))
end

111
src/systems/ui.lua Normal file
View File

@ -0,0 +1,111 @@
local UI = {}
local data = require("data")
local rgb = require("helpers.rgb")
local font = data.fonts["kenney-future"](16)
local on_press_sound = data.audio["switch-5"]
local on_release_sound = data.audio["switch-7"]
UI.required_groups = {"ui_button", "player"}
local BUTTON_TIME = 1.2
function UI:addToGroup(group, e)
if group == "ui_button" then
e.pressed_timer = 0
end
end
function UI:update(dt)
local physics = self.pool:getSystem(require("systems.physics"))
for _, btn in ipairs(self.pool.groups.ui_button.entities) do
local collisions = physics:queryRect(btn.pos, btn.size)
local pressed = #collisions > 0
if btn.pressed then
btn.pressed_timer = math.min(btn.pressed_timer + dt, BUTTON_TIME)
elseif btn.pressed_timer and btn.pressed_timer > 0 then
btn.pressed_timer = math.max(0, btn.pressed_timer - dt * 3)
end
if btn.pressed_timer == BUTTON_TIME and not btn.event_emitted then
btn.event_emitted = true
self.pool:emit("on_btn_pressed", btn)
elseif btn.pressed_timer < BUTTON_TIME and btn.event_emitted then
btn.event_emitted = nil
self.pool:emit("on_btn_released", btn)
end
if not btn.pressed and pressed then
on_press_sound:play()
elseif btn.pressed and not pressed then
on_release_sound:play()
end
btn.pressed = pressed
end
end
local mask_shader = love.graphics.newShader[[
extern number offset_x;
extern number scale;
extern number threshold;
extern vec4 mask_color;
vec4 effect(vec4 color, Image tex, vec2 texture_coords, vec2 screen_coords)
{
vec4 texturecolor = Texel(tex, texture_coords);
if (screen_coords.x > threshold * scale + offset_x) {
return texturecolor * color;
} else {
return texturecolor * mask_color;
}
}
]]
function UI:draw()
love.graphics.setFont(font)
local ScreenScaler = self.pool:getSystem(require("systems.screen-scaler"))
mask_shader:send("mask_color", rgb(10, 250, 20))
if ScreenScaler.active and not ScreenScaler.canvas then
mask_shader:send("scale", ScreenScaler.scale)
mask_shader:send("offset_x", ScreenScaler.offset_x)
else
mask_shader:send("scale", 1)
mask_shader:send("offset_x", 0)
end
for _, btn in ipairs(self.pool.groups.ui_button.entities) do
local panel = data.ui.panels["blue"]
local oy = 0
local r = 0
if btn.pressed then
panel = data.ui.panels["blue-pressed"]
oy = 2
r = math.sin(love.timer.getTime()*20)*0.15
end
love.graphics.setColor(1, 1, 1)
panel:draw(btn.pos.x, btn.pos.y, btn.size.x, btn.size.y)
local cx, cy, cw, ch = panel:getContentWindow()
local text_width = font:getWidth(btn.text)
local text_height = font:getHeight(btn.text)
local threshold = math.min(1, (btn.pressed_timer or 0) / BUTTON_TIME)
mask_shader:send("threshold", cx+threshold*text_width + (cw-text_width)/2)
love.graphics.setShader(mask_shader)
love.graphics.printf(
btn.text,
cx+cw/2,
cy+ch/2+oy,
cw,
"left",
r,
1, 1,
text_width/2,
text_height/2
)
love.graphics.setShader()
end
end
return UI