Love Loader v2.1

Bugfixes.

Removed cross-engine support, only Love2D.

Added filesystem and keyboard modules in LibLL.

Remade input handling in skins.

Files and broken folders are not shows in game list.

Add MOBILE variable and auto-fullscreen on mobile phones.
This commit is contained in:
Er2 2022-07-07 19:37:27 +03:00
parent f562b23197
commit e46bc230a6
13 changed files with 242 additions and 153 deletions

2
.gitignore vendored
View file

@ -1,3 +1,5 @@
dev
build/
games/*
!games/.gitkeep

View file

@ -3,23 +3,34 @@ 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)
--[[
ll.mdir = gme.base .. gme.dir
ll.mgme = gme
--[=[
if gme.main then
print('Load', ll.mdir ..'/'.. gme.main)
end
--]=]
--]]
error 'unimplemented'
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
error 'unimplemented'
end
end

20
lib/fs.lua Normal file
View 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

81
lib/keyb.lua Normal file
View 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

View file

@ -1,9 +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

View file

@ -1,33 +0,0 @@
return function(ll)
local ffi = require 'ffi'
ffi.cdef [[
int PHYSFS_mount(const char *, const char *, int);
int PHYSFS_unmount(const char *);
]]
function ll.mount(gme)
local mdir = gme.base .. gme.dir
love.filesystem.setRequirePath(''
.. mdir .. '/?.lua;'
.. mdir .. '/?/init.lua;'
.. '?.lua;?/init.lua;'
)
-- FIXME: Bug may appear in Linux. Recompile Love2D or use official PPA.
if ffi.C.PHYSFS_mount(mdir, '/', 0) == 0
then error 'Cannot mount'
else
ll.mdir = mdir
ll.mgme = gme
end
end
function ll.umount()
if ll.mdir ~= nil then
ffi.C.PHYSFS_unmount(ll.mdir)
ll.mdir = nil
end
end
end

View file

@ -1,28 +0,0 @@
return function(ll)
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
for _, dir in pairs(love.filesystem.getDirectoryItems(ll.cfg.root)) do
local file = ll.cfg.root .. dir..'/'.. 'info.ll'
ll.gameAdd(
love.filesystem.read(file),
file,
love.filesystem.getSource():match '(.*)[\\/]*' ..'/'.. ll.cfg.root, -- TODO: AppData folders support
dir
)
end
end

View file

@ -4,24 +4,24 @@ 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()
error 'go to home'
love.event.push('quit', 'restart')
end
if love then
require 'lib.love.chroot' (ll)
require 'lib.love.load' (ll)
function ll.home()
ll.umount()
love.event.push('quit', 'restart')
ll.dt = false
function ll.devtools()
if not ll.dt then
ll.dt = true
__LL = ll
pcall(function() require 'dev.tools' end)
end
llHome = ll.home
end
return ll

View file

@ -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
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)
W, H = x, y

View file

@ -9,6 +9,7 @@ return {
'llHome',
'resize',
'COLDIV',
'MOBILE',
'W', 'H',
}
},

View file

@ -15,8 +15,7 @@ with custom event handling and redrawing.
# LibLL
Love Loader from v2.0 includes library to simplify creating custom interfaces.
It is like a backend for Love Loader.
Love Loader from 2.0 includes backend API to simplify creating custom skins.
It have not so many functions and fields:
@ -43,7 +42,9 @@ It have not so many functions and fields:
Creates game object (defined above) and returns it.
- `ll.gameAdd(conf, file, base, dir)` - same as `ll.gameNew`, but inserts game into `ll.games`.
- `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.
@ -57,11 +58,29 @@ It have not so many functions and fields:
- `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 simplify task to the game developers, this loader creates some global variables to use.
To reduce things to do for game developers, this loader creates some global variables to use.
You can also use it without Love Loader by including `ll-min.lua` file.
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.
@ -75,6 +94,8 @@ You can also use it without Love Loader by including `ll-min.lua` file.
`COLDIV`: color divider (1 or 255) to `love.graphics.setColor` function.
`MOBILE`: is this device runs Android or iOS?
# Fill game information
To fill game information in game folder need to create `info.ll` file.
@ -89,3 +110,4 @@ desc = Some descripion about the game.
pic = screen.png
pics = [ screen.png; screen2.png ] # wow arrays
```

View file

@ -1,7 +1,5 @@
return function(ll)
-- Classic UI from Love Loader v1
ll.cfg.pcht = ll.cfg.pcht or 60 * 5
local pikchv, pikcha = 0, 0
@ -10,8 +8,8 @@ local pikchao = math.floor(1 / ll.cfg.pcht * 2 * 255 + 0.5)
local cx, cy, cw, ch
local f, bf
local mx, mb, mpb = 0, 0, 0
local cdir, sel = 0, 1
local sel = 1
local cdir
function resize()
cw = W / 1.25
@ -23,40 +21,25 @@ function resize()
f = love.graphics.newFont(th / 3)
bf = love.graphics.newFont(th / 2)
ll.kbInit('h', cx, cx + cw)
end
local function update()
mx = love.mouse.getX()
mpb = mb
if love.mouse.isDown(1) then mb = 1
else mb = 0 end
local sdi = 0
if mpb == 1 and mb == 0 then
if mx <= cx
then sdi = 1
elseif mx >= cx + cw
then sdi = 2
else sdi = 3
end
end
if love.keyboard.isScancodeDown('left', 'a')
then sdi = 1
elseif love.keyboard.isScancodeDown('right', 'd')
then sdi = 2
elseif love.keyboard.isScancodeDown('return', 'space')
then sdi = 3
end
local sdi = ll.kbGet()
if cdir ~= sdi then
cdir = sdi
if sdi == 1
if sdi == '<'
then sel = sel - 1
elseif sdi == 2
elseif sdi == '>'
then sel = sel + 1
elseif sdi == 3 and ll.games[sel]
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
@ -78,11 +61,6 @@ local function update()
end
else pikcha = math.min(255, pikcha + pikchao)
end
if love.keyboard.isDown 'menu'
and mx >= W - 8
then pcall(select(2, pcall(require, 'dev.devtools')), ll)
end
end
local tm = 0

View file

@ -15,6 +15,9 @@ 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
@ -26,57 +29,38 @@ function resize()
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 my, mb, mpb
local sdir = 0
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
my = love.mouse.getY()
mpb = mb
if love.mouse.isDown(1) then mb = 1
else mb = 0
end
if mb == 0 and mpb == 1 then
if my <= H / 2 - ch
then sdi = 1
elseif my >= H / 2 + ch + cg
then sdi = 2
else sdi = 3
end
elseif love.keyboard.isScancodeDown('up', 'w')
then sdi = 1
elseif love.keyboard.isScancodeDown('down', 's')
then sdi = 2
elseif love.keyboard.isScancodeDown('return', 'space')
then sdi = 3
else sdi = 0
end
local sdi = ll.kbGet()
if sdi ~= sdir then
if sdi ~= 0
if sdi
then rcss = css
css = 1
if sdi == 3
then css = 1.5
end
psel = sel
logger = logger .. sdi
if logger == chc then
resize()
ll.devtools()
end
end
sdir = sdi
if sdi == 1
if sdi == '<'
then sel = sel - 1
elseif sdi == 2
elseif sdi == '>'
then sel = sel + 1
elseif sdi == 3 and ll.games[sel]
elseif sdi == 'o' and ll.games[sel]
then ll.mount(ll.games[sel])
end