From 07e671eef9b3d99dfc96646b1082869457722693 Mon Sep 17 00:00:00 2001 From: Bauumm Date: Sun, 3 Sep 2023 18:49:38 +0200 Subject: [PATCH] add basic death screen --- game_handler/init.lua | 51 ++++++++++++++++++++++++++++++-------- game_handler/input.lua | 4 +++ ui/elements/dropdown.lua | 5 ---- ui/init.lua | 22 ++++++++++++---- ui/keyboard_navigation.lua | 13 +++++++--- ui/overlays/init.lua | 10 +++++++- ui/screens/game.lua | 44 ++++++++++++++++++++++++++++++++ 7 files changed, 124 insertions(+), 25 deletions(-) diff --git a/game_handler/init.lua b/game_handler/init.lua index 2d63215..c45c858 100644 --- a/game_handler/init.lua +++ b/game_handler/init.lua @@ -8,6 +8,7 @@ local games = { [20] = require("compat.game20"), [21] = require("compat.game21"), } +local last_pack, last_level, last_level_settings, last_version local current_game local current_game_version local first_play = true @@ -47,13 +48,15 @@ function game_handler.set_version(version) error("Game with version '" .. version .. "' does not exist or is unsupported.") end current_game_version = version + last_version = version end ---start a level and start recording a replay ---@param pack string ---@param level string ---@param level_settings table -function game_handler.record_start(pack, level, level_settings) +---@param is_retry boolean = false +function game_handler.record_start(pack, level, level_settings, is_retry) current_game.death_callback = function() if current_game.update_save_data ~= nil then current_game.update_save_data() @@ -66,8 +69,7 @@ function game_handler.record_start(pack, level, level_settings) end current_game.persistent_data = game_handler.profile.get_data(pack) - -- TODO: false when retrying - first_play = true + first_play = not is_retry input.replay = Replay:new() input.replay:set_game_data( @@ -79,11 +81,21 @@ function game_handler.record_start(pack, level, level_settings) level, level_settings ) + current_game.first_play = first_play input.record_start() current_game.start(pack, level, level_settings) start_time = love.timer.getTime() real_start_time = start_time current_game.update(1 / current_game.tickrate) + last_pack = pack + last_level = level + last_level_settings = level_settings +end + +---retry the level that was last started with record_start +function game_handler.retry() + game_handler.set_version(last_version) + game_handler.record_start(last_pack, last_level, last_level_settings, true) end ---read a replay file and run the game with its inputs and seeds @@ -131,6 +143,12 @@ function game_handler.stop() current_game.stop() end +---check if the game is replaying a replay +---@return boolean +function game_handler.is_replaying() + return input.is_replaying() +end + ---process an event (mainly used for aspect ratio resizing) ---@param name string ---@param ... unknown @@ -161,6 +179,25 @@ function game_handler.process_event(name, ...) end end +---get the dimensions of the game canvas (returns 0, 0 if it was not created yet) +---@return integer +---@return integer +function game_handler.get_game_dimensions() + if screen then + return screen:getDimensions() + else + return 0, 0 + end +end + +---get the position of the game canvas on the screen +---@return number +---@return number +function game_handler.get_game_position() + local width, height = love.graphics.getDimensions() + return (width - width * scale[1]) / 2, (height - height * scale[2]) / 2 +end + ---save the score and replay of the current attempt (gets called automatically on death) function game_handler.save_score() local elapsed_time = love.timer.getTime() - real_start_time @@ -193,13 +230,6 @@ function game_handler.update(ensure_tickrate) game_handler.onupdate() end end - --for "testing" purposes only (i just wante to play the game - if current_game.is_dead() then - current_game.stop() - local ui = require("ui") - ui.open_screen("levelselect") - --note: when reloading this room after the game, the score isnt in the menu until you reselect. this isnt a bug because there will be a proper death screen with retrying stuff in the future. - end end end @@ -223,6 +253,7 @@ function game_handler.draw(frametime) -- the color of the canvas' contents will look wrong if color isn't white love.graphics.setColor(1, 1, 1, 1) love.graphics.draw(screen) + love.graphics.origin() end end diff --git a/game_handler/input.lua b/game_handler/input.lua index 61ee07d..a1d8412 100644 --- a/game_handler/input.lua +++ b/game_handler/input.lua @@ -32,6 +32,10 @@ function input.replay_start() input.is_done_replaying = false end +function input.is_replaying() + return replaying +end + ---save the next seed when recording or get the next seed when replaying ---@param seed number ---@return number? diff --git a/ui/elements/dropdown.lua b/ui/elements/dropdown.lua index 4fbaac1..ccd6e5a 100644 --- a/ui/elements/dropdown.lua +++ b/ui/elements/dropdown.lua @@ -106,11 +106,6 @@ function dropdown:new(selections, options) end end end - local quad_scale = obj.set_scale - obj.set_scale = function(elem, scale) - quad_scale(elem, scale) - elem.selection_quad:set_scale(scale) - end local quad_layout = obj.calculate_layout obj.calculate_layout = function(elem, available_area) local w, h = quad_layout(elem, available_area) diff --git a/ui/init.lua b/ui/init.lua index 556c437..d4e7f88 100644 --- a/ui/init.lua +++ b/ui/init.lua @@ -1,6 +1,7 @@ local signal = require("ui.anim.signal") local overlays = require("ui.overlays") local flex = require("ui.layout.flex") +local game_handler = require("game_handler") local ui = {} local screens = { test = require("ui.screens.test"), @@ -22,11 +23,16 @@ function ui.set_scale(scale) end local function calculate_layout(width, height) + local game_width, game_height, x, y + if game_handler.is_running() then + game_width, game_height = game_handler.get_game_dimensions() + x, y = game_handler.get_game_position() + end local screen_area = { - x = 0, - y = 0, - width = width or love.graphics.getWidth(), - height = height or love.graphics.getHeight(), + x = x or 0, + y = y or 0, + width = width or game_width or love.graphics.getWidth(), + height = height or game_height or love.graphics.getHeight(), } local res_width, res_height = current_screen:calculate_layout(screen_area) -- as long as the resulting layout is smaller than the window, up gui scale (until user setting is reached) @@ -59,15 +65,21 @@ function ui.open_screen(name) end end +function ui.get_screen() + return current_screen +end + ---process a window event ---@param name string ---@param ... unknown function ui.process_event(name, ...) - local stop_propagation = overlays.process_event(name, ...) if current_screen then if name == "resize" then calculate_layout(...) end + end + local stop_propagation = overlays.process_event(name, ...) + if current_screen then if not stop_propagation then stop_propagation = current_screen:process_event(name, ...) end diff --git a/ui/keyboard_navigation.lua b/ui/keyboard_navigation.lua index 6049a12..248c77c 100644 --- a/ui/keyboard_navigation.lua +++ b/ui/keyboard_navigation.lua @@ -11,7 +11,10 @@ local function get_first_element(element) return element elseif element.elements then if element.last_selection then - return element.last_selection + local last_selected_elem = get_first_element(element.last_selection) + if last_selected_elem then + return last_selected_elem + end end for i = 1, #element.elements do local first_elem = get_first_element(element.elements[i]) @@ -32,9 +35,11 @@ function keyboard_navigation.set_screen(screen) current_screen.last_selected_element = selected_element end current_screen = screen - keyboard_navigation.select_element(nil, false) - if current_screen.last_selected_element then - keyboard_navigation.select_element(current_screen.last_selected_element, false) + if current_screen then + keyboard_navigation.select_element(nil, false) + if current_screen.last_selected_element then + keyboard_navigation.select_element(current_screen.last_selected_element, false) + end end end diff --git a/ui/overlays/init.lua b/ui/overlays/init.lua index cd2953f..593a090 100644 --- a/ui/overlays/init.lua +++ b/ui/overlays/init.lua @@ -16,6 +16,9 @@ function overlay_module.add_overlay(element) table.remove(free_overlay_indices, 1) end overlays[index] = element + -- require here to avoid circular import (now require just returns the cached table) + local ui = require("ui") + element:set_scale(ui.get_screen().scale) return index end @@ -35,6 +38,11 @@ end function overlay_module.process_event(name, ...) for i = 1, #overlays do if overlays[i] then + if name == "resize" then + -- require here to avoid circular import (now require just returns the cached table) + local ui = require("ui") + overlays[i]:set_scale(ui.get_screen().scale) + end if overlays[i]:process_event(name, ...) then return true end @@ -56,7 +64,7 @@ end ---@param dt number function overlay_module.update(dt) for i = 1, #overlays do - if overlays[i] then + if overlays[i] and overlays[i].update then overlays[i]:update(dt) end end diff --git a/ui/screens/game.lua b/ui/screens/game.lua index 929c3f7..7f2daa9 100644 --- a/ui/screens/game.lua +++ b/ui/screens/game.lua @@ -1,6 +1,9 @@ local label = require("ui.elements.label") local quad = require("ui.elements.quad") local game_handler = require("game_handler") +local get_death_overlay = require("ui.overlays.death") +local keyboard_navigation = require("ui.keyboard_navigation") +local overlays = require("ui.overlays") local timer = quad:new({ child_element = label:new("", { font_size = 64 }), @@ -8,6 +11,37 @@ local timer = quad:new({ vertex_offsets = { 0, 0, 40, 0, 0, 0, 0, 0 }, }) +local death_overlay_index + +local function back_to_menu() + if death_overlay_index then + keyboard_navigation.set_screen() + overlays.remove_overlay(death_overlay_index) + death_overlay_index = nil + end + game_handler.stop() + local ui = require("ui") + ui.open_screen("levelselect") +end + +local function retry() + if death_overlay_index then + keyboard_navigation.set_screen() + overlays.remove_overlay(death_overlay_index) + death_overlay_index = nil + end + game_handler.retry() +end + +local death_overlay = get_death_overlay(back_to_menu, retry) + +-- monkey patch layout calculation to add overlay (TODO: make this cleaner) +local old_calculate_layout = timer.calculate_layout +timer.calculate_layout = function(self, available_area) + death_overlay:calculate_layout(available_area) + return old_calculate_layout(self, available_area) +end + game_handler.onupdate = function() local score = game_handler.get_score() score = math.floor(score * 1000) / 1000 @@ -26,6 +60,16 @@ game_handler.onupdate = function() end timer.element.raw_text = score_str timer:calculate_layout() + -- show death screen when dead + if game_handler.is_dead() then + if game_handler.is_replaying() then + -- TODO: show appropriate ui when replay ends (retry/back buttons would be wrong here) + back_to_menu() + elseif keyboard_navigation.get_screen() ~= death_overlay then + death_overlay_index = overlays.add_overlay(death_overlay) + keyboard_navigation.set_screen(death_overlay) + end + end end return timer