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. | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | 
 | ||||||
|  | [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…
	
	Add table
		Add a link
		
	
		Reference in a new issue