diff --git a/.gitignore b/.gitignore index 05ba253..81b0c56 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,5 @@ dev build/ games/* +!games/.gitkeep + diff --git a/lib/chroot.lua b/lib/chroot.lua index 4d09f14..f0abb42 100644 --- a/lib/chroot.lua +++ b/lib/chroot.lua @@ -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 diff --git a/lib/fs.lua b/lib/fs.lua new file mode 100644 index 0000000..e6088c3 --- /dev/null +++ b/lib/fs.lua @@ -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 diff --git a/lib/keyb.lua b/lib/keyb.lua new file mode 100644 index 0000000..e3c0719 --- /dev/null +++ b/lib/keyb.lua @@ -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 diff --git a/lib/load.lua b/lib/load.lua index acc60dd..04a40e1 100644 --- a/lib/load.lua +++ b/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 diff --git a/lib/love/chroot.lua b/lib/love/chroot.lua deleted file mode 100644 index 24d9cb5..0000000 --- a/lib/love/chroot.lua +++ /dev/null @@ -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 diff --git a/lib/love/load.lua b/lib/love/load.lua deleted file mode 100644 index 787d1d7..0000000 --- a/lib/love/load.lua +++ /dev/null @@ -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 diff --git a/lib/main.lua b/lib/main.lua index 86d4169..dee7506 100644 --- a/lib/main.lua +++ b/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 diff --git a/ll-min.lua b/ll-min.lua index 37edda9..eafd105 100644 --- a/ll-min.lua +++ b/ll-min.lua @@ -1,8 +1,17 @@ --- minimal Love Loader API +-- Minimal Love Loader API +-- Version 2.1 +-- (c) Er2 2022 +-- 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 diff --git a/luapack.lua b/luapack.lua index f4ab5ff..ab4dec7 100644 --- a/luapack.lua +++ b/luapack.lua @@ -9,6 +9,7 @@ return { 'llHome', 'resize', 'COLDIV', + 'MOBILE', 'W', 'H', } }, diff --git a/readme.md b/readme.md index b14a9b3..f0871b3 100644 --- a/readme.md +++ b/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 ``` + diff --git a/skins/gui.lua b/skins/gui.lua index 10f6c67..fdb2708 100644 --- a/skins/gui.lua +++ b/skins/gui.lua @@ -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 diff --git a/skins/psp.lua b/skins/psp.lua index fc4a34e..2427148 100644 --- a/skins/psp.lua +++ b/skins/psp.lua @@ -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 = '<>><' + 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