Merge branch 'titlescreen-refactor' of git.hersi.changeip.net:hersi/ExperimentalGear into titlescreen-refactor

This commit is contained in:
Hersi 2024-02-02 02:36:08 +01:00
commit 6352093ae3
74 changed files with 537 additions and 536 deletions

View File

@ -1,15 +0,0 @@
require "api.platform"
filesys = require "api.platform.filesys_impl"
if os.name() == Platform.WINDOWS then
require "api.platform.win.filesys"
elseif os.name() == Platform.LINUX then
require "api.platform.linux.filesys"
elseif os.name() == Platform.MACOS then
game.Log("MacOS specific implementation missing, loading linux filesys module", game.LOGGER_WARNING)
require "api.platform.linux.filesys"
else
game.Log("OS Platform not recognized, loading linux filesys module", game.LOGGER_WARNING)
require "api.platform.linux.filesys"
end

View File

@ -1,6 +0,0 @@
-- TODO(local): put these class types somewhere more common
---@class StrokeParams
---@field color number[]?
---@field alpha number?
---@field size number?

View File

@ -1,47 +0,0 @@
require "common.globals"
require "api.logging"
---@note setup code from: https://stackoverflow.com/a/30960054
local BinaryFormat = package.cpath:match("%?%.(%a+)")
DetailedLog("BinaryFormat: " .. BinaryFormat, game.LOGGER_DEBUG)
if BinaryFormat == "dll" then
function os.name()
return "Windows"
end
elseif BinaryFormat == "so" then
function os.name()
return "Linux"
end
elseif BinaryFormat == "dylib" then
function os.name()
return "MacOS"
end
else
DetailedLog("Could not identify binary format", game.LOGGER_WARNING)
function os.name()
return nil
end
end
BinaryFormat = nil
Platform = {
WINDOWS = "Windows",
LINUX = "Linux",
MACOS = "MacOS"
}
---Get OS platform lua is running on
function GetPlatform()
return os.name()
end
---Merge base module table with implementation table, overwriting base
---@param base any
---@param implementation any
function MergeModules(base, implementation)
for key, value in pairs(implementation) do
base[key] = value
end
end

View File

@ -1,51 +0,0 @@
local FileSys = require "api.platform.filesys_impl"
FileSys.sep = "/"
function FileSys.getcwd()
local cwd, popen = "", io.popen
local pfile, err = popen("pwd")
if not pfile or err then
game.Log(FileSys.__name .. ".getcwd() : popen failed executing " .. tostring(err), game.LOGGER_ERROR)
if pfile then
pfile:close()
end
return nil, err
end
cwd = pfile:read()
pfile:close()
return cwd
end
function FileSys.scandir(path)
if not FileSys.exists(path) then
return nil, "Incorrect or non-existent path"
end
local i, t, popen = 0, {}, io.popen
local pfile, err = popen('find "' .. path .. '" -maxdepth 1 -print0')
if not pfile or err then
game.Log(FileSys.__name .. ".scandir() : popen failed executing " .. tostring(err), game.LOGGER_ERROR)
if pfile then
pfile:close()
end
return nil, err
end
for filename in pfile:lines() do
i = i + 1
t[i] = filename
end
pfile:close()
return t
end

View File

@ -1,60 +0,0 @@
local FileSys = require "api.platform.filesys_impl"
FileSys.sep = "\\"
function FileSys.getcwd()
local cwd, popen = "", io.popen
local pfile, err = popen("cmd /C cd")
if not pfile or err then
game.Log(FileSys.__name .. ".getcwd() : popen failed executing " .. tostring(err), game.LOGGER_ERROR)
if pfile then
pfile:close()
end
return nil, err
end
cwd = pfile:read()
pfile:close()
return cwd
end
local baseNormpath = FileSys.normpath
function FileSys.normpath(path)
path = path:gsub("/", "\\")
path = baseNormpath(path)
return path
end
function FileSys.scandir(path)
if not FileSys.exists(path) then
return nil, "Incorrect or non-existent path"
end
local i, t, popen = 0, {}, io.popen
local pfile, err = popen('cmd /C dir "' .. path .. '" /b /a-d')
if not pfile or err then
game.Log(FileSys.__name .. ".scandir() : popen failed executing " .. tostring(err), game.LOGGER_ERROR)
if pfile then
pfile:close()
end
return nil, err
end
for filename in pfile:lines() do
i = i + 1
t[i] = filename
end
pfile:close()
return t
end

View File

@ -1,4 +1,4 @@
local Easing = require("lib.easing"); local Easing = require("scripts.core.libb.easing");
local Footer = require("components.footer"); local Footer = require("components.footer");
local DiffRectangle = require('components.diff_rectangle'); local DiffRectangle = require('components.diff_rectangle');
local common = require('common.util'); local common = require('common.util');

View File

@ -1,106 +0,0 @@
require("common.globals")
require "api.filesys"
--file reader utility functions
---Get game path
---@return string, string
local function getGamePath()
return debug.getinfo(1,"S").source:sub(2):match("(.*)([\\/])skins") -- this is very hacky :)
end
local function readBytes(_file)
local out = {}
repeat
local buffer = _file:read(4*1024)
for c in (buffer or ''):gmatch(".") do
table.insert(out, c:byte())
end
until not buffer
return out
end
---Read a file in the game folder by lines
---@param path string relative path to game file
---@param mode? openmode default "r"
---@return nil|string[]
function ReadGameFileLines(path, mode)
mode = mode or "r"
local gamepath, sep = getGamePath()
local lines = {}
local f = io.open(gamepath .. sep .. path, mode)
if not f then return nil end
for line in f:lines("l") do
table.insert(lines, line)
end
f:close()
return lines
end
---Read a file in the game folder
---@param path string # relative path to game file
---@param mode? openmode # default "r"
---@return nil|string|integer[]
function ReadGameFile(path, mode)
mode = mode or "r"
local gamepath, sep = getGamePath()
local out
local f = io.open(gamepath .. sep .. path, mode)
if not f then return nil end
if mode:match(".*b") then
out = readBytes(f)
else
out = f:read("a")
end
f:close()
return out
end
---Find patterns in file
---@param path string # relative path to game file
---@param pattern string # search pattern
---@return table # {{group1, group2, ...}, ...}
function FindPatterns(path, pattern)
local matches = {}
for _, line in ipairs(ReadGameFileLines(path, "r")) do
if line:match(pattern) then
table.insert(matches, {line:match(pattern)})
end
end
return matches
end
--- Check if a file or directory exists in this path
---@param file string # relative path to game file
---@return boolean # file exists
---@return string? # error message
function IsFileExists(file)
local gamepath, sep = getGamePath()
file = gamepath .. sep .. file
local ok, err, code = os.rename(file, file)
if not ok then
game.Log("err: "..err..", code: "..code, game.LOGGER_DEBUG)
if code == 13 then
-- Permission denied, but it exists
return true
end
end
return ok, err
end
--- Check if a directory exists in this path
---@param path string # relative path to game directory
---@return boolean # directory exists
function IsDir(path)
-- "/" works on both Unix and Windows
return IsFileExists(path .. "/")
end

View File

@ -1,11 +1,36 @@
require("common.filereader") require("core.game")
GameConfig = {} GameConfig = {}
function RefreshConfig() ---Find patterns in file
for _, match in ipairs(FindPatterns("Main.cfg", "(%w*)%s*=%s*\"?([^\"%s]*)\"?")) do ---@param path string # relative path to game file
---@param pattern string # search pattern
---@return table? matches # {{group1, group2, ...}, ...}
---@return string? errmsg
local function match_all(path, pattern)
local matches = {}
local text, err = game.file.open(path, "r")
if not text then
return nil, err
end
for line in text:lines() do
if line:match(pattern) then
table.insert(matches, {line:match(pattern)})
end
end
return matches
end
function GameConfig.refresh()
local config, err = match_all("Main.cfg", "(%w*)%s*=%s*\"?([^\"%s]*)\"?")
if not config then
game.Log("Unable to refresh GameConfig, " .. err, game.LOGGER_ERROR)
return
end
for _, match in ipairs(config) do
GameConfig[match[1]] = match[2] GameConfig[match[1]] = match[2]
end end
end end
RefreshConfig() GameConfig.refresh()

View File

@ -1,22 +0,0 @@
---Drewol, what are you doing? Why is there no game.LOGGER_DEBUG?
game.LOGGER_DEBUG = 0
-- add missing inputs
game.MOUSE_LEFT = 0
game.MOUSE_RIGHT = 1
game.MOUSE_MIDDLE = 2
game.KNOB_LEFT = 0
game.KNOB_RIGHT = 1
-- some cool extensions to the builtins ----------------------------------------
---Looks for the last match of pattern in the string.
---@param s string
---@param pattern string
---@param init? integer
---@param plain? boolean
function string.rfind(s, pattern, init, plain)
pattern = pattern:reverse()
return s:len() - s:reverse():find(pattern, init, plain) + 1
end

View File

@ -1,89 +1,14 @@
local function split(s, delimiter) --TODO: implement iton graphics object instead of free function
result = {};
for match in (s..delimiter):gmatch("(.-)"..delimiter) do
table.insert(result, match);
end
return result;
end
local function filter(tableIn, predicate)
local out = {}
for _, val in ipairs(tableIn) do
if predicate(val) then
table.insert(out, val)
end
end
return out
end
local function clamp(x, min, max)
if x < min then
x = min
end
if x > max then
x = max
end
return x
end
local function round(num)
return num + (2^52 + 2^51) - (2^52 + 2^51)
end
local function sign(x)
return (
(x > 0) and 1
or
(x < 0) and -1
or
0
)
end
local function roundToZero(x)
if x < 0 then
return math.ceil(x)
elseif x > 0 then
return math.floor(x)
else
return 0
end
end
local function areaOverlap(x, y, areaX, areaY, areaW, areaH) local function areaOverlap(x, y, areaX, areaY, areaW, areaH)
return x > areaX and y > areaY and x < areaX + areaW and y < areaY + areaH return x > areaX and y > areaY and x < areaX + areaW and y < areaY + areaH
end end
local function lerp(x, x0, y0, x1, y1) --modulo operation for table index value
return y0 + (x - x0) * (y1 - y0) / (x1 - x0)
end
--modulo operation for index value
local function modIndex(index, mod) local function modIndex(index, mod)
return (index - 1) % mod + 1 return (index - 1) % mod + 1
end end
local function firstAlphaNum(s)
for i = 1, string.len(s) do
local byte = string.byte(s, i);
if ((byte >= 65 and byte <= 90) or (byte >= 97 and byte <= 122) or (byte >= 48 and byte <= 57)) then
return string.sub(s, i, i);
end
end
return '';
end
return { return {
split = split,
filter = filter,
clamp = clamp,
round = round,
sign = sign,
roundToZero = roundToZero,
areaOverlap = areaOverlap, areaOverlap = areaOverlap,
lerp = lerp,
modIndex = modIndex, modIndex = modIndex,
firstAlphaNum = firstAlphaNum,
} }

View File

@ -1,21 +1,21 @@
local MAJOR = 0 local major = 0
local MINOR = 2 local minor = 2
local PATCH = 2 local patch = 2
local function getLongVersion() local function long_version()
return "USC:E:G:S:" .. MAJOR .. MINOR .. PATCH return "USC:E:G:S:" .. major .. minor .. patch
end end
---Get version string ---Get version string
---@return string ---@return string
local function getVersion() local function version()
return table.concat({MAJOR, MINOR, PATCH}, ".") return table.concat({major, minor, patch}, ".")
end end
return { return {
MAJOR = MAJOR, major = major,
MINOR = MINOR, minor = minor,
PATCH = PATCH, patch = patch,
getLongVersion = getLongVersion, long_version = long_version,
getVersion = getVersion version = version
} }

13
scripts/core/filesys.lua Normal file
View File

@ -0,0 +1,13 @@
require("core.os")
if os.platform() == Platform.WINDOWS then
require "core.platform.win.filesys"
elseif os.platform() == Platform.LINUX then
require "core.platform.linux.filesys"
elseif os.platform() == Platform.MACOS then
game.Log("MacOS platform not implemented, loading linux filesys module as fallback", game.LOGGER_WARNING)
require "core.platform.linux.filesys"
else
game.Log("OS Platform not recognized, loading linux filesys module", game.LOGGER_WARNING)
require "core.platform.linux.filesys"
end

108
scripts/core/game.lua Normal file
View File

@ -0,0 +1,108 @@
---Drewol, what are you doing? Why is there no game.LOGGER_DEBUG?
game.LOGGER_DEBUG = 0
-- add missing inputs
game.MOUSE_LEFT = 0
game.MOUSE_RIGHT = 1
game.MOUSE_MIDDLE = 2
game.KNOB_LEFT = 0
game.KNOB_RIGHT = 1
--file reader utility functions
require("core.filesys")
game.file = {
__name = "game.file"
}
---Get game path
---@return string, string # path, separator
local function get_game_path()
return debug.getinfo(1,"S").source:sub(2):match("(.*)([\\/])skins") -- this is very hacky :)
end
---Read bytes out of binary file
---@param _file file*
---@return integer[]
local function read_bytes(_file)
local out = {}
repeat
local buffer = _file:read(4*1024)
for c in (buffer or ''):gmatch(".") do
table.insert(out, c:byte())
end
until not buffer
return out
end
---Read a file in the game folder
---@param path string # relative path to game file
---@param mode? openmode # default "r"
---@return file*? lines
---@return string? errmsg
function game.file.open(path, mode)
mode = mode or "r"
local gamepath, sep = get_game_path()
return io.open(gamepath .. sep .. path, mode)
end
---Read a file in the game folder
---@param path string # relative path to game file
---@return string? text
---@return string? errmsg
function game.file.readfile(path)
local gamepath, sep = get_game_path()
local out
local f, err = io.open(gamepath .. sep .. path)
if not f then
return nil, err
end
out = f:read("a")
f:close()
return out
end
---Read a file in the game folder
---@param path string # relative path to game file
---@return integer[]? bytes
---@return string? errmsg
function game.file.readbinary(path)
local gamepath, sep = get_game_path()
local out
local f, err = io.open(gamepath .. sep .. path, "rb")
if not f then
return nil, err
end
out = read_bytes(f)
f:close()
return out
end
--- Check if a file or directory exists at the path
---@param path string # relative path to game file
---@return boolean # file exists
---@return string? # error message
function game.file.exists(path)
local gamepath, sep = get_game_path()
path = gamepath .. sep .. path
return filesys.exists(path)
end
--- Check if a directory exists in this path
---@param path string # relative path to game directory
---@return boolean # directory exists
---@return string? # error message
function game.file.isdir(path)
-- "/" works on both Unix and Windows
return game.file.exists(path .. "/")
end

6
scripts/core/init.lua Normal file
View File

@ -0,0 +1,6 @@
require("core.game")
require("core.os")
require("core.filesys")
require("core.math")
require("core.string")
require("core.table")

View File

@ -29,13 +29,6 @@ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
-- c = change == ending - beginning -- c = change == ending - beginning
-- d = duration (total time) -- d = duration (total time)
local sin = math.sin
local cos = math.cos
local pi = math.pi
local sqrt = math.sqrt
local abs = math.abs
local asin = math.asin
local function linear(t, b, c, d) local function linear(t, b, c, d)
return c * t / d + b return c * t / d + b
end end
@ -164,15 +157,15 @@ local function outInQuint(t, b, c, d)
end end
local function inSine(t, b, c, d) local function inSine(t, b, c, d)
return -c * cos(t / d * (pi / 2)) + c + b return -c * math.cos(t / d * (math.pi / 2)) + c + b
end end
local function outSine(t, b, c, d) local function outSine(t, b, c, d)
return c * sin(t / d * (pi / 2)) + b return c * math.sin(t / d * (math.pi / 2)) + b
end end
local function inOutSine(t, b, c, d) local function inOutSine(t, b, c, d)
return -c / 2 * (cos(pi * t / d) - 1) + b return -c / 2 * (math.cos(math.pi * t / d) - 1) + b
end end
local function outInSine(t, b, c, d) local function outInSine(t, b, c, d)
@ -221,21 +214,21 @@ end
local function inCirc(t, b, c, d) local function inCirc(t, b, c, d)
t = t / d t = t / d
return(-c * (sqrt(1 - (t ^ 2)) - 1) + b) return(-c * (math.sqrt(1 - (t ^ 2)) - 1) + b)
end end
local function outCirc(t, b, c, d) local function outCirc(t, b, c, d)
t = t / d - 1 t = t / d - 1
return(c * sqrt(1 - (t ^ 2)) + b) return(c * math.sqrt(1 - (t ^ 2)) + b)
end end
local function inOutCirc(t, b, c, d) local function inOutCirc(t, b, c, d)
t = t / d * 2 t = t / d * 2
if t < 1 then if t < 1 then
return -c / 2 * (sqrt(1 - t * t) - 1) + b return -c / 2 * (math.sqrt(1 - t * t) - 1) + b
else else
t = t - 2 t = t - 2
return c / 2 * (sqrt(1 - t * t) + 1) + b return c / 2 * (math.sqrt(1 - t * t) + 1) + b
end end
end end
@ -258,16 +251,16 @@ local function inElastic(t, b, c, d, a, p)
local s local s
if not a or a < abs(c) then if not a or a < math.abs(c) then
a = c a = c
s = p / 4 s = p / 4
else else
s = p / (2 * pi) * asin(c/a) s = p / (2 * math.pi) * math.asin(c/a)
end end
t = t - 1 t = t - 1
return -(a * (2 ^ (10 * t)) * sin((t * d - s) * (2 * pi) / p)) + b return -(a * (2 ^ (10 * t)) * math.sin((t * d - s) * (2 * math.pi) / p)) + b
end end
-- a: amplitud -- a: amplitud
@ -283,14 +276,14 @@ local function outElastic(t, b, c, d, a, p)
local s local s
if not a or a < abs(c) then if not a or a < math.abs(c) then
a = c a = c
s = p / 4 s = p / 4
else else
s = p / (2 * pi) * asin(c/a) s = p / (2 * math.pi) * math.asin(c/a)
end end
return a * (2 ^ (10 * t)) * sin((t * d - s) * (2 * pi) / p) + c + b return a * (2 ^ (10 * t)) * math.sin((t * d - s) * (2 * math.pi) / p) + c + b
end end
-- p = period -- p = period
@ -307,19 +300,19 @@ local function inOutElastic(t, b, c, d, a, p)
local s local s
if not a or a < abs(c) then if not a or a < math.abs(c) then
a = c a = c
s = p / 4 s = p / 4
else else
s = p / (2 * pi) * asin(c / a) s = p / (2 * math.pi) * math.asin(c / a)
end end
if t < 1 then if t < 1 then
t = t - 1 t = t - 1
return -0.5 * (a * (2 ^ (10 * t)) * sin((t * d - s) * (2 * pi) / p)) + b return -0.5 * (a * (2 ^ (10 * t)) * math.sin((t * d - s) * (2 * math.pi) / p)) + b
else else
t = t - 1 t = t - 1
return a * (2 ^ (-10 * t)) * sin((t * d - s) * (2 * pi) / p ) * 0.5 + c + b return a * (2 ^ (-10 * t)) * math.sin((t * d - s) * (2 * math.pi) / p ) * 0.5 + c + b
end end
end end
@ -443,4 +436,4 @@ return {
outBounce = outBounce, outBounce = outBounce,
inOutBounce = inOutBounce, inOutBounce = inOutBounce,
outInBounce = outInBounce, outInBounce = outInBounce,
} }

50
scripts/core/math.lua Normal file
View File

@ -0,0 +1,50 @@
---Clamp value between minimum and maximum
---@param x number
---@param min number
---@param max number
---@return number
function math.clamp(x, min, max)
if x < min then
x = min
end
if x > max then
x = max
end
return x
end
---Return closest numerical value to number
---@param num number
---@return integer
---@note From https://stackoverflow.com/a/76576968
function math.round(num)
return math.floor((math.floor(num*2) + 1)/2)
end
---Return sign of number
---@param x number
---@return integer
function math.sign(x)
if (x > 0) then
return 1
end
if (x < 0) then
return -1
end
return 0
end
---Linear interpolation
---@param x number # x to interpolate
---@param x0 number # x value begin
---@param x1 number # x value end
---@param y0 number # y value at x0
---@param y1 number # y value at x1
---@return number # interpolated y value at x
function math.lerp(x, x0, x1, y0, y1)
return y0 + (x - x0) * (y1 - y0) / (x1 - x0)
end

47
scripts/core/os.lua Normal file
View File

@ -0,0 +1,47 @@
---@enum Platform
Platform = {
WINDOWS = 0,
LINUX = 1,
MACOS = 2,
UNKNOWN = 3
}
function os.platform()
return Platform.UNKNOWN
end
function os.name()
return "Unknown"
end
---@note setup code from: https://stackoverflow.com/a/30960054
local binary_format = package.cpath:match("%p[\\|/]?%p(%a+)")
if binary_format == "dll" then
function os.platform()
return Platform.WINDOWS
end
function os.name()
return "Windows"
end
elseif binary_format == "so" then
function os.platform()
return Platform.LINUX
end
function os.name()
return "Linux"
end
elseif binary_format == "dylib" then
function os.platform()
return Platform.MACOS
end
function os.name()
return "MacOS"
end
end

View File

@ -1,39 +1,40 @@
---@diagnostic disable: duplicate-set-field
require "common.globals" require "common.globals"
require "api.logging" require "api.logging"
---@class FileSys require("core.string")
local FileSys = {
__name = "FileSys", ---@class filesys
filesys = {
__name = "filesys",
sep = "/" sep = "/"
} }
setmetatable(FileSys, FileSys)
---Return a normalized absolutized version of `path` ---Return a normalized absolutized version of `path`
---@param path string ---@param path string
---@return string ---@return string
function FileSys.abspath(path) function filesys.abspath(path)
return FileSys.normpath(FileSys.join(FileSys.getcwd(), path)) return filesys.normpath(filesys.join(filesys.getcwd(), path))
end end
---Return the base name of `path` ---Return the base name of `path`
---@param path string ---@param path string
---@return string ---@return string
function FileSys.basename(path) function filesys.basename(path)
return string.sub(path, string.rfind(path, FileSys.sep, 1, true) + 1) return string.sub(path, string.rfind(path, filesys.sep, 1, true) + 1)
end end
---Return the directory name of `path` ---Return the directory name of `path`
---@param path string ---@param path string
---@return string ---@return string
function FileSys.dirname(path) function filesys.dirname(path)
return FileSys.split(path)[1] return filesys.split(path)[1]
end end
---Return true if `path` refers to an existing path ---Return true if `path` refers to an existing path
---@param path string ---@param path string
---@return boolean, string? ---@return boolean, string?
function FileSys.exists(path) function filesys.exists(path)
local ok, err, code = os.rename(path, path) local ok, err, code = os.rename(path, path)
if not ok then if not ok then
DetailedLog("err: "..err..", code: "..code, game.LOGGER_DEBUG) DetailedLog("err: "..err..", code: "..code, game.LOGGER_DEBUG)
@ -61,20 +62,21 @@ end
---Return a string representing the current working directory ---Return a string representing the current working directory
---@return string ---@return string
function FileSys.getcwd() function filesys.getcwd()
error(FileSys.__name .. ".getcwd() : Not Implemented") assert(false, filesys.__name .. ".getcwd() : Not Implemented")
return ""
end end
---Join one or more path components with the platform specific separator ---Join one or more path components with the platform specific separator
---@param path string ---@param path string
---@param ... string ---@param ... string
---@return string ---@return string
function FileSys.join(path, ...) function filesys.join(path, ...)
local vargs = { select(1, ...) } local vargs = { select(1, ...) }
local joinedpath = path local joinedpath = path
for _, value in ipairs(vargs) do for _, value in ipairs(vargs) do
joinedpath = joinedpath .. FileSys.sep .. value joinedpath = joinedpath .. filesys.sep .. value
end end
return joinedpath return joinedpath
@ -84,8 +86,8 @@ end
---@param path string ---@param path string
---@param overrideSep? string ---@param overrideSep? string
---@return string ---@return string
function FileSys.normpath(path, overrideSep) function filesys.normpath(path, overrideSep)
local sep = overrideSep or FileSys.sep local sep = overrideSep or filesys.sep
--remove multiple slashes --remove multiple slashes
path = path:gsub("("..sep..")"..sep.."+", "%1") path = path:gsub("("..sep..")"..sep.."+", "%1")
@ -110,8 +112,8 @@ end
---@param path string ---@param path string
---@param relativeTo? string ---@param relativeTo? string
---@return string ---@return string
function FileSys.relpath(path, relativeTo) function filesys.relpath(path, relativeTo)
relativeTo = relativeTo or FileSys.getcwd() relativeTo = relativeTo or filesys.getcwd()
path = path:sub(path:find(relativeTo, 1, true)[2] + 1) path = path:sub(path:find(relativeTo, 1, true)[2] + 1)
return path return path
end end
@ -119,24 +121,23 @@ end
---Return a list of files and directory names in `path` ---Return a list of files and directory names in `path`
---@param path string ---@param path string
---@return string[] ---@return string[]
function FileSys.scandir(path) function filesys.scandir(path)
error(FileSys.__name .. ".scandir() : Not Implemented") assert(false, filesys.__name .. ".scandir() : Not Implemented")
return {}
end end
---Split `path` to (head, tail), tail is the last component, head is everything else ---Split `path` to (head, tail), tail is the last component, head is everything else
---@param path string ---@param path string
---@return string, string ---@return string, string
function FileSys.split(path) function filesys.split(path)
local lastSep = path:rfind(FileSys.sep, 1, true) local lastSep = path:rfind(filesys.sep, 1, true)
return path:sub(1, lastSep), path:sub(lastSep + 1) return path:sub(1, lastSep), path:sub(lastSep + 1)
end end
---Split `path` to (root, ext), such that `root + ext == path`, ext is either empty or starts with a '.' ---Split `path` to (root, ext), such that `root + ext == path`, ext is either empty or starts with a '.'
---@param path string ---@param path string
---@return string, string ---@return string, string
function FileSys.splitext(path) function filesys.splitext(path)
local lastSep = path:rfind(".", 1, true) local lastSep = path:rfind(".", 1, true)
return path:sub(1, lastSep - 1), path:sub(lastSep) return path:sub(1, lastSep - 1), path:sub(lastSep)
end end
return FileSys

View File

@ -0,0 +1,44 @@
---@diagnostic disable: duplicate-set-field
require("core.platform.filesys")
filesys.sep = "/"
---Return a string representing the current working directory
---@return string
function filesys.getcwd()
local cwd, popen = "", io.popen
local pfile, err = popen("pwd")
if not pfile or err ~= 0 then
game.Log(tostring(filesys) .. ".getcwd() : popen failed executing " .. tostring(err), game.LOGGER_ERROR)
return ""
end
cwd = pfile:read() or ""
pfile:close()
return cwd
end
---Return a list of files and directory names in `path`
---@param path string
---@return string[]
function filesys.scandir(path)
local i, t, popen = 0, {}, io.popen
local pfile, err = popen('find "' .. path .. '" -maxdepth 1 -print0')
if not pfile or err ~= 0 then
game.Log(tostring(filesys) .. ".scandir() : popen failed executing " .. tostring(err), game.LOGGER_ERROR)
return {}
end
for filename in pfile:lines() do
i = i + 1
t[i] = filename
end
pfile:close()
return t
end

View File

@ -0,0 +1,56 @@
---@diagnostic disable: duplicate-set-field
require("core.platform.filesys")
filesys.sep = "\\"
---Return a string representing the current working directory
---@return string
function filesys.getcwd()
local cwd, popen = "", io.popen
local pfile, err = popen("cd")
if not pfile or err ~= 0 then
game.Log(tostring(filesys) .. ".getcwd() : popen failed executing " .. tostring(err), game.LOGGER_ERROR)
return ""
end
cwd = pfile:read() or ""
pfile:close()
return cwd
end
local _normpath = filesys.normpath
---Normalize `path`, collapse redundant separators and up references
---@param path string
---@return string
function filesys.normpath(path)
path = _normpath(path)
path = path:gsub("/", "\\")
return path
end
---Return a list of files and directory names in `path`
---@param path string
---@return string[]
function filesys.scandir(path)
local i, t, popen = 0, {}, io.popen
local pfile, err = popen('dir "' .. path .. '" /b /ad')
if not pfile or err ~= 0 then
game.Log(tostring(filesys) .. ".scandir() : popen failed executing " .. tostring(err), game.LOGGER_ERROR)
return {}
end
for filename in pfile:lines() do
i = i + 1
t[i] = filename
end
pfile:close()
return t
end

23
scripts/core/string.lua Normal file
View File

@ -0,0 +1,23 @@
---Looks for the last match of pattern in the string.
---@param s string
---@param pattern string
---@param init? integer
---@param plain? boolean
function string.rfind(s, pattern, init, plain)
pattern = pattern:reverse()
return s:len() - s:reverse():find(pattern, init, plain) + 1
end
---Split string into list
---@param s string
---@param delimiter? string # default " "
---@return string[]
function string.split(s, delimiter)
delimiter = delimiter or " "
local result = {}
for match in (s..delimiter):gmatch("(.-)"..delimiter) do
table.insert(result, match)
end
return result
end

13
scripts/core/table.lua Normal file
View File

@ -0,0 +1,13 @@
---Filter elements of a table where the predicate is true
---@param t table
---@param predicate fun(x: any): boolean
---@return table
function table.filter(t, predicate)
local out = {}
for _, val in ipairs(t) do
if predicate(val) then
table.insert(out, val)
end
end
return out
end

View File

@ -1,4 +1,4 @@
json = require "lib.json" json = require "scripts.core.libb.json"
local header = {} local header = {}
header["user-agent"] = "unnamed_sdvx_clone" header["user-agent"] = "unnamed_sdvx_clone"

View File

@ -1,6 +1,6 @@
local VolforceWindow = require('components.volforceWindow') local VolforceWindow = require('components.volforceWindow')
local Dimensions = require 'common.dimensions'; local Dimensions = require 'scripts.graphics.dimensions';
do do
local resx, resy = game.GetResolution(); local resx, resy = game.GetResolution();
@ -21,7 +21,7 @@ local HitFX = require 'gameplay.hitfx'
local TrackEnd = require('gameplay.track_end') local TrackEnd = require('gameplay.track_end')
local json = require("lib.json") local json = require("scripts.core.libb.json")
local showHitAnims = true; local showHitAnims = true;

View File

@ -1,5 +1,5 @@
local Dimensions = require 'common.dimensions' local Dimensions = require 'scripts.graphics.dimensions'
local consoleBaseImage = gfx.CreateSkinImage("gameplay/console/base.png", 0) local consoleBaseImage = gfx.CreateSkinImage("gameplay/console/base.png", 0)
@ -32,4 +32,4 @@ end
return { return {
render=render render=render
} }

View File

@ -1,5 +1,5 @@
local Dimensions = require 'common.dimensions' local Dimensions = require 'scripts.graphics.dimensions'
local blackGradientImage = gfx.CreateSkinImage('gameplay/crit_line/black_gradient.png', 0) local blackGradientImage = gfx.CreateSkinImage('gameplay/crit_line/black_gradient.png', 0)
@ -111,4 +111,4 @@ end
return { return {
renderBase=renderBase, renderBase=renderBase,
renderOverlay=renderOverlay renderOverlay=renderOverlay
} }

View File

@ -1,9 +1,9 @@
require 'common.globals' require 'common.globals'
local Dimensions = require 'common.dimensions' local Dimensions = require 'scripts.graphics.dimensions'
local Animation = require 'api.animation' local Animation = require 'scripts.graphics.frameanimation'
local Animations = { local Animations = {
Crit = Animation.new('gameplay/hit_animation_frames/critical_taps', { Crit = Animation.new('gameplay/hit_animation_frames/critical_taps', {

View File

@ -1,5 +1,5 @@
local Common = require('common.util') local Common = require('common.util')
local Easing = require('lib.easing') local Easing = require('scripts.core.libb.easing')
local bgImage = gfx.CreateSkinImage("gameplay/track_end/bg.png", 0) local bgImage = gfx.CreateSkinImage("gameplay/track_end/bg.png", 0)
local bgHexTopImage = gfx.CreateSkinImage("gameplay/track_end/top_hex.png", gfx.IMAGE_REPEATX) local bgHexTopImage = gfx.CreateSkinImage("gameplay/track_end/top_hex.png", gfx.IMAGE_REPEATX)

View File

@ -1,5 +1,5 @@
local version = require("common.version") local version = require("common.version")
local Dim = require("common.dimensions") local Dim = require("scripts.graphics.dimensions")
local BAR_ALPHA = 191 local BAR_ALPHA = 191
@ -31,7 +31,7 @@ local function drawFooter()
gfx.FontSize(20) gfx.FontSize(20)
gfx.TextAlign(gfx.TEXT_ALIGN_LEFT + gfx.TEXT_ALIGN_TOP) gfx.TextAlign(gfx.TEXT_ALIGN_LEFT + gfx.TEXT_ALIGN_TOP)
gfx.FillColor(255, 255, 255, 255) gfx.FillColor(255, 255, 255, 255)
gfx.Text("EXPERIMENTALGEAR " .. version.MAJOR .. "." .. version.MINOR .. "." .. version.PATCH .. "", 8, 1895) gfx.Text("EXPERIMENTALGEAR " .. version.major .. "." .. version.minor .. "." .. version.patch .. "", 8, 1895)
end end
local function progressTransitions(deltaTime) local function progressTransitions(deltaTime)

View File

@ -6,7 +6,7 @@ require "api.filesys"
require "api.graphics" require "api.graphics"
local Image = require "api.image" local Image = require "scripts.graphics.image"
---@class AnimationParam: Animation ---@class AnimationParam: Animation
---@field animPath string ---@field animPath string

View File

@ -1,4 +1,4 @@
local Dim = require("common.dimensions") local Dim = require("scripts.graphics.dimensions")
local BAR_ALPHA = 191 local BAR_ALPHA = 191

View File

@ -1,6 +1,6 @@
require "common.globals" require "common.globals"
local Dim = require "common.dimensions" local Dim = require "scripts.graphics.dimensions"
local Image = require "api.image" local Image = require "scripts.graphics.image"
local BAR_ALPHA = 191 local BAR_ALPHA = 191
local HEADER_HEIGHT = 100 local HEADER_HEIGHT = 100
@ -30,4 +30,4 @@ local function draw()
gfx.Restore() gfx.Restore()
end end
return {draw = draw} return {draw = draw}

View File

@ -1,4 +1,4 @@
local Dim = require("common.dimensions") local Dim = require("scripts.graphics.dimensions")
local BAR_ALPHA = 191 local BAR_ALPHA = 191

View File

@ -0,0 +1 @@
--TODO: implement

View File

@ -1,4 +1,4 @@
local Dim = require("common.dimensions") local Dim = require("scripts.graphics.dimensions")
local backgroundImage = gfx.CreateSkinImage("bg_pattern.png", gfx.IMAGE_REPEATX | gfx.IMAGE_REPEATY) local backgroundImage = gfx.CreateSkinImage("bg_pattern.png", gfx.IMAGE_REPEATX | gfx.IMAGE_REPEATY)
local bgImageWidth, bgImageHeight = gfx.ImageSize(backgroundImage) local bgImageWidth, bgImageHeight = gfx.ImageSize(backgroundImage)
@ -20,4 +20,4 @@ end
return { return {
render = render render = render
} }

View File

@ -1,22 +0,0 @@
local call = nil
local EN = require("language.EN")
local DE = require("language.DE")
local SK = require("language.SK")
local HU = require("language.HU")
local test2 = require("language.test2")
if game.GetSkinSetting('words') == "EN" then
call = EN
elseif game.GetSkinSetting('words') == "DE" then
call = DE
elseif game.GetSkinSetting('words') == "SK" then
call = SK
elseif game.GetSkinSetting('words') == "HU" then
call = HU
elseif game.GetSkinSetting('words') == "test2" then
call = test2
end
return call

15
scripts/language/init.lua Normal file
View File

@ -0,0 +1,15 @@
local language = nil
if game.GetSkinSetting('words') == "EN" then
language = require("language.EN")
elseif game.GetSkinSetting('words') == "DE" then
language = require("language.DE")
elseif game.GetSkinSetting('words') == "SK" then
language = require("language.SK")
elseif game.GetSkinSetting('words') == "HU" then
language = require("language.HU")
elseif game.GetSkinSetting('words') == "test2" then
language = require("language.test2")
end
return language

View File

@ -1,4 +1,4 @@
local json = require("lib.json") local json = require("scripts.core.libb.json")
local common = require('common.util'); local common = require('common.util');
local Sound = require("common.sound") local Sound = require("common.sound")

View File

@ -1,10 +1,10 @@
local Easing = require('lib.easing'); local Easing = require('scripts.core.libb.easing');
local Charting = require('common.charting'); local Charting = require('common.charting');
local Background = require('components.background'); local Background = require('components.background');
local Footer = require('components.footer'); local Footer = require('components.footer');
local Numbers = require('components.numbers') local Numbers = require('components.numbers')
local DiffRectangle = require('components.diff_rectangle'); local DiffRectangle = require('components.diff_rectangle');
local lang = require("language.call") local lang = require("language")
local creww = game.GetSkinSetting("single_idol") local creww = game.GetSkinSetting("single_idol")

View File

@ -1,5 +1,5 @@
local Easing = require('lib.easing') local Easing = require('scripts.core.libb.easing')
local Dim = require("common.dimensions") local Dim = require("scripts.graphics.dimensions")
local SongSelectHeader = require('components.headers.songSelectHeader') local SongSelectHeader = require('components.headers.songSelectHeader')
local Footer = require('components.footer') local Footer = require('components.footer')

View File

@ -1,7 +1,7 @@
local Charting = require('common.charting') local Charting = require('common.charting')
local Easing = require('lib.easing') local Easing = require('scripts.core.libb.easing')
local Background = require('components.background') local Background = require('components.background')
local Dim = require("common.dimensions") local Dim = require("scripts.graphics.dimensions")
local Wallpaper = require("components.wallpaper") local Wallpaper = require("components.wallpaper")
local common = require('common.util') local common = require('common.util')
local Sound = require("common.sound") local Sound = require("common.sound")
@ -193,6 +193,17 @@ local resolutionChange = function(x, y)
game.Log('resX:' .. resX .. ' // resY:' .. resY .. ' // fullX:' .. fullX .. ' // fullY:' .. fullY, game.LOGGER_ERROR) game.Log('resX:' .. resX .. ' // resY:' .. resY .. ' // fullX:' .. fullX .. ' // fullY:' .. fullY, game.LOGGER_ERROR)
end end
local function firstAlphaNum(s)
for i = 1, string.len(s) do
local byte = string.byte(s, i)
if ((byte >= 65 and byte <= 90) or (byte >= 97 and byte <= 122) or (byte >= 48 and byte <= 57)) then
return string.sub(s, i, i)
end
end
return ""
end
function getCorrectedIndex(from, offset) function getCorrectedIndex(from, offset)
total = #songwheel.songs total = #songwheel.songs
@ -786,7 +797,7 @@ function drawScrollbar()
gfx.TextAlign(gfx.TEXT_ALIGN_MIDDLE + gfx.TEXT_ALIGN_CENTER) gfx.TextAlign(gfx.TEXT_ALIGN_MIDDLE + gfx.TEXT_ALIGN_CENTER)
if (songwheel.songs[selectedIndex] ~= nil) then if (songwheel.songs[selectedIndex] ~= nil) then
local title = songwheel.songs[selectedIndex].title; local title = songwheel.songs[selectedIndex].title;
local letter = string.upper(common.firstAlphaNum(title)) local letter = string.upper(firstAlphaNum(title))
gfx.Text(letter, fillXPos-10, scrollbarYPos + 5) gfx.Text(letter, fillXPos-10, scrollbarYPos + 5)
end end
end end

View File

@ -1,4 +1,4 @@
local Easing = require('lib.easing'); local Easing = require('scripts.core.libb.easing');
local resx, resy = game.GetResolution() local resx, resy = game.GetResolution()
local desw, desh = 1080, 1920 local desw, desh = 1080, 1920

View File

@ -1,6 +1,6 @@
require "common.globals" require "common.globals"
local Common = require "common.util" local Common = require "common.util"
local Dim = require "common.dimensions" local Dim = require "scripts.graphics.dimensions"
local Wallpaper = require "components.wallpaper" local Wallpaper = require "components.wallpaper"
local PageView = require "api.page.pageview" local PageView = require "api.page.pageview"

View File

@ -1,7 +1,7 @@
require "common.globals" require "common.globals"
require "common.class" require "common.class"
require "common.filereader" require "common.filereader"
local Dim = require "common.dimensions" local Dim = require "scripts.graphics.dimensions"
local Version = require "common.version" local Version = require "common.version"
local PageRegistry = require "api.page.pageregistry" local PageRegistry = require "api.page.pageregistry"
@ -33,7 +33,7 @@ function BootPage.new(params)
self:onInvalidation(reason) self:onInvalidation(reason)
end end
self:addField(ServiceField.new{posX = 32, posY = 32, label = Version.getLongVersion(), value = ""}) self:addField(ServiceField.new{posX = 32, posY = 32, label = Version.long_version(), value = ""})
self:addField(ServiceField.new{posX = 64, posY = 64, label = "UNNAMED SDVX CLONE STARTUP...", value = ""}) self:addField(ServiceField.new{posX = 64, posY = 64, label = "UNNAMED SDVX CLONE STARTUP...", value = ""})
local valueOffX = 220 local valueOffX = 220

View File

@ -1,5 +1,5 @@
require("common.class") require("common.class")
local Dim = require("common.dimensions") local Dim = require("scripts.graphics.dimensions")
local Page = require("api.page.page") local Page = require("api.page.page")
local CheckUpdateField = require("titlescreen.boot.checkupdatefield") local CheckUpdateField = require("titlescreen.boot.checkupdatefield")
local DialogField = require("titlescreen.boot.dialogfield") local DialogField = require("titlescreen.boot.dialogfield")

View File

@ -32,4 +32,3 @@ end
function DetailedLog(message, severity) function DetailedLog(message, severity)
game.Log(Where(": ", 3)..message, severity) game.Log(Where(": ", 3)..message, severity)
end end

View File

@ -1,5 +1,5 @@
require("common.class") require("common.class")
local Dim = require("common.dimensions") local Dim = require("scripts.graphics.dimensions")
local Field = require("api.page.field") local Field = require("api.page.field")
---@class ServiceField: Field ---@class ServiceField: Field

View File

@ -1,5 +1,5 @@
require("common.class") require("common.class")
local Dim = require("common.dimensions") local Dim = require("scripts.graphics.dimensions")
local Util = require("common.util") local Util = require("common.util")
local Page = require("api.page.page") local Page = require("api.page.page")
local ServiceField = require("api.page.servicefield") local ServiceField = require("api.page.servicefield")

View File

@ -4,8 +4,8 @@ require "common.class"
local Footer = require("components.footer") local Footer = require("components.footer")
local Wallpaper = require("components.wallpaper") local Wallpaper = require("components.wallpaper")
local Background = require("components.background") local Background = require("components.background")
local Dim = require("common.dimensions") local Dim = require("scripts.graphics.dimensions")
local lang = require("language.call") local lang = require("language")
local util = require("common.util") local util = require("common.util")
local cursorIndex = 3 local cursorIndex = 3
@ -472,7 +472,7 @@ end
local Background = require "components.background" local Background = require "components.background"
local Animation = require "api.animation" local Animation = require "scripts.graphics.frameanimation"
local AudioSample = require "api.audiosample" local AudioSample = require "api.audiosample"
local Page = require "api.page.page" local Page = require "api.page.page"

View File

@ -1,8 +1,8 @@
require "common.class" require "common.class"
local Dim = require "common.dimensions" local Dim = require "scripts.graphics.dimensions"
local Lang = require "language.call" local Lang = require "language"
local AudioSample = require "api.audiosample" local AudioSample = require "api.audiosample"
local Page = require "api.page.page" local Page = require "api.page.page"

View File

@ -1,5 +1,5 @@
require("common.class") require("common.class")
local Dim = require("common.dimensions") local Dim = require("scripts.graphics.dimensions")
local ServicePage = require("api.page.servicepage") local ServicePage = require("api.page.servicepage")
---@class ScreenCheckPage: ServicePage ---@class ScreenCheckPage: ServicePage

View File

@ -35,7 +35,7 @@ function VersionInfoPage.new(params)
local logStr = ReadGameFile("log_usc-game.exe.txt") or ReadGameFile("log_usc-game.txt") local logStr = ReadGameFile("log_usc-game.exe.txt") or ReadGameFile("log_usc-game.txt")
local list = ListField.new{selectedIndex = 2, locked = true} local list = ListField.new{selectedIndex = 2, locked = true}
list:addField(ServiceField.new{label = "SKIN ID CODE", value = Version.getLongVersion(), MARGIN = {0, 0, 0, 24}}) list:addField(ServiceField.new{label = "SKIN ID CODE", value = Version.long_version(), MARGIN = {0, 0, 0, 24}})
list:addField(UpdateField.new{label = "USC VERSION", value = getGameLogValue("Version", logStr)}) list:addField(UpdateField.new{label = "USC VERSION", value = getGameLogValue("Version", logStr)})
list:addField(ServiceField.new{label = "USC BRANCH", value = GameConfig["UpdateChannel"]}) list:addField(ServiceField.new{label = "USC BRANCH", value = GameConfig["UpdateChannel"]})
list:addField(ServiceField.new{label = "USC GIT COMMIT", value = getGameLogValue("Git commit", logStr), MARGIN = {0, 0, 0, 24}}) list:addField(ServiceField.new{label = "USC GIT COMMIT", value = getGameLogValue("Git commit", logStr), MARGIN = {0, 0, 0, 24}})

View File

@ -1,5 +1,5 @@
local Util = require("common.util") local Util = require("common.util")
local Dim = require("common.dimensions") local Dim = require("scripts.graphics.dimensions")
require "common.globals" require "common.globals"
require "common.class" require "common.class"

View File

@ -1,9 +1,9 @@
require "common.globals" require "common.globals"
require "common.class" require "common.class"
local Dim = require "common.dimensions" local Dim = require "scripts.graphics.dimensions"
local Image = require "api.image" local Image = require "scripts.graphics.image"
local Page = require "api.page.page" local Page = require "api.page.page"
@ -60,4 +60,4 @@ function CreditsPage:render(deltaTime)
self._timer = self._timer + deltaTime self._timer = self._timer + deltaTime
end end
return CreditsPage return CreditsPage

View File

@ -1,8 +1,8 @@
require "common.globals" require "common.globals"
require "common.class" require "common.class"
local Dim = require "common.dimensions" local Dim = require "scripts.graphics.dimensions"
local Image = require "api.image" local Image = require "scripts.graphics.image"
local AudioSample = require "api.audiosample" local AudioSample = require "api.audiosample"
local Page = require "api.page.page" local Page = require "api.page.page"
@ -71,4 +71,4 @@ function KShootManiaPage:render(deltaTime)
self._timer = self._timer + deltaTime self._timer = self._timer + deltaTime
end end
return KShootManiaPage return KShootManiaPage

View File

@ -1,9 +1,9 @@
require "common.globals" require "common.globals"
require "common.class" require "common.class"
local Dim = require "common.dimensions" local Dim = require "scripts.graphics.dimensions"
local Image = require "api.image" local Image = require "scripts.graphics.image"
local Page = require "api.page.page" local Page = require "api.page.page"
@ -60,4 +60,4 @@ function TeamExceedPage:render(deltaTime)
self._timer = self._timer + deltaTime self._timer = self._timer + deltaTime
end end
return TeamExceedPage return TeamExceedPage

View File

@ -1,9 +1,9 @@
require "common.globals" require "common.globals"
require "common.class" require "common.class"
local Dim = require "common.dimensions" local Dim = require "scripts.graphics.dimensions"
local Image = require "api.image" local Image = require "scripts.graphics.image"
local Page = require "api.page.page" local Page = require "api.page.page"
@ -60,4 +60,4 @@ function USCPage:render(deltaTime)
self._timer = self._timer + deltaTime self._timer = self._timer + deltaTime
end end
return USCPage return USCPage

View File

@ -1,13 +1,13 @@
require "common.globals" require "common.globals"
local Version = require "common.version" local Version = require "common.version"
local Dim = require "common.dimensions" local Dim = require "scripts.graphics.dimensions"
local Image = require "api.image" local Image = require "scripts.graphics.image"
local Page = require "api.page.page" local Page = require "api.page.page"
local versionString = Version.getLongVersion() local versionString = Version.long_version()
---@class TitlePage : Page ---@class TitlePage : Page
---@field background_img Image ---@field background_img Image
@ -51,4 +51,4 @@ function TitlePage:drawForeground(deltaTime)
gfx.Text(versionString, 10, 10) gfx.Text(versionString, 10, 10)
end end
return TitlePage return TitlePage