feat(P.3.4): initial — Sporel Launcher v0.1.0 (FSM + click-UI + quit-confirm)
This commit is contained in:
133
init.lua
Normal file
133
init.lua
Normal file
@@ -0,0 +1,133 @@
|
||||
local input = require("lib-core.input")
|
||||
-- fsm.lua lives in this module's dir; engine_lua_lib's searcher only resolves
|
||||
-- declared lib-deps, so multi-file modules use dofile + engine.module.dir_of.
|
||||
local fsm = dofile(engine.module.dir_of("lib-sporel.launcher") .. "/fsm.lua")
|
||||
|
||||
local STATE_LIST, STATE_QUIT, EXIT = fsm.STATE_LIST, fsm.STATE_QUIT_CONFIRM, fsm.EXIT
|
||||
local state = STATE_LIST
|
||||
|
||||
local modules = {} -- [{id, version, name, rect={x,y,w,h}}]
|
||||
local quit_buttons = {} -- {yes = rect, no = rect}
|
||||
local screen_w, screen_h = 1280, 720 -- matched ENGINE_WINDOW_DEFAULT_WIDTH/HEIGHT
|
||||
|
||||
local TITLE = "Sporel Launcher"
|
||||
local QUIT_PROMPT = "Sporel beenden?"
|
||||
local YES_LABEL = "Ja"
|
||||
local NO_LABEL = "Nein"
|
||||
local EMPTY_LABEL = "Keine Module installiert."
|
||||
|
||||
local FONT_TITLE = 28
|
||||
local FONT_BUTTON = 20
|
||||
local PAD_X = 16
|
||||
local PAD_Y = 8
|
||||
|
||||
local COLOR_BG = 0x202020FF
|
||||
local COLOR_BTN = 0x404040FF
|
||||
local COLOR_HOVER = 0x606060FF
|
||||
local COLOR_TEXT = 0xE0E0E0FF
|
||||
local COLOR_DIM = 0x000000B4 -- semi-transparent overlay
|
||||
local COLOR_MODAL = 0x303030FF
|
||||
|
||||
local hit = engine.spatial.aabb_contains_point
|
||||
|
||||
function init(ctx)
|
||||
local list = engine.module.list()
|
||||
local y = 80
|
||||
for i, m in ipairs(list) do
|
||||
local label = m.name or m.id
|
||||
local w, h = engine.render.measure_text(label, FONT_BUTTON)
|
||||
local btn_w = w + 2 * PAD_X
|
||||
local btn_h = h + 2 * PAD_Y
|
||||
modules[i] = {
|
||||
id = m.id,
|
||||
version = m.version,
|
||||
name = label,
|
||||
rect = { x = (screen_w - btn_w) / 2, y = y, w = btn_w, h = btn_h }
|
||||
}
|
||||
y = y + btn_h + 8
|
||||
end
|
||||
|
||||
local wy, hy = engine.render.measure_text(YES_LABEL, FONT_BUTTON)
|
||||
local wn, hn = engine.render.measure_text(NO_LABEL, FONT_BUTTON)
|
||||
local btn_w = math.max(wy, wn) + 2 * PAD_X
|
||||
local btn_h = math.max(hy, hn) + 2 * PAD_Y
|
||||
local total_w = 2 * btn_w + 24
|
||||
quit_buttons.yes = { x = (screen_w - total_w) / 2, y = screen_h / 2 + 24, w = btn_w, h = btn_h }
|
||||
quit_buttons.no = { x = (screen_w - total_w) / 2 + btn_w + 24, y = screen_h / 2 + 24, w = btn_w, h = btn_h }
|
||||
|
||||
input.bind("ui_click", { "mouse_left" })
|
||||
input.bind("ui_back", { "escape" })
|
||||
input.bind("ui_confirm", { "enter" })
|
||||
|
||||
engine.print(string.format("launcher: init ok, modules=%d", #modules))
|
||||
end
|
||||
|
||||
function update(ctx, dt)
|
||||
local mx, my = engine.input.get_mouse_pos()
|
||||
|
||||
if state == STATE_LIST then
|
||||
if input.was_action_pressed("ui_back") then
|
||||
state = fsm.next(state, "esc")
|
||||
elseif input.was_action_pressed("ui_click") then
|
||||
for _, m in ipairs(modules) do
|
||||
if hit(m.rect.x, m.rect.y, m.rect.w, m.rect.h, mx, my) then
|
||||
engine.switch_module(m.id)
|
||||
return
|
||||
end
|
||||
end
|
||||
end
|
||||
elseif state == STATE_QUIT then
|
||||
if input.was_action_pressed("ui_back") then
|
||||
state = fsm.next(state, "esc")
|
||||
elseif input.was_action_pressed("ui_confirm") then
|
||||
engine.exit(0); return
|
||||
elseif input.was_action_pressed("ui_click") then
|
||||
if hit(quit_buttons.yes.x, quit_buttons.yes.y, quit_buttons.yes.w, quit_buttons.yes.h, mx, my) then
|
||||
engine.exit(0); return
|
||||
elseif hit(quit_buttons.no.x, quit_buttons.no.y, quit_buttons.no.w, quit_buttons.no.h, mx, my) then
|
||||
state = fsm.next(state, "click_no")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function render(ctx)
|
||||
local mx, my = engine.input.get_mouse_pos()
|
||||
|
||||
engine.render.clear_color(COLOR_BG)
|
||||
local tw = engine.render.measure_text(TITLE, FONT_TITLE)
|
||||
engine.render.draw_text(TITLE, (screen_w - tw) / 2, 24, FONT_TITLE, COLOR_TEXT)
|
||||
|
||||
if #modules == 0 then
|
||||
local ew = engine.render.measure_text(EMPTY_LABEL, FONT_BUTTON)
|
||||
engine.render.draw_text(EMPTY_LABEL, (screen_w - ew) / 2, screen_h / 2, FONT_BUTTON, COLOR_TEXT)
|
||||
else
|
||||
for _, m in ipairs(modules) do
|
||||
local color = hit(m.rect.x, m.rect.y, m.rect.w, m.rect.h, mx, my) and COLOR_HOVER or COLOR_BTN
|
||||
engine.render.draw_rect(m.rect.x, m.rect.y, m.rect.w, m.rect.h, color)
|
||||
local lw = engine.render.measure_text(m.name, FONT_BUTTON)
|
||||
engine.render.draw_text(m.name, m.rect.x + (m.rect.w - lw) / 2, m.rect.y + PAD_Y, FONT_BUTTON, COLOR_TEXT)
|
||||
end
|
||||
end
|
||||
|
||||
if state == STATE_QUIT then
|
||||
engine.render.draw_rect(0, 0, screen_w, screen_h, COLOR_DIM)
|
||||
local modal_w, modal_h = 360, 140
|
||||
engine.render.draw_rect((screen_w - modal_w) / 2, (screen_h - modal_h) / 2, modal_w, modal_h, COLOR_MODAL)
|
||||
local pw = engine.render.measure_text(QUIT_PROMPT, FONT_BUTTON)
|
||||
engine.render.draw_text(QUIT_PROMPT, (screen_w - pw) / 2, screen_h / 2 - 24, FONT_BUTTON, COLOR_TEXT)
|
||||
|
||||
for _, key in ipairs({ "yes", "no" }) do
|
||||
local b = quit_buttons[key]
|
||||
local color = hit(b.x, b.y, b.w, b.h, mx, my) and COLOR_HOVER or COLOR_BTN
|
||||
engine.render.draw_rect(b.x, b.y, b.w, b.h, color)
|
||||
local label = (key == "yes") and YES_LABEL or NO_LABEL
|
||||
local lw = engine.render.measure_text(label, FONT_BUTTON)
|
||||
engine.render.draw_text(label, b.x + (b.w - lw) / 2, b.y + PAD_Y, FONT_BUTTON, COLOR_TEXT)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function cleanup(ctx)
|
||||
engine.print("launcher: cleanup")
|
||||
end
|
||||
Reference in New Issue
Block a user