Compare commits
2 commits
9513a5c471
...
e46bc230a6
Author | SHA1 | Date | |
---|---|---|---|
e46bc230a6 | |||
f562b23197 |
17 changed files with 1023 additions and 208 deletions
5
.gitignore
vendored
Normal file
5
.gitignore
vendored
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
dev
|
||||||
|
build/
|
||||||
|
games/*
|
||||||
|
!games/.gitkeep
|
||||||
|
|
37
lib/chroot.lua
Normal file
37
lib/chroot.lua
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
return function(ll)
|
||||||
|
|
||||||
|
ll.mdir = nil
|
||||||
|
ll.mgme = nil
|
||||||
|
|
||||||
|
local ffi = require 'ffi'
|
||||||
|
ffi.cdef [[
|
||||||
|
int PHYSFS_mount(const char *, const char *, int);
|
||||||
|
int PHYSFS_unmount(const char *);
|
||||||
|
]]
|
||||||
|
|
||||||
|
local baseReq = '?.lua;?/init.lua;'
|
||||||
|
function ll.mount(gme)
|
||||||
|
local mdir = gme.base .. gme.dir
|
||||||
|
ll.mgme = gme
|
||||||
|
|
||||||
|
love.filesystem.setRequirePath(''
|
||||||
|
.. mdir .. '/?.lua;'
|
||||||
|
.. mdir .. '/?/init.lua;'
|
||||||
|
.. baseReq
|
||||||
|
)
|
||||||
|
-- FIXME: Bug may appear in Linux. Recompile Love2D or use official PPA.
|
||||||
|
if ffi.C.PHYSFS_mount(mdir, '/', 0) == 0
|
||||||
|
then error('Cannot mount '..mdir)
|
||||||
|
love.filesystem.setRequirePath(baseReq)
|
||||||
|
else ll.mdir = mdir
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function ll.umount()
|
||||||
|
if ll.mdir ~= nil then
|
||||||
|
ffi.C.PHYSFS_unmount(ll.mdir)
|
||||||
|
ll.mdir = nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
20
lib/fs.lua
Normal file
20
lib/fs.lua
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
return function(ll)
|
||||||
|
|
||||||
|
function ll.fsIsAbs(f)
|
||||||
|
f = f:sub(1, 1)
|
||||||
|
return f == '/' or f == '\\'
|
||||||
|
end
|
||||||
|
|
||||||
|
function ll.fsIsRel(f)
|
||||||
|
return not ll.fsIsAbs(f)
|
||||||
|
end
|
||||||
|
|
||||||
|
function ll.fsFile(f)
|
||||||
|
return f:match '([^/\\]*)[/\\]*$'
|
||||||
|
end
|
||||||
|
|
||||||
|
function ll.fsDir(f)
|
||||||
|
return f:match '^(.*)[/\\]+[^/\\]*[/\\]*$'
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
100
lib/game.lua
Normal file
100
lib/game.lua
Normal file
|
@ -0,0 +1,100 @@
|
||||||
|
return function(ll)
|
||||||
|
|
||||||
|
ll.games = {}
|
||||||
|
|
||||||
|
local function parse(conf)
|
||||||
|
local r = {}
|
||||||
|
local k, v
|
||||||
|
|
||||||
|
local buf = ''
|
||||||
|
local t = 'str'
|
||||||
|
local esc = false
|
||||||
|
local prg = true
|
||||||
|
|
||||||
|
local i = 1
|
||||||
|
repeat
|
||||||
|
local ch = conf:sub(i, i)
|
||||||
|
|
||||||
|
if ch == '\\'
|
||||||
|
then esc = true
|
||||||
|
|
||||||
|
elseif ch == '#' then
|
||||||
|
repeat i = i + 1
|
||||||
|
ch = conf:sub(i, i)
|
||||||
|
until ch == '' or ch == '\n' or ch == '\r'
|
||||||
|
|
||||||
|
elseif ch == '[' then
|
||||||
|
buf = buf:match '%s*(.*)%s*'
|
||||||
|
assert(#buf == 0, 'Unexpected array usage')
|
||||||
|
t = 'arr'
|
||||||
|
prg = true
|
||||||
|
v = {}
|
||||||
|
|
||||||
|
elseif ch == ''
|
||||||
|
or (ch == '=' and not k)
|
||||||
|
or (ch == ']' and t == 'arr')
|
||||||
|
or (ch == ';' and t == 'arr')
|
||||||
|
or ch == '\n' or ch == '\r' then
|
||||||
|
buf = buf:match '^%s*(.-)%s*$'
|
||||||
|
|
||||||
|
if ch == '=' then
|
||||||
|
assert(t == 'str', 'Cannot use other types than string for key')
|
||||||
|
end
|
||||||
|
|
||||||
|
if t == 'str'
|
||||||
|
or (t == 'arr' and ch == ']')
|
||||||
|
then prg = false end
|
||||||
|
|
||||||
|
if not prg or ch == ';' then
|
||||||
|
if #buf ~= 0 then
|
||||||
|
if k then
|
||||||
|
if t == 'str'
|
||||||
|
then v = buf
|
||||||
|
elseif t == 'arr'
|
||||||
|
then table.insert(v, buf)
|
||||||
|
else error 'wut?' end
|
||||||
|
else k = buf
|
||||||
|
end
|
||||||
|
buf = ''
|
||||||
|
elseif ch ~= ''
|
||||||
|
and ch ~= '\r'
|
||||||
|
and ch ~= '\n'
|
||||||
|
then error 'empty buffer'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if k and v and not prg then
|
||||||
|
r[k] = v
|
||||||
|
k = nil
|
||||||
|
v = nil
|
||||||
|
buf = ''
|
||||||
|
t = 'str'
|
||||||
|
end
|
||||||
|
|
||||||
|
elseif esc then
|
||||||
|
buf = buf .. ch
|
||||||
|
esc = false
|
||||||
|
|
||||||
|
else buf = buf .. ch
|
||||||
|
end
|
||||||
|
i = i + 1
|
||||||
|
until i >= #conf + 1
|
||||||
|
return r
|
||||||
|
end
|
||||||
|
|
||||||
|
function ll.gameNew(conf, file, base, dir)
|
||||||
|
local cfg = parse(conf or '')
|
||||||
|
local gme = {
|
||||||
|
name = cfg.name or dir or 'No name',
|
||||||
|
desc = cfg.desc or 'No description provided.',
|
||||||
|
base = base,
|
||||||
|
dir = dir,
|
||||||
|
main = cfg.main or 'main.lua',
|
||||||
|
screens = cfg.screens or cfg.pics or nil,
|
||||||
|
scrcur = 1,
|
||||||
|
scrprv = 1,
|
||||||
|
dat = nil,
|
||||||
|
}
|
||||||
|
return gme
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
81
lib/keyb.lua
Normal file
81
lib/keyb.lua
Normal file
|
@ -0,0 +1,81 @@
|
||||||
|
return function(ll)
|
||||||
|
|
||||||
|
local mx, my, mb, mpb
|
||||||
|
local dir, sc1, sc2, sclm
|
||||||
|
|
||||||
|
-- d - direction (h, v, x, y, *)
|
||||||
|
-- c1 - coordinate before card (mouse) (be left or top)
|
||||||
|
-- c2 - coordinate after card (mouse) (be right or bottom)
|
||||||
|
-- clm - other coordinate limit (mouse) (set -1 to disable)
|
||||||
|
function ll.kbInit(d, c1, c2, clim)
|
||||||
|
if d == 'h' or d == 'v' or d == '*'
|
||||||
|
then dir = d
|
||||||
|
elseif d == 'y'
|
||||||
|
then dir = 'v'
|
||||||
|
elseif d == 'x'
|
||||||
|
then dir = 'h'
|
||||||
|
else error 'Direction must be *, h (x) or v (y)'
|
||||||
|
end
|
||||||
|
|
||||||
|
c1, c2 =
|
||||||
|
tonumber(c1) or 0,
|
||||||
|
tonumber(c2) or 0
|
||||||
|
sc1, sc2, sclm =
|
||||||
|
math.min(c1, c2),
|
||||||
|
math.max(c1, c2),
|
||||||
|
tonumber(clim) or -1
|
||||||
|
end
|
||||||
|
|
||||||
|
-- returns: <, >, o, m, nil
|
||||||
|
-- ^ and v if dir is *
|
||||||
|
function ll.kbGet()
|
||||||
|
assert(dir, 'Call ll.kbInit(dir, coord1, coord2, coordlimit) before')
|
||||||
|
mx, my = love.mouse.getPosition()
|
||||||
|
mpb = mb
|
||||||
|
if love.mouse.isDown(1) then mb = 1
|
||||||
|
else mb = 0
|
||||||
|
end
|
||||||
|
|
||||||
|
if love.keyboard.isScancodeDown('up', 'w')
|
||||||
|
then return dir == '*' and '^' or '<'
|
||||||
|
|
||||||
|
elseif love.keyboard.isScancodeDown('left', 'a')
|
||||||
|
then return '<'
|
||||||
|
|
||||||
|
elseif love.keyboard.isScancodeDown('down', 's')
|
||||||
|
then return dir == '*' and 'v' or '>'
|
||||||
|
|
||||||
|
elseif love.keyboard.isScancodeDown('right', 'd')
|
||||||
|
then return '>'
|
||||||
|
|
||||||
|
elseif love.keyboard.isScancodeDown('return', 'space')
|
||||||
|
then return 'o'
|
||||||
|
|
||||||
|
elseif love.keyboard.isDown 'menu'
|
||||||
|
then return 'm'
|
||||||
|
|
||||||
|
elseif mb == 0 and mpb == 1 then -- unpressed
|
||||||
|
if dir == 'h' then
|
||||||
|
if sclm < 0 or my <= sclm then
|
||||||
|
if mx <= sc1
|
||||||
|
then return '<'
|
||||||
|
elseif mx >= sc2
|
||||||
|
then return '>'
|
||||||
|
else return 'o'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
else
|
||||||
|
if sclm < 0 or mx <= sclm then
|
||||||
|
if my <= sc1
|
||||||
|
then return '<'
|
||||||
|
elseif my >= sc2
|
||||||
|
then return '>'
|
||||||
|
else return 'o'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
51
lib/load.lua
Normal file
51
lib/load.lua
Normal file
|
@ -0,0 +1,51 @@
|
||||||
|
return function(ll)
|
||||||
|
|
||||||
|
function ll.addGame(file, cont)
|
||||||
|
local dir = ll.fsDir(file)
|
||||||
|
file = ll.fsFile(file)
|
||||||
|
local ext = file:match '%.(%w+)$'
|
||||||
|
print(file, ext, dir)
|
||||||
|
return 'NO!', nil
|
||||||
|
end
|
||||||
|
|
||||||
|
function ll.gameAdd(conf, file, base, dir)
|
||||||
|
local gme = ll.gameNew(conf, file, base, dir)
|
||||||
|
gme.dat = {}
|
||||||
|
|
||||||
|
if gme.screens and gme.screens[1] then
|
||||||
|
gme.dat.scr = {}
|
||||||
|
for i = 1, #gme.screens do
|
||||||
|
table.insert(gme.dat.scr, love.graphics.newImage(ll.cfg.root .. gme.dir ..'/'.. gme.screens[i]))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
table.insert(ll.games, gme)
|
||||||
|
return gme
|
||||||
|
end
|
||||||
|
|
||||||
|
local lfs = love.filesystem
|
||||||
|
local info = lfs.getInfo
|
||||||
|
|
||||||
|
for _, dir in pairs(love.filesystem.getDirectoryItems(ll.cfg.root)) do
|
||||||
|
local isDir
|
||||||
|
if info
|
||||||
|
then isDir = info(ll.cfg.root .. dir).type == 'directory'
|
||||||
|
else isDir = lfs.isDirectory(ll.cfg.root .. dir)
|
||||||
|
end
|
||||||
|
|
||||||
|
if isDir then
|
||||||
|
local file = ll.cfg.root .. dir..'/'.. 'info.ll'
|
||||||
|
local realDir = love.filesystem.getRealDirectory(file)
|
||||||
|
or love.filesystem.getRealDirectory(ll.cfg.root .. dir..'/main.lua')
|
||||||
|
if realDir
|
||||||
|
then ll.gameAdd(
|
||||||
|
love.filesystem.read(file),
|
||||||
|
file,
|
||||||
|
realDir ..'/'.. ll.cfg.root,
|
||||||
|
dir
|
||||||
|
)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
27
lib/main.lua
Normal file
27
lib/main.lua
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
local ll = {}
|
||||||
|
|
||||||
|
ll.cfg = {
|
||||||
|
root = 'games/',
|
||||||
|
}
|
||||||
|
|
||||||
|
require 'lib.fs' (ll)
|
||||||
|
require 'lib.game' (ll)
|
||||||
|
require 'lib.chroot' (ll)
|
||||||
|
require 'lib.load' (ll)
|
||||||
|
require 'lib.keyb' (ll)
|
||||||
|
|
||||||
|
function ll.home()
|
||||||
|
ll.umount()
|
||||||
|
love.event.push('quit', 'restart')
|
||||||
|
end
|
||||||
|
|
||||||
|
ll.dt = false
|
||||||
|
function ll.devtools()
|
||||||
|
if not ll.dt then
|
||||||
|
ll.dt = true
|
||||||
|
__LL = ll
|
||||||
|
pcall(function() require 'dev.tools' end)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return ll
|
11
ll-min.lua
11
ll-min.lua
|
@ -1,8 +1,17 @@
|
||||||
-- minimal Love Loader API
|
-- Minimal Love Loader API
|
||||||
|
-- Version 2.1
|
||||||
|
-- (c) Er2 2022 <er2@dismail.de>
|
||||||
|
-- Zlib License
|
||||||
|
|
||||||
if not llUsed then
|
if not llUsed then
|
||||||
|
|
||||||
COLDIV = love.getVersion() == 0 and 1 or 255
|
COLDIV = love.getVersion() == 0 and 1 or 255
|
||||||
|
MOBILE = love.system.getOS() == 'Android'
|
||||||
|
or love.system.getOS() == 'iOS'
|
||||||
|
|
||||||
|
if MOBILE
|
||||||
|
then love.window.setFullscreen(true)
|
||||||
|
end
|
||||||
|
|
||||||
function love.resize(x, y)
|
function love.resize(x, y)
|
||||||
W, H = x, y
|
W, H = x, y
|
||||||
|
|
17
luapack.lua
Normal file
17
luapack.lua
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
return {
|
||||||
|
entry = 'main.lua',
|
||||||
|
output = 'build/main.lua',
|
||||||
|
plug = {
|
||||||
|
require 'plug.minify' {
|
||||||
|
extGlob = {
|
||||||
|
'love',
|
||||||
|
'llUsed',
|
||||||
|
'llHome',
|
||||||
|
'resize',
|
||||||
|
'COLDIV',
|
||||||
|
'MOBILE',
|
||||||
|
'W', 'H',
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
238
main.lua
238
main.lua
|
@ -1,219 +1,71 @@
|
||||||
|
local ll = require 'lib.main'
|
||||||
|
error = love.errhand or love.errorhandler
|
||||||
|
|
||||||
local root = 'games/'
|
function splash()
|
||||||
|
love.graphics.setColor(255, 255, 255, 100 / COLDIV)
|
||||||
-- games library and selection
|
if ll.mgme.screens and ll.mgme.screens[1] then
|
||||||
local gms = {}
|
local img = love.graphics.newImage(ll.mgme.screens[1])
|
||||||
local sel = 1
|
love.graphics.draw(img, 0, 0, 0, W / img:getWidth(), H / img:getHeight())
|
||||||
local sgme
|
|
||||||
|
|
||||||
-- some internals
|
|
||||||
local cx, cy, cw, ch
|
|
||||||
local f, bf
|
|
||||||
|
|
||||||
local ffi = require 'ffi'
|
|
||||||
local fd
|
|
||||||
ffi.cdef [[
|
|
||||||
int PHYSFS_mount(const char *, const char *, int);
|
|
||||||
int PHYSFS_unmount(const char *);
|
|
||||||
]]
|
|
||||||
|
|
||||||
local function llResz()
|
|
||||||
cw, ch = W / 1.25, H / 1.25
|
|
||||||
cx, cy = (W - cw) / 2, (H - ch) / 2
|
|
||||||
|
|
||||||
local th = math.min(W, H) / 8
|
|
||||||
|
|
||||||
f = love.graphics.newFont(th / 3)
|
|
||||||
bf = love.graphics.newFont(th / 2)
|
|
||||||
end
|
end
|
||||||
resize = llResz
|
love.graphics.setColor(255, 255, 255, 255)
|
||||||
|
love.graphics.print('Loading '..ll.mgme.name, W / 2, H / 2)
|
||||||
|
end
|
||||||
|
|
||||||
|
ll.skin = require 'skins.psp' (ll)
|
||||||
|
|
||||||
require 'll-min'
|
require 'll-min'
|
||||||
llUsed = true
|
llUsed = true
|
||||||
|
|
||||||
local function chroot(dir, main)
|
if love.errorhandler
|
||||||
fd = love.filesystem.getSource() .. dir
|
then love.errorhandler = error
|
||||||
love.filesystem.setRequirePath(
|
else love.errhand = error
|
||||||
'?.lua;?/init.lua;'
|
|
||||||
.. dir .. '/?.lua;'
|
|
||||||
.. dir .. '/?/init.lua;'
|
|
||||||
)
|
|
||||||
-- FIXME: Bug in Linux (Debian)
|
|
||||||
ffi.C.PHYSFS_mount(fd, '/', 0);
|
|
||||||
if main then
|
|
||||||
load(love.filesystem.read(dir ..'/'.. main), main)()
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local function escChroot()
|
|
||||||
if fd then
|
|
||||||
ffi.C.PHYSFS_unmount(fd);
|
|
||||||
fd = nil
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local function gmeNew(cont, dir, file)
|
|
||||||
local gme = {
|
|
||||||
name = 'No name',
|
|
||||||
desc = 'No description provided.',
|
|
||||||
dir = dir,
|
|
||||||
main = 'main.lua',
|
|
||||||
pics = nil,
|
|
||||||
psel = 2,
|
|
||||||
ppsl = 1,
|
|
||||||
}
|
|
||||||
local fi = false
|
|
||||||
cont = cont or ''
|
|
||||||
cont = cont:gsub('[^\\]#[^\n]*', '')
|
|
||||||
for v in cont:gmatch '([^\n]+)' do
|
|
||||||
local k, v = v:match '^%s*([%w_]+)%s*=%s*(.*)%s*$'
|
|
||||||
if k == 'name'
|
|
||||||
or k == 'desc'
|
|
||||||
or k == 'main'
|
|
||||||
then gme[k] = v
|
|
||||||
fi = true
|
|
||||||
elseif k == 'pic' then
|
|
||||||
gme.pics = gme.pics or {}
|
|
||||||
table.insert(gme.pics, v)
|
|
||||||
fi = true
|
|
||||||
elseif k == 'pics' then
|
|
||||||
local t = {}
|
|
||||||
v = v:sub(2, -2)
|
|
||||||
for v in v:gmatch '%s*([^;]+%a+)%s*;?'
|
|
||||||
do table.insert(t, v) end
|
|
||||||
gme[k] = t
|
|
||||||
fi = true
|
|
||||||
elseif k
|
|
||||||
then error('unknown field "'.. k ..'" in "'.. file ..'"')
|
|
||||||
end
|
|
||||||
end
|
|
||||||
if gme.pics then
|
|
||||||
for k, v in pairs(gme.pics) do
|
|
||||||
gme.pics[k] = love.graphics.newImage(root .. dir ..'/'.. v)
|
|
||||||
end
|
|
||||||
gme.psel = math.min(2, #gme.pics)
|
|
||||||
end
|
|
||||||
if not fi then
|
|
||||||
gme.name = dir
|
|
||||||
end
|
|
||||||
table.insert(gms, gme)
|
|
||||||
end
|
|
||||||
|
|
||||||
for _, v in pairs(love.filesystem.getDirectoryItems(root)) do
|
|
||||||
local file = v..'/'.. 'info.ll'
|
|
||||||
gmeNew(love.filesystem.read(root .. file), v, file)
|
|
||||||
end
|
|
||||||
|
|
||||||
local pf, mx, mb, mpb
|
|
||||||
|
|
||||||
local pcht = 60 * 5
|
|
||||||
local pchat = math.floor(1 / pcht * 2 * 255 + 0.5)
|
|
||||||
local pchv, pcha = 0, 0
|
|
||||||
local function llUpdate()
|
|
||||||
mx = love.mouse.getX()
|
|
||||||
mpb = mb
|
|
||||||
mb = 0
|
|
||||||
if love.mouse.isDown(1) then mb = 1
|
|
||||||
end
|
|
||||||
|
|
||||||
if mpb == 1 and mb == 0 then
|
|
||||||
if mx <= cx then
|
|
||||||
sel = sel - 1
|
|
||||||
if sel < 1 then sel = #gms end
|
|
||||||
elseif mx >= cx + cw then
|
|
||||||
sel = sel + 1
|
|
||||||
if sel > #gms then sel = 1 end
|
|
||||||
else sgme = gms[sel]
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
pchv = pchv + 1
|
|
||||||
if pchv >= pcht then
|
|
||||||
pchv, pcha = 0, 0
|
|
||||||
for _, v in pairs(gms) do
|
|
||||||
if v.pics then
|
|
||||||
v.ppsl = v.psel
|
|
||||||
v.psel = v.psel + 1
|
|
||||||
if v.psel > #v.pics
|
|
||||||
then v.psel = 1 end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
else pcha = math.min(255, pcha + pchat)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local function llDraw()
|
|
||||||
love.graphics.polygon('fill',
|
|
||||||
8, H / 2,
|
|
||||||
32, H / 2 - 32,
|
|
||||||
32, H / 2 + 32)
|
|
||||||
|
|
||||||
love.graphics.polygon('fill',
|
|
||||||
W - 8, H / 2,
|
|
||||||
W - 32, H / 2 - 32,
|
|
||||||
W - 32, H / 2 + 32)
|
|
||||||
|
|
||||||
local oy, t = 0, ''
|
|
||||||
local gme = gms[sel]
|
|
||||||
if gme then
|
|
||||||
local ph = ch / 1.5
|
|
||||||
if gme.pics then
|
|
||||||
local p, n = gme.pics[gme.ppsl], gme.pics[gme.psel]
|
|
||||||
love.graphics.draw(p, cx, cy, 0, cw / p:getWidth(), ph / p:getHeight())
|
|
||||||
love.graphics.setColor(255, 255, 255, pcha / COLDIV)
|
|
||||||
love.graphics.draw(n, cx, cy, 0, cw / n:getWidth(), ph / n:getHeight())
|
|
||||||
love.graphics.setColor(255, 255, 255, 255)
|
|
||||||
end
|
|
||||||
love.graphics.rectangle('line', cx, cy, cw, ph)
|
|
||||||
love.graphics.rectangle('line', cx, cy, cw, ch)
|
|
||||||
|
|
||||||
oy = cy + ph
|
|
||||||
love.graphics.setFont(bf)
|
|
||||||
love.graphics.print(gme.name, cx, oy)
|
|
||||||
oy = oy + bf:getHeight(gme.name)
|
|
||||||
love.graphics.setFont(f)
|
|
||||||
love.graphics.print(gme.desc, cx, oy)
|
|
||||||
|
|
||||||
else oy = H / 2
|
|
||||||
t = 'No games'
|
|
||||||
love.graphics.setFont(bf)
|
|
||||||
love.graphics.print(t, (W - bf:getWidth(t)) / 2, oy)
|
|
||||||
oy = oy + bf:getHeight(t)
|
|
||||||
love.graphics.setFont(f)
|
|
||||||
t = 'There is no projects/games to run'
|
|
||||||
love.graphics.print(t, (W - f:getWidth(t)) / 2, oy)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function llHome()
|
|
||||||
escChroot()
|
|
||||||
love.event.push('quit', 'restart')
|
|
||||||
end
|
end
|
||||||
|
|
||||||
local brk = false
|
local brk = false
|
||||||
while not brk and not sgme do
|
while not brk and not ll.mdir do
|
||||||
-- event handling
|
-- event handling
|
||||||
love.event.pump()
|
love.event.pump()
|
||||||
for n, a,b,c,d,e,f in love.event.poll() do
|
for n, a,b,c in love.event.poll() do
|
||||||
if n == 'quit' then
|
if n == 'quit'
|
||||||
escChroot()
|
or (n == 'keypressed' and a == 'escape')
|
||||||
|
then ll.umount()
|
||||||
love.event.push('quit')
|
love.event.push('quit')
|
||||||
brk = true; break
|
brk = true; break
|
||||||
end
|
end
|
||||||
love.handlers[n](a,b,c,d,e,f)
|
love.handlers[n](a,b,c)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- update and drawing
|
-- update and drawing
|
||||||
llUpdate()
|
ll.skin.update()
|
||||||
love.graphics.origin()
|
love.graphics.origin()
|
||||||
love.graphics.clear(0, 0, 0)
|
love.graphics.clear(0, 0, 0)
|
||||||
llDraw()
|
ll.skin.draw()
|
||||||
love.graphics.present()
|
love.graphics.present()
|
||||||
love.timer.sleep(0.001)
|
love.timer.sleep(0.001)
|
||||||
end
|
end
|
||||||
|
|
||||||
if sgme then
|
if ll.mdir then
|
||||||
love.graphics.setNewFont()
|
love.graphics.setNewFont()
|
||||||
resize = nil
|
resize = nil
|
||||||
chroot(root .. sgme.dir, sgme.main)
|
|
||||||
|
love.graphics.clear(0, 0, 0)
|
||||||
|
splash()
|
||||||
|
love.graphics.present()
|
||||||
|
|
||||||
|
if ll.skin.lovecb then
|
||||||
|
for _, v in pairs(ll.skin.lovecb)
|
||||||
|
do love[v] = nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
love.filesystem.setIdentity(ll.mgme.dir)
|
||||||
|
local f, err = load(love.filesystem.read(ll.mgme.main), ll.mgme.name)
|
||||||
|
if not f then error(err)
|
||||||
|
else xpcall(f, function(e)
|
||||||
|
error(e)
|
||||||
|
llHome()
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
|
love.resize(love.graphics.getDimensions())
|
||||||
end
|
end
|
||||||
|
|
97
readme.md
97
readme.md
|
@ -2,31 +2,99 @@
|
||||||
|
|
||||||
Custom menu for selecting game from multiple for Love2D.
|
Custom menu for selecting game from multiple for Love2D.
|
||||||
|
|
||||||
|
![PSP Style](./scr/psp.png)
|
||||||
|
|
||||||
|
[Other screenshots](./scr)
|
||||||
|
|
||||||
# How it works?
|
# How it works?
|
||||||
|
|
||||||
If simple: just place games in `games` folder with structure like `games/yourgame/main.lua`
|
Just place games into `games` folder! (like `this_folder/games/game/main.lua`)
|
||||||
|
|
||||||
If technically: creates local variables functions and variables.
|
Technically, this creates the loop until game wasn't selected or user wants to exit
|
||||||
Creates the loop until game wasn't selected with manual event handling and redrawing.
|
with custom event handling and redrawing.
|
||||||
|
|
||||||
# Things left or API
|
# LibLL
|
||||||
|
|
||||||
We are not keep environment super clear (except local variables ;))
|
Love Loader from 2.0 includes backend API to simplify creating custom skins.
|
||||||
so there are some variables can be used in game:
|
|
||||||
|
|
||||||
`W` and `H` variables: width and height of the screen, controlled by love.resize function.
|
It have not so many functions and fields:
|
||||||
|
|
||||||
`love.resize` and optional `resize` payload: functions called when screen size when changed and at boot.
|
- `ll.games` - field for games, which have this structure:
|
||||||
|
```lua
|
||||||
|
{
|
||||||
|
name = 'string', -- Friendly name for game or placeholder
|
||||||
|
desc = 'string', -- Description for game or placeholder
|
||||||
|
base = 'string', -- base directory used in game mounting, must end with `/`
|
||||||
|
dir = 'string', -- directory name, used if no name was defined
|
||||||
|
main = 'string', -- main file to execute or `main.lua`
|
||||||
|
screens = {'array of', 'path to screenshots'},
|
||||||
|
scrcur = 1, --[[number]] -- current index from screenshots
|
||||||
|
scrprv = 1, --[[number]] -- previous index from screenshots
|
||||||
|
dat = nil, --[[any]] -- maybe platform-dependent data to reduce operations
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
`love.event.quit`: function to quit to menu screen
|
- `ll.mdir` - string or nil, contains full mounted directory.
|
||||||
|
|
||||||
`COLDIV`: color divider (1 or 255) to setColor function
|
- `ll.mgme` - game or nil, contains mounted game.
|
||||||
|
|
||||||
`llUsed`: is Love Loader used
|
- `ll.gameNew(configuration --[[string]], fileName --[[string, not used]], base --[[string]], directory --[[string]])`
|
||||||
|
|
||||||
`llHome`: function to quit to menu screen
|
Creates game object (defined above) and returns it.
|
||||||
|
|
||||||
They also can be used without Love Loader if load `ll-min.lua`
|
- `ll.gameAdd(conf, file, base, dir)` - same as `ll.gameNew` with insertion into `ll.games`.
|
||||||
|
|
||||||
|
- `ll.addGame(fileName, fileContent)` - function for file dropping, reserved for v3.0.
|
||||||
|
|
||||||
|
- `ll.mount(game)` - mounts game.
|
||||||
|
|
||||||
|
Can throw an error.
|
||||||
|
|
||||||
|
Sets `ll.mdir` and `ll.mgme`.
|
||||||
|
|
||||||
|
- `ll.umount()` - unmounts game if can.
|
||||||
|
|
||||||
|
Unsets `ll.mdir` and `ll.mgme`.
|
||||||
|
|
||||||
|
- `ll.home()` - calls `llHome`
|
||||||
|
|
||||||
|
- `ll.dt` - is developer tools enabled?
|
||||||
|
|
||||||
|
- `__LL` - global variable of Love Loader instance when developer tools enabled.
|
||||||
|
|
||||||
|
- `ll.devtools()` - enable developer tools.
|
||||||
|
|
||||||
|
- `ll.fsIsAbs(file)` - is file absolute (/file)?
|
||||||
|
|
||||||
|
- `ll.fsIsRel(file)` - is file relative, inverted result of ll.fsIsAbs (./file).
|
||||||
|
|
||||||
|
- `ll.fsDir(path)` - get directory name (2 from /1/2/3.file).
|
||||||
|
|
||||||
|
- `ll.fsFile(path)` - get file (including dividers after) (2 from /1/2/).
|
||||||
|
|
||||||
|
- `ll.kbInit(direction --[[string: *, h, v, x, y]], c1 --[[number, coordinate before card for mouse (left/top)]], c2 --[[number, coordinate after card for mouse (right/bottom)]], clim --[[other coordinate limit for mouse or -1 to disable]])` - initialize keyboard module for skins.
|
||||||
|
|
||||||
|
- `ll.kbGet() --[[nil, string: <, >, o, m, ^ anv v if direction is *]]` - get key pressed.
|
||||||
|
|
||||||
|
# API
|
||||||
|
|
||||||
|
To reduce things to do for game developers, this loader creates some global variables to use.
|
||||||
|
|
||||||
|
You can also use it without Love Loader (or if your game can distribute without loader) by including `ll-min.lua` file.
|
||||||
|
|
||||||
|
`W` and `H`: width and height of the screen which controls by custom love.resize function.
|
||||||
|
|
||||||
|
`love.resize` and optional `resize` payload: functions called when screen size was changed and on booting.
|
||||||
|
|
||||||
|
`love.event.quit`: function to exit to main screen.
|
||||||
|
|
||||||
|
`llHome`: function called by `love.event.quit` (broken for now).
|
||||||
|
|
||||||
|
`llUsed`: is Love Loader (not minimal API) used.
|
||||||
|
|
||||||
|
`COLDIV`: color divider (1 or 255) to `love.graphics.setColor` function.
|
||||||
|
|
||||||
|
`MOBILE`: is this device runs Android or iOS?
|
||||||
|
|
||||||
# Fill game information
|
# Fill game information
|
||||||
|
|
||||||
|
@ -40,5 +108,6 @@ name = New awesome game using Love Loader
|
||||||
desc = Some descripion about the game.
|
desc = Some descripion about the game.
|
||||||
# main = optional main file instead of `main.lua`
|
# main = optional main file instead of `main.lua`
|
||||||
pic = screen.png
|
pic = screen.png
|
||||||
pics = [ screen.png; screen2.png ] # wow array
|
pics = [ screen.png; screen2.png ] # wow arrays
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
BIN
scr/base.png
Normal file
BIN
scr/base.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 11 KiB |
BIN
scr/gui.png
Normal file
BIN
scr/gui.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 62 KiB |
BIN
scr/psp.png
Normal file
BIN
scr/psp.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 25 KiB |
190
skins/base.lua
Normal file
190
skins/base.lua
Normal file
|
@ -0,0 +1,190 @@
|
||||||
|
return function(ll)
|
||||||
|
|
||||||
|
-- Basic cmd-like style UI
|
||||||
|
-- FIXME: Console scrolling
|
||||||
|
|
||||||
|
local cw, ch, ctw, cth
|
||||||
|
local ct = 'Love Loader v2.0\n(c) Er2 2022\tZlib License\n\n'
|
||||||
|
local cce = false
|
||||||
|
local buf = ''
|
||||||
|
|
||||||
|
local mb, mpb, mt = 0, 0, 0
|
||||||
|
|
||||||
|
local function ccp()
|
||||||
|
local argv = {}
|
||||||
|
for v in buf:gmatch '%S+'
|
||||||
|
do table.insert(argv, v)
|
||||||
|
end
|
||||||
|
local cmd = (argv[1] or ''):lower()
|
||||||
|
if cmd == '' then return '' -- nothing
|
||||||
|
elseif cmd == 'help' then return 'Available commands:'
|
||||||
|
.. '\nhelp\t\tThis command'
|
||||||
|
.. '\nls\t\tList of games'
|
||||||
|
.. '\ndir\t\tSame as ls'
|
||||||
|
.. '\ncat\t<path>\tInformation about game'
|
||||||
|
.. '\ninfo\t<path>\tSame as cat'
|
||||||
|
.. '\nrun\t<path>\tStart game'
|
||||||
|
.. '\nmount\t<path>\tSame as run'
|
||||||
|
.. '\nchroot\t<path>\tSame as run'
|
||||||
|
.. '\nclear\t\tClear screen'
|
||||||
|
.. '\neval\t<code>\tExecute code'
|
||||||
|
elseif cmd == 'clear' then
|
||||||
|
ct = ''
|
||||||
|
return ''
|
||||||
|
elseif cmd == 'eval' then
|
||||||
|
local code = buf:match '^%S+%s+(.*)'
|
||||||
|
local f, err = load(code, 'test.lua')
|
||||||
|
if not f then return err
|
||||||
|
else return tostring(f() or 'nil')
|
||||||
|
end
|
||||||
|
elseif cmd == 'ls'
|
||||||
|
or cmd == 'dir' then
|
||||||
|
local r = ''
|
||||||
|
if #ll.games == 0 then
|
||||||
|
return ('%s: No games found. Add some to the library.'):format(argv[1])
|
||||||
|
end
|
||||||
|
for i = 1, #ll.games do
|
||||||
|
local v = ll.games[i]
|
||||||
|
r = r .. ('%s\t(%s)\t - %s\n'):format(v.dir, v.name, v.desc)
|
||||||
|
end
|
||||||
|
return r .. ('\nTotal: %3d games\n'):format(#ll.games)
|
||||||
|
elseif cmd == 'cat'
|
||||||
|
or cmd == 'info' then
|
||||||
|
if not argv[2]
|
||||||
|
then return ('%s: Game directory missing'):format(argv[1])
|
||||||
|
end
|
||||||
|
local r = ''
|
||||||
|
for _, v in pairs(ll.games) do
|
||||||
|
if argv[2] == v.dir
|
||||||
|
or argv[2] == v.base .. v.dir
|
||||||
|
then r = r..
|
||||||
|
( '\nFriendly name:\t%s'
|
||||||
|
..'\nDescription:\t%s'
|
||||||
|
..'\nLocation:\t%s%s'
|
||||||
|
..'\nMain file:\t%s'
|
||||||
|
..'\n\n\t\tScreenshots are not supported yet.'
|
||||||
|
..'\n'):format(v.name, v.desc, v.base, v.dir, v.main)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if r == '' then r = 'Not found' end
|
||||||
|
return r
|
||||||
|
elseif cmd == 'run'
|
||||||
|
or cmd == 'mount'
|
||||||
|
or cmd == 'chroot' then
|
||||||
|
local gme
|
||||||
|
for _, v in pairs(ll.games) do
|
||||||
|
if argv[2] == v.dir
|
||||||
|
or argv[2] == v.base .. v.dir
|
||||||
|
then if gme then
|
||||||
|
return 'Error: multiple paths found, use full location'
|
||||||
|
end
|
||||||
|
gme = v end
|
||||||
|
end
|
||||||
|
if gme
|
||||||
|
then ll.mount(gme)
|
||||||
|
love.keyboard.setTextInput(false)
|
||||||
|
return ''
|
||||||
|
else return 'Not found'
|
||||||
|
end
|
||||||
|
else return 'Unknown command "'.. cmd .. '"'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local function update()
|
||||||
|
mpb = mb
|
||||||
|
if love.mouse.isDown(1) then mb = 1
|
||||||
|
if mpb ~= 1
|
||||||
|
then mt = os.time()
|
||||||
|
end
|
||||||
|
else mb = 0
|
||||||
|
end
|
||||||
|
|
||||||
|
if mb == 0 and mpb == 1
|
||||||
|
and os.time() - mt <= 1 then
|
||||||
|
if love.system.getOS() == 'Android' then
|
||||||
|
love.keyboard.setTextInput(
|
||||||
|
not love.keyboard.hasTextInput(),
|
||||||
|
0, H, W, ch)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if not cce then
|
||||||
|
cce = true
|
||||||
|
local r = ccp()
|
||||||
|
if #r ~= 0 then ct = ct.. '\n'.. r end
|
||||||
|
ct = ct.. '\n> '
|
||||||
|
buf = ''
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local utf8 = require 'utf8'
|
||||||
|
local function draw()
|
||||||
|
love.graphics.setColor(255, 255, 255)
|
||||||
|
|
||||||
|
local x, y = 0, 0
|
||||||
|
for p, cde in utf8.codes(ct) do
|
||||||
|
local chr = utf8.char(cde)
|
||||||
|
if chr == '\n'
|
||||||
|
then x, y = 0, y + 1
|
||||||
|
elseif chr == '\r'
|
||||||
|
then x = 0
|
||||||
|
elseif chr == '\t'
|
||||||
|
then x = x + 8 - (x % 8)
|
||||||
|
elseif chr == '\v'
|
||||||
|
then y = y + 1
|
||||||
|
else love.graphics.print(chr, x * cw, y * ch)
|
||||||
|
if x >= ctw
|
||||||
|
then x, y = x - ctw, y + 1
|
||||||
|
else x = x + 1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if os.time() % 2 == 0
|
||||||
|
then love.graphics.rectangle('fill', x * cw, y * ch, cw, ch)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function resize()
|
||||||
|
local f = love.graphics.newFont(math.min(W, H) / 32)
|
||||||
|
love.graphics.setFont(f)
|
||||||
|
cw, ch = f:getWidth 'm', f:getHeight 'A'
|
||||||
|
ctw, cth =
|
||||||
|
math.floor(W / cw) - 1,
|
||||||
|
math.floor(H / ch) - 1
|
||||||
|
|
||||||
|
love.keyboard.setTextInput(true, 0, H, W, ch)
|
||||||
|
end
|
||||||
|
|
||||||
|
function love.textinput(txt)
|
||||||
|
ct = ct.. txt
|
||||||
|
buf = buf.. txt
|
||||||
|
end
|
||||||
|
|
||||||
|
function love.keypressed(k)
|
||||||
|
if k == 'backspace' then
|
||||||
|
local off = utf8.offset(buf, -1)
|
||||||
|
if off then
|
||||||
|
buf = buf:sub(1, off - 1)
|
||||||
|
off = utf8.offset(ct, -1)
|
||||||
|
if off
|
||||||
|
then ct = ct:sub(1, off - 1)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
elseif k == 'return'
|
||||||
|
then cce = false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
love.keyboard.setKeyRepeat(true)
|
||||||
|
love.window.setMode(800, 600, {resizable = true})
|
||||||
|
|
||||||
|
return {
|
||||||
|
update = update,
|
||||||
|
draw = draw,
|
||||||
|
lovecb = {
|
||||||
|
'textinput',
|
||||||
|
'keypressed',
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
end
|
198
skins/gui.lua
Normal file
198
skins/gui.lua
Normal file
|
@ -0,0 +1,198 @@
|
||||||
|
return function(ll)
|
||||||
|
|
||||||
|
ll.cfg.pcht = ll.cfg.pcht or 60 * 5
|
||||||
|
|
||||||
|
local pikchv, pikcha = 0, 0
|
||||||
|
local pikchao = math.floor(1 / ll.cfg.pcht * 2 * 255 + 0.5)
|
||||||
|
|
||||||
|
local cx, cy, cw, ch
|
||||||
|
local f, bf
|
||||||
|
|
||||||
|
local sel = 1
|
||||||
|
local cdir
|
||||||
|
|
||||||
|
function resize()
|
||||||
|
cw = W / 1.25
|
||||||
|
ch = H / 1.25
|
||||||
|
cx = (W - cw) / 2
|
||||||
|
cy = (H - ch) / 2
|
||||||
|
|
||||||
|
local th = math.min(W, H) / 8
|
||||||
|
|
||||||
|
f = love.graphics.newFont(th / 3)
|
||||||
|
bf = love.graphics.newFont(th / 2)
|
||||||
|
|
||||||
|
ll.kbInit('h', cx, cx + cw)
|
||||||
|
end
|
||||||
|
|
||||||
|
local function update()
|
||||||
|
local sdi = ll.kbGet()
|
||||||
|
|
||||||
|
if cdir ~= sdi then
|
||||||
|
cdir = sdi
|
||||||
|
if sdi == '<'
|
||||||
|
then sel = sel - 1
|
||||||
|
elseif sdi == '>'
|
||||||
|
then sel = sel + 1
|
||||||
|
elseif sdi == 'o' and ll.games[sel]
|
||||||
|
then ll.mount(ll.games[sel])
|
||||||
|
|
||||||
|
elseif sdi == 'm'
|
||||||
|
and love.mouse.getX() >= W - 8
|
||||||
|
then ll.devtools()
|
||||||
|
end
|
||||||
|
|
||||||
|
if sel < 1 then sel = #ll.games end
|
||||||
|
if sel > #ll.games then sel = 1 end
|
||||||
|
end
|
||||||
|
|
||||||
|
pikchv = pikchv + 1
|
||||||
|
if pikchv >= ll.cfg.pcht then
|
||||||
|
pikchv = 0
|
||||||
|
pikcha = 0
|
||||||
|
for _, v in pairs(ll.games) do
|
||||||
|
if v.dat.scr then
|
||||||
|
v.scrprv = v.scrcur
|
||||||
|
v.scrcur = v.scrcur + 1
|
||||||
|
if v.scrcur > #v.dat.scr
|
||||||
|
then v.scrcur = 1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
else pikcha = math.min(255, pikcha + pikchao)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local tm = 0
|
||||||
|
local function draw()
|
||||||
|
love.graphics.setColor(0 / COLDIV, 50 / COLDIV, 75 / COLDIV)
|
||||||
|
love.graphics.rectangle('fill', 0, 0, W, H)
|
||||||
|
love.graphics.setColor(255, 255, 255, 100 / COLDIV)
|
||||||
|
love.graphics.setLineWidth(8)
|
||||||
|
love.graphics.setFont(bf)
|
||||||
|
local t = 'Love Loader'
|
||||||
|
local tw, th = bf:getWidth(t), bf:getHeight(t)
|
||||||
|
love.graphics.print(t, W - tw - 8, H - th)
|
||||||
|
|
||||||
|
tm = (tm + 0.02) % 6.28
|
||||||
|
local c, pc, ps = tm, math.cos(tm), math.sin(tm)
|
||||||
|
local oy = H / 2
|
||||||
|
for x = 0, W + 8, 8 do
|
||||||
|
c = c + 0.1
|
||||||
|
local c, s = math.cos(c), math.sin(c)
|
||||||
|
local cy, ncy, sy, nsy =
|
||||||
|
pc * H / 12,
|
||||||
|
c * H / 12,
|
||||||
|
ps * H / 12,
|
||||||
|
s * H / 12
|
||||||
|
|
||||||
|
love.graphics.line(x - 8, sy + oy, x-1, nsy + oy)
|
||||||
|
love.graphics.line(x - 8, sy/2 + oy, x-1, nsy/2 + oy)
|
||||||
|
love.graphics.line(x - 8, cy/1.5 + oy, x-1, ncy/1.5 + oy)
|
||||||
|
love.graphics.line(x - 8, cy*1.5 + oy, x-1, ncy*1.5 + oy)
|
||||||
|
pc, ps = c, s
|
||||||
|
end
|
||||||
|
love.graphics.setColor(255, 255, 255, 255)
|
||||||
|
love.graphics.setLineWidth(1)
|
||||||
|
|
||||||
|
local oy, t = 0, ''
|
||||||
|
local gme = ll.games[sel]
|
||||||
|
if gme then
|
||||||
|
love.graphics.polygon('fill',
|
||||||
|
8, H / 2,
|
||||||
|
32, H / 2 - 32,
|
||||||
|
32, H / 2 + 32)
|
||||||
|
|
||||||
|
love.graphics.polygon('fill',
|
||||||
|
W - 8, H / 2,
|
||||||
|
W - 32, H / 2 - 32,
|
||||||
|
W - 32, H / 2 + 32)
|
||||||
|
|
||||||
|
love.graphics.stencil(function()
|
||||||
|
love.graphics.rectangle('fill', cx, cy, cw, ch, 16)
|
||||||
|
end)
|
||||||
|
love.graphics.setStencilTest('greater', 0)
|
||||||
|
love.graphics.setColor(50 / COLDIV, 50 / COLDIV, 50 / COLDIV, 100 / COLDIV)
|
||||||
|
love.graphics.rectangle('fill', cx, cy, cw, ch)
|
||||||
|
love.graphics.setColor(255, 255, 255, 255)
|
||||||
|
|
||||||
|
if gme.dat.scr then
|
||||||
|
local p, n = gme.dat.scr[gme.scrprv], gme.dat.scr[gme.scrcur]
|
||||||
|
love.graphics.draw(p, cx, cy, 0, cw / p:getWidth(), ch / p:getHeight())
|
||||||
|
love.graphics.setColor(255, 255, 255, pikcha / COLDIV)
|
||||||
|
love.graphics.draw(n, cx, cy, 0, cw / n:getWidth(), ch / n:getHeight())
|
||||||
|
love.graphics.setColor(0, 0, 0, 150 / COLDIV)
|
||||||
|
love.graphics.rectangle('fill', cx, cy, cw, ch)
|
||||||
|
love.graphics.setColor(255, 255, 255, 255)
|
||||||
|
end
|
||||||
|
|
||||||
|
oy = cy + ch / 1.25
|
||||||
|
love.graphics.setFont(bf)
|
||||||
|
love.graphics.print(gme.name, cx + 16, oy)
|
||||||
|
oy = oy + bf:getHeight(gme.name)
|
||||||
|
love.graphics.setFont(f)
|
||||||
|
love.graphics.print(gme.desc, cx + 16, oy)
|
||||||
|
love.graphics.setStencilTest()
|
||||||
|
love.graphics.rectangle('line', cx, cy, cw, ch, 16)
|
||||||
|
|
||||||
|
elseif ll.games[1] then
|
||||||
|
sel = 1
|
||||||
|
love.graphics.setColor(255, 255, 255, 255)
|
||||||
|
love.graphics.print('Sorry :)', W / 2, H / 2)
|
||||||
|
else love.graphics.setColor(255, 255, 255, 255)
|
||||||
|
oy = H / 2.5
|
||||||
|
t = 'No games'
|
||||||
|
love.graphics.setFont(bf)
|
||||||
|
love.graphics.print(t, (W - bf:getWidth(t)) / 2, oy)
|
||||||
|
oy = oy + bf:getHeight(t)
|
||||||
|
love.graphics.setFont(f)
|
||||||
|
t = 'There are no projects/games to run'
|
||||||
|
love.graphics.print(t, (W - f:getWidth(t)) / 2, oy)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function error(msg)
|
||||||
|
msg = tostring(msg)
|
||||||
|
|
||||||
|
love.graphics.reset()
|
||||||
|
love.graphics.origin()
|
||||||
|
local bf = love.graphics.newFont(64)
|
||||||
|
local f = love.graphics.setNewFont(16)
|
||||||
|
local perc = 0
|
||||||
|
|
||||||
|
local q
|
||||||
|
repeat
|
||||||
|
perc = math.min(100, perc + 0.1 * math.random(50))
|
||||||
|
|
||||||
|
love.graphics.clear(17/COLDIV, 113/COLDIV, 172/COLDIV)
|
||||||
|
love.graphics.setColor(255, 255, 255)
|
||||||
|
love.graphics.setFont(bf)
|
||||||
|
love.graphics.print(':(', 64, 16)
|
||||||
|
love.graphics.setFont(f)
|
||||||
|
love.graphics.print(('%d%% complete'):format(perc), 64, 100)
|
||||||
|
love.graphics.printf(msg, 64, 132, W - 64)
|
||||||
|
love.graphics.print('(c) Love Loader and Er2', 8, H - 16 - 8)
|
||||||
|
love.graphics.present()
|
||||||
|
|
||||||
|
love.event.pump()
|
||||||
|
for n, a,b,c in love.event.poll() do
|
||||||
|
if n == 'quit'
|
||||||
|
or n == 'mousereleased'
|
||||||
|
or (n == 'keypressed' and a == 'escape')
|
||||||
|
then q = true end
|
||||||
|
end
|
||||||
|
|
||||||
|
love.timer.sleep(0.5)
|
||||||
|
until q or perc == 100
|
||||||
|
|
||||||
|
-- Unfortunately, we can't restart Love in error handler
|
||||||
|
llHome() -- but can outside of love.errorhandler
|
||||||
|
return function() return 1 end
|
||||||
|
end
|
||||||
|
|
||||||
|
return {
|
||||||
|
update = update,
|
||||||
|
draw = draw,
|
||||||
|
}
|
||||||
|
|
||||||
|
end
|
159
skins/psp.lua
Normal file
159
skins/psp.lua
Normal file
|
@ -0,0 +1,159 @@
|
||||||
|
return function(ll)
|
||||||
|
|
||||||
|
-- New UI from Sony PSP
|
||||||
|
|
||||||
|
local sel = 1
|
||||||
|
local psel
|
||||||
|
|
||||||
|
local cx, cy, cw, ch, cg
|
||||||
|
local css, rcss = 2, 0
|
||||||
|
|
||||||
|
local f, bf
|
||||||
|
|
||||||
|
ll.cfg.pcht = ll.cfg.pcht or 60 * 5
|
||||||
|
|
||||||
|
local pikchv, pikcha = 0, 0
|
||||||
|
local pikchao = math.floor(1 / ll.cfg.pcht * 2 * 255 + 0.5)
|
||||||
|
|
||||||
|
local logger = ''
|
||||||
|
local chc = '<>><m><'
|
||||||
|
|
||||||
|
function resize()
|
||||||
|
r = math.min(W, H) / 30
|
||||||
|
|
||||||
|
cg = H / 64
|
||||||
|
cw = W / 5
|
||||||
|
ch = (H - cg) / 6
|
||||||
|
cx = W / 10
|
||||||
|
cy = -H / 2
|
||||||
|
|
||||||
|
f = love.graphics.setNewFont(math.min(W, H) / 30)
|
||||||
|
bf = love.graphics.newFont(math.min(W, H) / 20)
|
||||||
|
|
||||||
|
ll.kbInit('v', H / 2 - ch, H / 2 + ch + cg, cx + cw * 2)
|
||||||
|
end
|
||||||
|
|
||||||
|
love.window.setMode(800, 600, {resizable = true})
|
||||||
|
|
||||||
|
local sdir
|
||||||
|
local function update()
|
||||||
|
local ty = H / 2 - (ch + cg) * sel
|
||||||
|
cy = cy + (ty - cy) * 0.1
|
||||||
|
css = css + (2 - css) * 0.2
|
||||||
|
rcss = 2 - css + 1
|
||||||
|
|
||||||
|
local sdi = ll.kbGet()
|
||||||
|
|
||||||
|
if sdi ~= sdir then
|
||||||
|
if sdi
|
||||||
|
then rcss = css
|
||||||
|
css = 1
|
||||||
|
psel = sel
|
||||||
|
logger = logger .. sdi
|
||||||
|
if logger == chc then
|
||||||
|
resize()
|
||||||
|
ll.devtools()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
sdir = sdi
|
||||||
|
if sdi == '<'
|
||||||
|
then sel = sel - 1
|
||||||
|
elseif sdi == '>'
|
||||||
|
then sel = sel + 1
|
||||||
|
elseif sdi == 'o' and ll.games[sel]
|
||||||
|
then ll.mount(ll.games[sel])
|
||||||
|
end
|
||||||
|
|
||||||
|
if sel < 1 then sel = #ll.games end
|
||||||
|
if sel > #ll.games then sel = 1 end
|
||||||
|
end
|
||||||
|
|
||||||
|
pikchv = pikchv + 1
|
||||||
|
if pikchv >= ll.cfg.pcht then
|
||||||
|
pikchv = 0
|
||||||
|
pikcha = 0
|
||||||
|
for _, v in pairs(ll.games) do
|
||||||
|
if v.dat.scr then
|
||||||
|
v.scrprv = v.scrcur
|
||||||
|
v.scrcur = v.scrcur + 1
|
||||||
|
if v.scrcur > #v.dat.scr
|
||||||
|
then v.scrcur = 1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
else pikcha = math.min(255, pikcha + pikchao)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local function scrCard(gme, x, y, w, h, a)
|
||||||
|
if not gme.dat.scr then return end
|
||||||
|
love.graphics.setColor(255, 255, 255, a / COLDIV)
|
||||||
|
local p, n = gme.dat.scr[gme.scrprv], gme.dat.scr[gme.scrcur]
|
||||||
|
love.graphics.draw(p, x, y, 0, w / p:getWidth(), h / p:getHeight())
|
||||||
|
love.graphics.setColor(255, 255, 255, math.min(a, pikcha) / COLDIV)
|
||||||
|
love.graphics.draw(n, x, y, 0, w / n:getWidth(), h / n:getHeight())
|
||||||
|
love.graphics.setColor(0, 0, 0, 150 / COLDIV)
|
||||||
|
love.graphics.rectangle('fill', x, y, w, h)
|
||||||
|
end
|
||||||
|
|
||||||
|
local function draw()
|
||||||
|
love.graphics.setColor(0 / COLDIV, 50 / COLDIV, 75 / COLDIV)
|
||||||
|
love.graphics.rectangle('fill', 0, 0, W, H)
|
||||||
|
if ll.games[sel] then
|
||||||
|
local cur = ll.games[sel]
|
||||||
|
scrCard(cur, 0, 0, W, H, (css - 1) * 255)
|
||||||
|
love.graphics.setColor(255, 255, 255, 255)
|
||||||
|
love.graphics.setFont(bf)
|
||||||
|
love.graphics.printf(cur.desc, cx + cw * 2, cx, W - (cx + cw * 2))
|
||||||
|
else love.graphics.setColor(255, 255, 255, 255)
|
||||||
|
sel = #ll.games
|
||||||
|
end
|
||||||
|
love.graphics.setFont(f)
|
||||||
|
local oy = 0
|
||||||
|
for k, v in pairs(ll.games) do
|
||||||
|
local x, w, h, g = cx, cw, ch, cg
|
||||||
|
if k == sel then
|
||||||
|
x = cx / css
|
||||||
|
w = cw * css
|
||||||
|
h = ch * css
|
||||||
|
g = cg * 3
|
||||||
|
oy = oy + cg * css
|
||||||
|
elseif k == psel then
|
||||||
|
x = cx / rcss
|
||||||
|
w = cw * rcss
|
||||||
|
h = ch * rcss
|
||||||
|
oy = oy + cg * rcss
|
||||||
|
end
|
||||||
|
|
||||||
|
love.graphics.stencil(function()
|
||||||
|
love.graphics.rectangle('fill', x, cy + oy, w, h, r)
|
||||||
|
end)
|
||||||
|
love.graphics.setStencilTest('greater', 0)
|
||||||
|
|
||||||
|
love.graphics.setColor(50 / COLDIV, 50 / COLDIV, 50 / COLDIV, 100 / COLDIV)
|
||||||
|
love.graphics.rectangle('fill', x, cy + oy, w, h)
|
||||||
|
love.graphics.setColor(255, 255, 255, 255)
|
||||||
|
|
||||||
|
scrCard(v, x, cy + oy, w, h, 255)
|
||||||
|
love.graphics.setColor(255, 255, 255, 255)
|
||||||
|
|
||||||
|
local th = f:getHeight(v.name)
|
||||||
|
love.graphics.print(v.name, x + cg, cy + oy + h - th - cg)
|
||||||
|
|
||||||
|
love.graphics.setStencilTest()
|
||||||
|
love.graphics.rectangle('line', x, cy + oy, w, h, r)
|
||||||
|
oy = oy + h + g
|
||||||
|
end
|
||||||
|
if #ll.games == 0 then
|
||||||
|
love.graphics.print('No games', cx, cy - ch)
|
||||||
|
love.graphics.setFont(bf)
|
||||||
|
love.graphics.print('Add some to the library', cx, cy)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return {
|
||||||
|
update = update,
|
||||||
|
draw = draw,
|
||||||
|
}
|
||||||
|
|
||||||
|
end
|
Loading…
Reference in a new issue