Compare commits
	
		
			No commits in common. "e46bc230a6ecef40329d64bcb32ac1c73557f8f9" and "9513a5c4717a0bc7a5ab24a293791df0136fe31c" have entirely different histories.
		
	
	
		
			e46bc230a6
			...
			9513a5c471
		
	
		
					 17 changed files with 208 additions and 1023 deletions
				
			
		
							
								
								
									
										5
									
								
								.gitignore
									
										
									
									
										vendored
									
									
								
							
							
						
						
									
										5
									
								
								.gitignore
									
										
									
									
										vendored
									
									
								
							|  | @ -1,5 +0,0 @@ | |||
| dev | ||||
| build/ | ||||
| games/* | ||||
| !games/.gitkeep | ||||
| 
 | ||||
|  | @ -1,37 +0,0 @@ | |||
| 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
									
										
									
									
									
								
							
							
						
						
									
										20
									
								
								lib/fs.lua
									
										
									
									
									
								
							|  | @ -1,20 +0,0 @@ | |||
| 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
									
										
									
									
									
								
							
							
						
						
									
										100
									
								
								lib/game.lua
									
										
									
									
									
								
							|  | @ -1,100 +0,0 @@ | |||
| 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
									
										
									
									
									
								
							
							
						
						
									
										81
									
								
								lib/keyb.lua
									
										
									
									
									
								
							|  | @ -1,81 +0,0 @@ | |||
| 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
									
										
									
									
									
								
							
							
						
						
									
										51
									
								
								lib/load.lua
									
										
									
									
									
								
							|  | @ -1,51 +0,0 @@ | |||
| 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
									
										
									
									
									
								
							
							
						
						
									
										27
									
								
								lib/main.lua
									
										
									
									
									
								
							|  | @ -1,27 +0,0 @@ | |||
| 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,17 +1,8 @@ | |||
| -- Minimal Love Loader API | ||||
| -- Version 2.1 | ||||
| -- (c) Er2 2022 <er2@dismail.de> | ||||
| -- Zlib License | ||||
| -- minimal Love Loader API | ||||
| 
 | ||||
| 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 | ||||
|  |  | |||
							
								
								
									
										17
									
								
								luapack.lua
									
										
									
									
									
								
							
							
						
						
									
										17
									
								
								luapack.lua
									
										
									
									
									
								
							|  | @ -1,17 +0,0 @@ | |||
| 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,71 +1,219 @@ | |||
| local ll = require 'lib.main' | ||||
| error = love.errhand or love.errorhandler | ||||
| 
 | ||||
| function splash() | ||||
|   love.graphics.setColor(255, 255, 255, 100 / COLDIV) | ||||
|   if ll.mgme.screens and ll.mgme.screens[1] then | ||||
|     local img = love.graphics.newImage(ll.mgme.screens[1]) | ||||
|     love.graphics.draw(img, 0, 0, 0, W / img:getWidth(), H / img:getHeight()) | ||||
|   end | ||||
|   love.graphics.setColor(255, 255, 255, 255) | ||||
|   love.graphics.print('Loading '..ll.mgme.name, W / 2, H / 2) | ||||
| end | ||||
| local root = 'games/' | ||||
| 
 | ||||
| ll.skin = require 'skins.psp' (ll) | ||||
| -- games library and selection | ||||
| local gms = {} | ||||
| local sel = 1 | ||||
| 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 | ||||
| resize = llResz | ||||
| 
 | ||||
| require 'll-min' | ||||
| llUsed = true | ||||
| 
 | ||||
| if love.errorhandler | ||||
| then love.errorhandler = error | ||||
| else love.errhand = error | ||||
| local function chroot(dir, main) | ||||
|   fd = love.filesystem.getSource() .. dir | ||||
|   love.filesystem.setRequirePath( | ||||
|     '?.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 | ||||
| 
 | ||||
| local brk = false | ||||
| while not brk and not ll.mdir do | ||||
| while not brk and not sgme do | ||||
|   -- event handling | ||||
|   love.event.pump() | ||||
|   for n, a,b,c in love.event.poll() do | ||||
|     if n == 'quit' | ||||
|     or (n == 'keypressed' and a == 'escape') | ||||
|     then ll.umount() | ||||
|   for n, a,b,c,d,e,f in love.event.poll() do | ||||
|     if n == 'quit' then | ||||
|       escChroot() | ||||
|       love.event.push('quit') | ||||
|       brk = true; break | ||||
|     end | ||||
|     love.handlers[n](a,b,c) | ||||
|     love.handlers[n](a,b,c,d,e,f) | ||||
|   end | ||||
| 
 | ||||
|   -- update and drawing | ||||
|   ll.skin.update() | ||||
|   llUpdate() | ||||
|   love.graphics.origin() | ||||
|   love.graphics.clear(0, 0, 0) | ||||
|   ll.skin.draw() | ||||
|   llDraw() | ||||
|   love.graphics.present() | ||||
|   love.timer.sleep(0.001) | ||||
| end | ||||
| 
 | ||||
| if ll.mdir then | ||||
| if sgme then | ||||
|   love.graphics.setNewFont() | ||||
|   resize = nil | ||||
| 
 | ||||
|   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()) | ||||
|   chroot(root .. sgme.dir, sgme.main) | ||||
| end | ||||
|  |  | |||
							
								
								
									
										97
									
								
								readme.md
									
										
									
									
									
								
							
							
						
						
									
										97
									
								
								readme.md
									
										
									
									
									
								
							|  | @ -2,99 +2,31 @@ | |||
| 
 | ||||
| Custom menu for selecting game from multiple for Love2D. | ||||
| 
 | ||||
|  | ||||
| 
 | ||||
| [Other screenshots](./scr) | ||||
| 
 | ||||
| # How it works? | ||||
| 
 | ||||
| Just place games into `games` folder! (like `this_folder/games/game/main.lua`) | ||||
| If simple: just place games in `games` folder with structure like `games/yourgame/main.lua` | ||||
| 
 | ||||
| Technically, this creates the loop until game wasn't selected or user wants to exit | ||||
| with custom event handling and redrawing. | ||||
| If technically: creates local variables functions and variables. | ||||
| Creates the loop until game wasn't selected with manual event handling and redrawing. | ||||
| 
 | ||||
| # LibLL | ||||
| # Things left or API | ||||
| 
 | ||||
| Love Loader from 2.0 includes backend API to simplify creating custom skins. | ||||
| We are not keep environment super clear (except local variables ;)) | ||||
| so there are some variables can be used in game: | ||||
| 
 | ||||
| It have not so many functions and fields: | ||||
| `W` and `H` variables: width and height of the screen, controlled by love.resize function. | ||||
| 
 | ||||
| - `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.resize` and optional `resize` payload: functions called when screen size when changed and at boot. | ||||
| 
 | ||||
| - `ll.mdir` - string or nil, contains full mounted directory. | ||||
| `love.event.quit`: function to quit to menu screen | ||||
| 
 | ||||
| - `ll.mgme` - game or nil, contains mounted game. | ||||
| `COLDIV`: color divider (1 or 255) to setColor function | ||||
| 
 | ||||
| - `ll.gameNew(configuration --[[string]], fileName --[[string, not used]], base --[[string]], directory --[[string]])` | ||||
| `llUsed`: is Love Loader used | ||||
| 
 | ||||
|   Creates game object (defined above) and returns it. | ||||
| `llHome`: function to quit to menu screen | ||||
| 
 | ||||
| - `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? | ||||
| They also can be used without Love Loader if load `ll-min.lua` | ||||
| 
 | ||||
| # Fill game information | ||||
| 
 | ||||
|  | @ -108,6 +40,5 @@ name = New awesome game using Love Loader | |||
| desc = Some descripion about the game. | ||||
| # main = optional main file instead of `main.lua` | ||||
| pic = screen.png | ||||
| pics = [ screen.png; screen2.png ] # wow arrays | ||||
| pics = [ screen.png; screen2.png ] # wow array | ||||
| ``` | ||||
| 
 | ||||
|  |  | |||
							
								
								
									
										
											BIN
										
									
								
								scr/base.png
									
										
									
									
									
								
							
							
						
						
									
										
											BIN
										
									
								
								scr/base.png
									
										
									
									
									
								
							
										
											Binary file not shown.
										
									
								
							| Before Width: | Height: | Size: 11 KiB | 
							
								
								
									
										
											BIN
										
									
								
								scr/gui.png
									
										
									
									
									
								
							
							
						
						
									
										
											BIN
										
									
								
								scr/gui.png
									
										
									
									
									
								
							
										
											Binary file not shown.
										
									
								
							| Before Width: | Height: | Size: 62 KiB | 
							
								
								
									
										
											BIN
										
									
								
								scr/psp.png
									
										
									
									
									
								
							
							
						
						
									
										
											BIN
										
									
								
								scr/psp.png
									
										
									
									
									
								
							
										
											Binary file not shown.
										
									
								
							| Before Width: | Height: | Size: 25 KiB | 
							
								
								
									
										190
									
								
								skins/base.lua
									
										
									
									
									
								
							
							
						
						
									
										190
									
								
								skins/base.lua
									
										
									
									
									
								
							|  | @ -1,190 +0,0 @@ | |||
| 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
									
										
									
									
									
								
							
							
						
						
									
										198
									
								
								skins/gui.lua
									
										
									
									
									
								
							|  | @ -1,198 +0,0 @@ | |||
| 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
									
										
									
									
									
								
							
							
						
						
									
										159
									
								
								skins/psp.lua
									
										
									
									
									
								
							|  | @ -1,159 +0,0 @@ | |||
| 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…
	
	Add table
		Add a link
		
	
		Reference in a new issue