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:
parent
f562b23197
commit
e46bc230a6
13 changed files with 242 additions and 153 deletions
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -1,3 +1,5 @@
|
|||
dev
|
||||
build/
|
||||
games/*
|
||||
!games/.gitkeep
|
||||
|
||||
|
|
|
@ -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
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
|
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
|
42
lib/load.lua
42
lib/load.lua
|
@ -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
|
||||
|
|
|
@ -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
|
|
@ -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
|
18
lib/main.lua
18
lib/main.lua
|
@ -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
|
||||
|
|
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
|
||||
|
||||
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
|
||||
|
|
|
@ -9,6 +9,7 @@ return {
|
|||
'llHome',
|
||||
'resize',
|
||||
'COLDIV',
|
||||
'MOBILE',
|
||||
'W', 'H',
|
||||
}
|
||||
},
|
||||
|
|
32
readme.md
32
readme.md
|
@ -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
|
||||
```
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
Loading…
Reference in a new issue