diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..6146740 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,4 @@ +*.html linguist-generated +*.css linguist-generated +*.sh linguist-generated +*.bat linguist-generated diff --git a/.gitignore b/.gitignore index 4f1656b..8831805 100644 --- a/.gitignore +++ b/.gitignore @@ -2,4 +2,5 @@ luacov.* vendor/ dist/ html/ -latex/ \ No newline at end of file +latex/ +.out \ No newline at end of file diff --git a/README.md b/README.md index 5762910..07c297d 100644 --- a/README.md +++ b/README.md @@ -32,5 +32,26 @@ Need a web server to work, use live server in your vscode for development and gi $ ./cli.sh build ./examples/asteroids/game.lua --core html5 ``` +Platform Support List +===================== + +| core | tier | native | plataform | +| :------------- | :----: | :----: | --------: | +| ginga | tier 1 | yes | TV | +| love | tier 1 | no | Library | +| repl | tier 1 | yes | PC | +| curses | tier ? | yes | PC | +| html5 | tier 2 | no | Browser | +| html5_webos | tier 2 | no | TV | +| html5_tizen | tier 2 | no | TV | +| html5_ginga | tier ? | no | TV | +| esp32 | tier ? | yes | Embed | +| roblox | tier ? | no | Game | +| arkos | tier ? | yes | Console | +| odroid | tier ? | yes | Console | +| raylib | tier ? | no | Library | +| Nintendo GBA | tier ? | yes | Console | +| Nintendo WII | tier ? | yes | Console | + --- This game engine is **open source** and **free** for all uses, focusing on promoting content for our **commercial platform**. diff --git a/SUPPORT.md b/SUPPORT.md deleted file mode 100644 index e97b055..0000000 --- a/SUPPORT.md +++ /dev/null @@ -1,27 +0,0 @@ -Platform Support List -===================== - -Tier 1 ------- - - - [X] [Ginga](https://github.com/TeleMidia/ginga) - - [X] [Love2D](https://github.com/love2d/love) - - [X] REPL - -Tier 2 ------- - - - [ ] Browser - -Tier 3 ------- - - - [ ] Curses - - [ ] Console - -Mayable ??? ---- - - - Roblox - - GameBoyAdvance - - ESP32 diff --git a/src/cli/main.lua b/src/cli/main.lua index 108375f..0267816 100644 --- a/src/cli/main.lua +++ b/src/cli/main.lua @@ -1,5 +1,6 @@ local os = require('os') local zeebo_args = require('src/shared/args') +local zeebo_meta = require('src/cli/meta') local zeebo_fs = require('src/cli/fs') --! @cond @@ -30,11 +31,28 @@ local core_list = { 'src/lib/ginga/main.ncl' } }, - ginga_html5={ + html5_webos={ + src='src/lib/html5/main.lua', + post_exe='webos24 $(pwd)/dist', + pipeline={ + zeebo_meta.late(game):file(dist..'index.html'):file(dist..'appinfo.json'):pipe() + }, + extras={ + 'src/lib/html5_webos/appinfo.json', + 'src/lib/html5_webos/icon.png', + 'src/lib/html5/index.html', + 'src/lib/html5/index.html', + 'src/lib/html5/engine.js', + } + }, + html5_ginga={ src='src/lib/html5/main.lua', post_exe='ginga dist/main.ncl -s '..screen, + pipeline={ + zeebo_meta.late(game):file(dist..'index.html'):pipe() + }, extras={ - 'src/lib/ginga_html5/main.ncl', + 'src/lib/html5_ginga/main.ncl', 'src/lib/html5/index.html', 'src/lib/html5/index.html', 'src/lib/html5/engine.js', @@ -42,6 +60,9 @@ local core_list = { }, html5={ src='src/lib/html5/main.lua', + pipeline={ + zeebo_meta.late(game):file(dist..'index.html'):pipe() + }, extras={ 'src/lib/html5/index.html', 'src/lib/html5/engine.js' @@ -57,6 +78,11 @@ if command == 'run' then os.exit(os.execute(core_list[core].exe) and 0 or 1) elseif command == 'clear' then zeebo_fs.clear(dist) +elseif command == 'meta' then + if core == 'ginga' then + core = '{{title}} {{version}}' + end + zeebo_meta.current(game):stdout(core):run() elseif command == 'bundler' then local path, file = game:match("(.-)([^/\\]+)$") zeebo_fs.bundler(path, file, dist..file) @@ -116,10 +142,22 @@ elseif command == 'build' then zeebo_fs.clear(dist..bundler) end + -- post process + if core.pipeline then + local index = 1 + while index <= #core.pipeline do + local eval = core.pipeline[index] + while type(eval) == 'function' do + eval = eval() + end + index = index + 1 + end + end + if run then if not core.post_exe then print('this core cannot be runned after build!') - exit(1) + os.exit(1) end os.exit(os.execute(core.post_exe) and 0 or 1) end diff --git a/src/cli/meta.lua b/src/cli/meta.lua new file mode 100644 index 0000000..7883bcf --- /dev/null +++ b/src/cli/meta.lua @@ -0,0 +1,104 @@ +local application_default = require('src/object/application') + +local function replace(src, meta, default) + if src and #src > 0 then + return (src + :gsub('{{id}}', meta.id or default.id) + :gsub('{{title}}', meta.title or default.title) + :gsub('{{company}}', meta.company or default.company) + :gsub('{{version}}', meta.version or default.version) + :gsub('{{description}}', meta.description or default.description) + ) + end + return '' +end + +local function file(self, file) + local file_copy = string.format("%s", file) + self.pipeline[#self.pipeline + 1] = function() + if not self.loaded then return self end + + local file_meta = io.open(file_copy, 'r') + local file_temp = io.open(file_copy..'.tmp', 'w') + + repeat + local line = file_meta:read() + file_temp:write(replace(line, self.meta, application_default.meta), '\n') + until not line + + file_meta:close() + file_temp:close() + + os.remove(file_copy) + os.rename(file_copy..'.tmp', file_copy) + end + return self +end + +local function stdout(self, format) + local format_copy = string.format("%s", format) + if format_copy == 'json' then + format_copy = '{"id":"{{id}}","title":"{{title}}","company":"{{company}}",' + format_copy = format_copy..'"version":"{{version}}","description":"{{description}}"}' + end + self.pipeline[#self.pipeline + 1] = function() + if not self.loaded then return self end + print(replace(format_copy, self.meta, application_default.meta)) + end + return self +end + +local function pipe(self) + return function() + self:run() + end +end + +local function run(self) + local index = 1 + while index <= #self.pipeline do + self.pipeline[index]() + index = index + 1 + end + return self +end + +local function current(game, application) + local metadata = game and #game > 0 and dofile(game) + + if not application then + application = { + meta = application_default.meta, + pipeline={}, + file=file, + stdout=stdout, + pipe=pipe, + run=run + } + end + + if metadata then + application.loaded = true + application.meta = metadata.meta + end + + return application +end + +local function late(game) + local game_copy = string.format("%s", game) + local application = current() + local eval = application.run + application.run = function() + current(game_copy, application) + eval(application) + end + return application +end + +local P = { + current = current, + late = late +} + +return P diff --git a/src/lib/common/math.lua b/src/lib/common/math.lua index 6f982fb..de6cb10 100644 --- a/src/lib/common/math.lua +++ b/src/lib/common/math.lua @@ -3,6 +3,30 @@ --! @defgroup math --! @{ +--! @short abs module +--! @par Equation +--! @f$ |value| @f$ +--! @param[in] value +--! @return number +local function abs(value) + if value < 0 then + return -value + end + return value +end + +--! @short clamp +--! @par Equation +--! @f$ +--! \begin{cases} +--! value\_min, & \text{if } value \gt value\_min \\ +--! value\_max, & \text{if } value \lt value\_max \\ +--! value, & \text{if } value\_min \lt value \lt value\_max +--! \end{cases} +--! @f$ +--! @param[in] value The value to clamp. +--! @param[in] value_min The minimum value that value can be clamped to. +--! @param[in] value_max The maximum value that value can be clamped to. local function clamp(value, value_min, value_max) if value < value_min then return value_min @@ -13,6 +37,56 @@ local function clamp(value, value_min, value_max) end end +--! @short clamp +--! @note similar to @ref clamp but cyclical. +--! @par Equation +--! @f$ +--! (value - value\_min) \mod (value\_max - value\_min + 1) + value\_min +--! @f$ +--! @param[in] value The value to clamp. +--! @param[in] value_min The minimum value that value can be clamped to. +--! @param[in] value_max The maximum value that value can be clamped to. +local function clamp2(value, value_min, value_max) + return (value - value_min) % (value_max - value_min + 1) + value_min +end + +--! @short periodic cycle +--! @par Equation +--! @f$ +--! \begin{cases} +--! \frac{passed \mod duration}{duration}, & \text{if } (passed \mod duration \neq 0) \\ +--! \frac{passed \mod (2 \times duration)}{duration}, & \text{if } (passed \mod duration = 0) +--! \end{cases} +--! @f$ +--! @param[in] passed +--! @param[in] duration +--! @retval 0 start of period +--! @retval 0.5 middle of period +--! @retval 1 end of period +--! @par Example +--! @code +--! local anim = std.math.cycle(game.millis, 1000) * 5 +--! std.draw.text(x, y + anim, 'hello!') +--! @endcode +local function cycle(passed, duration) + local endtime = (passed) % duration + return ((endtime == 0 and (passed % (duration * 2)) or endtime)) / duration +end + +--! @short direction +--! @par Equation +--! @f$ +--! \begin{cases} +--! -1, & \text{if } |value| \gt \alpha \land value \lt 0 \\ +--! 1, & \text{if } |value| \gt \alpha \land value \gt 0 \\ +--! 0, & \text{if } |value| \leq \alpha +--! \end{cases} +--! @f$ +--! @param[in] value +--! @param[in] alpha @c default=0 +--! @retval -1 less than alpha +--! @retval 0 when in alpha +--! @retval 1 greater than alpha local function dir(value, alpha) alpha = alpha or 0 if value < -alpha then @@ -24,52 +98,74 @@ local function dir(value, alpha) end end +--! @brief euclidean distance +--! @par Equation +--! @f$ +--! \sqrt{(x_2 - x_1)^2 + (y_2 - y_1)^2} +--! @f$ +--! @param[in] x1 The x coordinate of the first point. +--! @param[in] y1 The y coordinate of the first point. +--! @param[in] x2 The x coordinate of the second point. +--! @param[in] y2 The y coordinate of the second point. +--! @return distance between the two points (x1, y1) and (x2, y2). local function dis(x1,y1,x2,y2) return ((x2 - x1) ^ 2 + (y2 - y1) ^ 2) ^ 0.5 end -local function abs(value) - if value < 0 then - return -value - end - return value -end - -local function saw(value) - if value < 0.25 then - return value * 4 - elseif value < 0.50 then - return 1 - ((value - 0.25) * 4) - elseif value < 0.75 then - return ((value - 0.50) * 4) * (-1) - end - return ((value - 0.75) * 4) - 1 +--! @brief quadratic distance +--! @note this is an optimization of @ref dis but it cannot be used to calculate collisions. +--! @par Equation +--! @f$ +--! (x_2 - x_1)^2 + (y_2 - y_1)^2 +--! @f$ +--! @param[in] x1 The x coordinate of the first point. +--! @param[in] y1 The y coordinate of the first point. +--! @param[in] x2 The x coordinate of the second point. +--! @param[in] y2 The y coordinate of the second point. +--! @return distance between the two points (x1, y1) and (x2, y2). +local function dis2(x1,y1,x2,y2) + return (x2 - x1) ^ 2 + (y2 - y1) ^ 2 end +--! @brief linear interpolation +--! @par Equation +--! @f$ +--! a + \alpha \cdot (b - a) +--! @f$ +--! @param[in] a The starting value +--! @param[in] b The ending value +--! @param[in] alpha The interpolation parameter, typically in the range [0, 1]. +--! @return The interpolated value between 'a' and 'b' based on 'alpha'. local function lerp(a, b, alpha) return a + alpha * ( b - a ) end +--! @brief re-maps +--! @li +--! +--! @par Equation +--! @f$ +--! (value - in\_min) \cdot \frac{(out\_max - out\_min)}{(in\_max - in\_min)} + out\_min +--! @f$ +--! @param[in] value The value to be mapped from the input range to the output range. +--! @param[in] in_min The minimum value of the input range. +--! @param[in] in_max The maximum value of the input range. +--! @param[in] out_min The minimum value of the output range. +--! @param[in] out_max The maximum value of the output range. +--! @return The mapped value in the output range corresponding to 'value' in the input range. local function map(value, in_min, in_max, out_min, out_max) return (value - in_min) * (out_max - out_min) / (in_max - in_min) + out_min end -local function clamp2(value, value_min, value_max) - return (value - value_min) % (value_max - value_min + 1) + value_min -end - -local function cycle(passed, duration) - local endtime = (passed) % duration - return ((endtime == 0 and (passed % (duration * 2)) or endtime)) / duration -end - +--! @shor maximum +--! @todo document this local function max(...) local args = {...} local index = 1 local value = nil local max_value = nil - if #args == 1 and type(args[1]) == "table" then + if #args == 1 then args = args[1] end @@ -84,20 +180,67 @@ local function max(...) return max_value end +--! @short minimum +--! @todo document this +local function min(...) + local args = {...} + local index = 1 + local value = nil + local min_value = nil + + if #args == 1 then + args = args[1] + end + + while index <= #args do + value = args[index] + if min_value == nil or value < min_value then + min_value = value + end + index = index + 1 + end + + return min_value +end + +--! @brief sawtooth +--! @todo document this +--! @par Equation +--! @f$ +--! \begin{cases} +--! value \times 4, & \text{if } 0 \leq value < 0.25 \\ +--! 1 - ((value - 0.25) \times 4), & \text{if } 0.25 \leq value < 0.50 \\ +--! ((value - 0.50) \times 4) \times (-1), & \text{if } 0.50 \leq value < 0.75 \\ +--! ((value - 0.75) \times 4) - 1, & \text{if } 0.75 \leq value \leq 1 \\ +--! \end{cases} +--! @f$ +local function saw(value) + if value < 0.25 then + return value * 4 + elseif value < 0.50 then + return 1 - ((value - 0.25) * 4) + elseif value < 0.75 then + return ((value - 0.50) * 4) * (-1) + end + return ((value - 0.75) * 4) - 1 +end + --! @} --! @} local P = { - cycle=cycle, + abs=abs, clamp=clamp, clamp2=clamp2, + cycle=cycle, + dir=dir, + dis=dis, + dis2=dis2, lerp=lerp, - abs=abs, map=map, - dis=dis, - saw=saw, max=max, - dir=dir + min=min, + saw=saw } return P; diff --git a/src/lib/ginga/main.lua b/src/lib/ginga/main.lua index 2e7015f..b5d4600 100644 --- a/src/lib/ginga/main.lua +++ b/src/lib/ginga/main.lua @@ -154,7 +154,7 @@ local function setup(evt) game.width=w game.height=h game.fps_max = application.config and application.config.fps_max or 100 - game.fps_show = application.config and application.config.fps_max or 0 + game.fps_show = application.config and application.config.fps_show or 0 fps_obj.drop_time = application.config and application.config.fps_time or 1 fps_obj.drop_count = application.config and application.config.fps_drop or 2 application.callbacks.init(std, game) diff --git a/src/lib/html5/index.html b/src/lib/html5/index.html index e84cca2..8773e80 100644 --- a/src/lib/html5/index.html +++ b/src/lib/html5/index.html @@ -1,9 +1,15 @@ - Lua Engine + {{title}} - {{version}} + + + + + +