From 27a5a4e5bfe4bf00c9afa006f0514ced0ce9e2fd Mon Sep 17 00:00:00 2001 From: makatymba2001 Date: Wed, 29 Apr 2026 02:38:59 +0300 Subject: [PATCH 01/14] tabs ui for rulesets --- lib/practice_mode.lua | 2 +- localization/en-us.lua | 3 + ui/lobby/_lobby_options/main_options.lua | 26 +-- .../play_button/ghost_replay_picker.lua | 2 +- .../play_button/play_button_callbacks.lua | 4 +- .../play_button/ruleset_selection.lua | 160 ++++++++++++++---- 6 files changed, 145 insertions(+), 52 deletions(-) diff --git a/lib/practice_mode.lua b/lib/practice_mode.lua index 0b950b5c..544f48e7 100644 --- a/lib/practice_mode.lua +++ b/lib/practice_mode.lua @@ -16,7 +16,7 @@ function G.FUNCS.setup_practice_mode(e) MP.GHOST.clear() G.FUNCS.overlay_menu({ - definition = G.UIDEF.ruleset_selection_options("practice"), + definition = G.UIDEF.ruleset_selection_tabs("practice"), }) end diff --git a/localization/en-us.lua b/localization/en-us.lua index 2ed5ade1..f842f05d 100644 --- a/localization/en-us.lua +++ b/localization/en-us.lua @@ -1331,6 +1331,9 @@ return { k_chaos_description = "Everything, everywhere, all at once.\n\nCombines Standard, Small World, Sandbox, and Speedlatro timer\ninto one ruleset. Good luck.", k_release = "Release Ver.", k_release_description = "Allan please add details", + k_mp_ruleset_tab_general = "General", + k_mp_ruleset_tab_torunaments = "Tournaments", + k_mp_ruleset_tab_experimental = "Experimental", k_cost_up = "Cost Up", k_destabilized = "Destabilized", k_oops_ex = "Oops!", diff --git a/ui/lobby/_lobby_options/main_options.lua b/ui/lobby/_lobby_options/main_options.lua index 07b63885..c9481fea 100644 --- a/ui/lobby/_lobby_options/main_options.lua +++ b/ui/lobby/_lobby_options/main_options.lua @@ -66,19 +66,19 @@ function MP.UI.Main_Lobby_Options(info_area_id, default_info_area, button_func, categories[#categories + 1] = MP.UI.BackgroundGrouping(localize(category.name), buttons) end - return create_UIBox_generic_options({ - back_func = "play_options", - contents = { - { n = G.UIT.C, config = { align = "tm", minh = 8, minw = 4, padding = 0.1 }, nodes = categories }, - { - n = G.UIT.C, - config = { align = "cm", minh = 8, maxh = 8, minw = 11 }, - nodes = { - { n = G.UIT.O, config = { id = info_area_id, object = default_info_area } }, - }, - }, - }, - }) + return { + n = G.UIT.ROOT, config = { colour = G.C.CLEAR }, + nodes = { + { n = G.UIT.C, config = { align = "tm", minh = 8, minw = 4, padding = 0.1 }, nodes = categories }, + { + n = G.UIT.C, + config = { align = "cm", minh = 8, maxh = 8, minw = 11 }, + nodes = { + { n = G.UIT.O, config = { id = info_area_id, object = default_info_area } }, + }, + }, + } + } end function MP.UI.Change_Main_Lobby_Options(e, info_area_id, info_area_func, default_button_id, update_lobby_config_func) diff --git a/ui/main_menu/play_button/ghost_replay_picker.lua b/ui/main_menu/play_button/ghost_replay_picker.lua index 19d5706e..2fd66a02 100644 --- a/ui/main_menu/play_button/ghost_replay_picker.lua +++ b/ui/main_menu/play_button/ghost_replay_picker.lua @@ -4,7 +4,7 @@ local function reopen_practice_menu() G.FUNCS.overlay_menu({ - definition = G.UIDEF.ruleset_selection_options("practice"), + definition = G.UIDEF.ruleset_selection_tabs("practice"), }) end diff --git a/ui/main_menu/play_button/play_button_callbacks.lua b/ui/main_menu/play_button/play_button_callbacks.lua index db319dc9..31003f36 100644 --- a/ui/main_menu/play_button/play_button_callbacks.lua +++ b/ui/main_menu/play_button/play_button_callbacks.lua @@ -7,7 +7,7 @@ function G.FUNCS.setup_run_singleplayer(e) MP.GHOST.clear() G.FUNCS.overlay_menu({ - definition = G.UIDEF.ruleset_selection_options("sp"), + definition = G.UIDEF.ruleset_selection_tabs("sp"), }) end @@ -37,7 +37,7 @@ function G.FUNCS.create_lobby(e) G.SETTINGS.paused = true G.FUNCS.overlay_menu({ - definition = G.UIDEF.ruleset_selection_options("mp"), + definition = G.UIDEF.ruleset_selection_tabs("mp"), }) end diff --git a/ui/main_menu/play_button/ruleset_selection.lua b/ui/main_menu/play_button/ruleset_selection.lua index e81aaefa..913e9350 100644 --- a/ui/main_menu/play_button/ruleset_selection.lua +++ b/ui/main_menu/play_button/ruleset_selection.lua @@ -1,21 +1,141 @@ -function G.UIDEF.ruleset_selection_options(mode) +local ruleset_buttons_data = { + { + name = "k_matchmaking", + buttons = { + { button_id = "standard_ranked_ruleset_button", button_localize_key = "k_standard_ranked" }, + { button_id = "legacy_ranked_ruleset_button", button_localize_key = "k_legacy_ranked" }, + { button_id = "smallworld_ruleset_button", button_localize_key = "k_smallworld" }, + { button_id = "sandbox_ruleset_button", button_localize_key = "k_sandbox" }, + }, + }, + { + name = "k_custom", + buttons = { + { button_id = "blitz_ruleset_button", button_localize_key = "k_blitz" }, + { button_id = "traditional_ruleset_button", button_localize_key = "k_traditional" }, + { button_id = "vanilla_ruleset_button", button_localize_key = "k_vanilla" }, + { button_id = "badlatro_ruleset_button", button_localize_key = "k_badlatro" }, + { button_id = "speedlatro_ruleset_button", button_localize_key = "k_speedlatro" }, + { button_id = "chaos_ruleset_button", button_localize_key = "k_chaos" }, + }, + }, + { + name = "k_tournament", + buttons = { + { button_id = "majorleague_ruleset_button", button_localize_key = "k_majorleague" }, + { button_id = "minorleague_ruleset_button", button_localize_key = "k_minorleague" }, + }, + }, +} + +local rulesets_tabs = { + default = { + { + name = "k_mp_ruleset_tab_general", + data = { + { + name = "k_matchmaking", + buttons = { + { button_id = "standard_ranked_ruleset_button", button_localize_key = "k_standard_ranked" }, + { button_id = "legacy_ranked_ruleset_button", button_localize_key = "k_legacy_ranked" }, + { button_id = "smallworld_ruleset_button", button_localize_key = "k_smallworld" }, + { button_id = "sandbox_ruleset_button", button_localize_key = "k_sandbox" }, + }, + }, + { + name = "k_custom", + buttons = { + { button_id = "blitz_ruleset_button", button_localize_key = "k_blitz" }, + { button_id = "traditional_ruleset_button", button_localize_key = "k_traditional" }, + { button_id = "vanilla_ruleset_button", button_localize_key = "k_vanilla" }, + + }, + }, + }, + }, + { + name = "k_mp_ruleset_tab_torunaments", + data = { + { + name = "k_tournament", + buttons = { + { button_id = "majorleague_ruleset_button", button_localize_key = "k_majorleague" }, + { button_id = "minorleague_ruleset_button", button_localize_key = "k_minorleague" }, + }, + }, + { + name = "k_custom", + buttons = { + { button_id = "speedlatro_ruleset_button", button_localize_key = "k_speedlatro" }, + { button_id = "badlatro_ruleset_button", button_localize_key = "k_badlatro" }, + { button_id = "chaos_ruleset_button", button_localize_key = "k_chaos" }, + }, + }, + } + }, + { + name = "k_mp_ruleset_tab_experimental", + data = { + { + name = "k_tournament", + buttons = { + { button_id = "badlatro_ruleset_button", button_localize_key = "k_badlatro" }, + { button_id = "chaos_ruleset_button", button_localize_key = "k_chaos" }, + }, + }, + } + } + }, +} + +function G.UIDEF.ruleset_selection_tabs(mode) + local tabs_schema = rulesets_tabs[mode] or rulesets_tabs.default + local tabs = {} + for _, item in ipairs(tabs_schema) do + table.insert(tabs, { + label = localize(item.name), + tab_definition_function = function() + return G.UIDEF.ruleset_selection_options(mode, item.data) + end, + }) + end + tabs[1].chosen = true + local t = create_UIBox_generic_options({ + back_func = "play_options", + contents = { + { + n = G.UIT.R, + config = { align = "cm", padding = 0 }, + nodes = { + create_tabs({ + tabs = tabs, + colour = G.C.BOOSTER, + }), + }, + }, + }, + }) + return t +end + +function G.UIDEF.ruleset_selection_options(mode, buttons) mode = mode or "mp" MP.LOBBY.fetched_weekly = "smallworld" -- temp -- If ghost is active, preserve the replay's ruleset instead of resetting to default local default_ruleset if mode == "practice" and MP.GHOST.is_active() and MP.SP.ruleset then - default_ruleset = MP.SP.ruleset:gsub("^ruleset_mp_", "") + default_ruleset = MP.SP.ruleset else - default_ruleset = "standard_ranked" + default_ruleset = string.match(buttons[1].buttons[1].button_id, "(.+)_%w+_button") end - local default_button = default_ruleset .. "_ruleset_button" if mode == "sp" or mode == "practice" then MP.SP.ruleset = "ruleset_mp_" .. default_ruleset else MP.LOBBY.config.ruleset = "ruleset_mp_" .. default_ruleset end + MP.LoadReworks(default_ruleset) local default_ruleset_area = UIBox({ @@ -23,43 +143,13 @@ function G.UIDEF.ruleset_selection_options(mode) config = { align = "cm" }, }) - local ruleset_buttons_data = { - { - name = "k_matchmaking", - buttons = { - { button_id = "standard_ranked_ruleset_button", button_localize_key = "k_standard_ranked" }, - { button_id = "legacy_ranked_ruleset_button", button_localize_key = "k_legacy_ranked" }, - { button_id = "smallworld_ruleset_button", button_localize_key = "k_smallworld" }, - { button_id = "sandbox_ruleset_button", button_localize_key = "k_sandbox" }, - }, - }, - { - name = "k_custom", - buttons = { - { button_id = "blitz_ruleset_button", button_localize_key = "k_blitz" }, - { button_id = "traditional_ruleset_button", button_localize_key = "k_traditional" }, - { button_id = "vanilla_ruleset_button", button_localize_key = "k_vanilla" }, - { button_id = "badlatro_ruleset_button", button_localize_key = "k_badlatro" }, - { button_id = "speedlatro_ruleset_button", button_localize_key = "k_speedlatro" }, - { button_id = "chaos_ruleset_button", button_localize_key = "k_chaos" }, - }, - }, - { - name = "k_tournament", - buttons = { - { button_id = "majorleague_ruleset_button", button_localize_key = "k_majorleague" }, - { button_id = "minorleague_ruleset_button", button_localize_key = "k_minorleague" }, - }, - }, - } - MP.UI.ruleset_selection_mode = mode return MP.UI.Main_Lobby_Options( "ruleset_area", default_ruleset_area, "change_ruleset_selection", - ruleset_buttons_data + buttons ) end From 59d031e80f11729d75fd545281a8c24a43372908 Mon Sep 17 00:00:00 2001 From: makatymba2001 Date: Thu, 30 Apr 2026 08:10:50 +0300 Subject: [PATCH 02/14] Rulesets adjustments --- layers/experimental.lua | 3 +++ layers/ranked.lua | 3 --- layers/smods_version_check.lua | 5 +++++ localization/en-us.lua | 8 +++++++ rulesets/blitz.lua | 2 +- rulesets/experimental.lua | 22 +++++++++++++++++-- rulesets/legacy_ranked.lua | 2 +- rulesets/ranked.lua | 2 +- rulesets/smallworld.lua | 2 +- rulesets/traditional.lua | 2 +- .../play_button/ruleset_selection.lua | 11 +++++++--- 11 files changed, 49 insertions(+), 13 deletions(-) create mode 100644 layers/smods_version_check.lua diff --git a/layers/experimental.lua b/layers/experimental.lua index 8ff2bca6..01ee37c0 100644 --- a/layers/experimental.lua +++ b/layers/experimental.lua @@ -33,4 +33,7 @@ MP.Layer("experimental", { "m_mp_display_glass", "m_mp_display_gold", }, + is_disabled = function() + return false + end, }) diff --git a/layers/ranked.lua b/layers/ranked.lua index 61d689f5..0dad4742 100644 --- a/layers/ranked.lua +++ b/layers/ranked.lua @@ -1,8 +1,5 @@ MP.Layer("ranked", { forced_lobby_options = true, - is_disabled = function(self) - return MP.UTILS.check_smods_version() or MP.UTILS.check_lovely_version() - end, force_lobby_options = function(self) MP.LOBBY.config.the_order = true return true diff --git a/layers/smods_version_check.lua b/layers/smods_version_check.lua new file mode 100644 index 00000000..73bb27a7 --- /dev/null +++ b/layers/smods_version_check.lua @@ -0,0 +1,5 @@ +MP.Layer("smods_version_check", { + is_disabled = function(self) + return MP.UTILS.check_smods_version() or MP.UTILS.check_lovely_version() + end, +}) diff --git a/localization/en-us.lua b/localization/en-us.lua index 99b53baa..cf1954b4 100644 --- a/localization/en-us.lua +++ b/localization/en-us.lua @@ -1314,6 +1314,14 @@ return { k_blitz_description = "The balanced Multiplayer ruleset.\n\nIncludes Multiplayer jokers and balance changes\nwith full control over your lobby settings.\n\n(See bans and reworks tabs for details)", k_experimental = "Experimental", k_experimental_description = "Standard's bleeding edge.\n\nHeavier balance changes being trialed\nfor a future Standard ruleset.\nExpect things to shift between versions.\n\n(See bans and reworks tabs for details)", + k_experimental_pressure = "Pressure timer + Balance", + k_experimental_pressure_description = "One of versions of Multiplayer 0.4.0 update.\n\nNew timer and balance changes included.\n\nLonger timer which ticks from the start\nof a game but stops during animations.\n240 seconds per ante, no additional time from skipping.\n\n(See bans and reworks tabs for new balance changes)", + k_experimental_no_animation = "No-Animation timer + Balance", + k_experimental_no_animation_description = "One of versions of Multiplayer 0.4.0 update.\n\nAlternative timer and balance changes included.\n\nSmaller timer which stops during animations.\n\n(See bans and reworks tabs for new balance changes)", + k_experimental_pressure_only = "Pressure timer", + k_experimental_pressure_only_description = "One of versions of Multiplayer 0.4.0 update.\n\nOnly new timer included.\n\nLonger timer which ticks from the start\nof a game but stops during animations.\n240 seconds per ante, no additional time from skipping.", + k_experimental_no_animation_only = "No-Animation timer", + k_experimental_no_animation_only_description = "One of versions of Multiplayer 0.4.0 update.\n\nOnly alternative timer included.\n\nSmaller timer which stops during animations.", k_traditional = "Traditional", k_traditional_description = "Multiplayer content without time pressure.\n\nIncludes Multiplayer jokers and balance changes,\nbut removes time-based mechanics for methodical play.\n\nTime-based jokers are banned.\nTimer is disabled.\n\n(See bans and reworks tabs for details)", k_majorleague = "Major League", diff --git a/rulesets/blitz.lua b/rulesets/blitz.lua index af534846..59974bc3 100644 --- a/rulesets/blitz.lua +++ b/rulesets/blitz.lua @@ -1,4 +1,4 @@ MP.Ruleset({ key = "blitz", - layers = { "standard", "pressure_timer" }, + layers = { "standard" }, }):inject() diff --git a/rulesets/experimental.lua b/rulesets/experimental.lua index c9e08606..01ce52ff 100644 --- a/rulesets/experimental.lua +++ b/rulesets/experimental.lua @@ -1,5 +1,23 @@ MP.Ruleset({ - key = "experimental", - layers = { "experimental", "ranked", "pressure_timer" }, + key = "experimental_pressure", + layers = { "ranked", "experimental", "pressure_timer" }, + forced_gamemode = "gamemode_mp_attrition", +}):inject() + +MP.Ruleset({ + key = "experimental_no_animation", + layers = { "ranked", "experimental", "no_animation_timer" }, + forced_gamemode = "gamemode_mp_attrition", +}):inject() + +MP.Ruleset({ + key = "experimental_pressure_only", + layers = { "standard", "ranked", "pressure_timer" }, + forced_gamemode = "gamemode_mp_attrition", +}):inject() + +MP.Ruleset({ + key = "experimental_no_animation_only", + layers = { "standard", "ranked", "no_animation_timer" }, forced_gamemode = "gamemode_mp_attrition", }):inject() diff --git a/rulesets/legacy_ranked.lua b/rulesets/legacy_ranked.lua index 6c4bd2ab..9993d9e4 100644 --- a/rulesets/legacy_ranked.lua +++ b/rulesets/legacy_ranked.lua @@ -1,5 +1,5 @@ MP.Ruleset({ key = "legacy_ranked", - layers = { "classic", "ranked" }, + layers = { "classic", "ranked", "smods_version_check" }, forced_gamemode = "gamemode_mp_attrition", }):inject() diff --git a/rulesets/ranked.lua b/rulesets/ranked.lua index 750a0e3f..200776ce 100644 --- a/rulesets/ranked.lua +++ b/rulesets/ranked.lua @@ -1,5 +1,5 @@ MP.Ruleset({ key = "standard_ranked", - layers = { "standard", "ranked", "no_animation_timer" }, + layers = { "standard", "ranked", "smods_version_check" }, forced_gamemode = "gamemode_mp_attrition", }):inject() diff --git a/rulesets/smallworld.lua b/rulesets/smallworld.lua index c911a9c0..c5f660bf 100644 --- a/rulesets/smallworld.lua +++ b/rulesets/smallworld.lua @@ -1,4 +1,4 @@ MP.Ruleset({ key = "smallworld", - layers = { "standard", "smallworld", "pressure_timer" }, + layers = { "standard", "smallworld" }, }):inject() diff --git a/rulesets/traditional.lua b/rulesets/traditional.lua index a25eac72..cd6d4e5b 100644 --- a/rulesets/traditional.lua +++ b/rulesets/traditional.lua @@ -1,6 +1,6 @@ MP.Ruleset({ key = "traditional", - layers = { "standard", "pressure_timer" }, + layers = { "standard" }, banned_jokers = { "j_mp_speedrun", "j_mp_conjoined_joker", diff --git a/ui/main_menu/play_button/ruleset_selection.lua b/ui/main_menu/play_button/ruleset_selection.lua index f1374e6c..962d69e0 100644 --- a/ui/main_menu/play_button/ruleset_selection.lua +++ b/ui/main_menu/play_button/ruleset_selection.lua @@ -79,7 +79,10 @@ local rulesets_tabs = { { name = "k_experimental", buttons = { - { button_id = "experimental_ruleset_button", button_localize_key = "k_experimental" }, + { button_id = "experimental_pressure_ruleset_button", button_localize_key = "k_experimental_pressure" }, + { button_id = "experimental_no_animation_ruleset_button", button_localize_key = "k_experimental_no_animation" }, + { button_id = "experimental_pressure_only_ruleset_button", button_localize_key = "k_experimental_pressure_only" }, + { button_id = "experimental_no_animation_only_ruleset_button", button_localize_key = "k_experimental_no_animation_only" }, }, }, } @@ -126,8 +129,9 @@ function G.UIDEF.ruleset_selection_options(mode, buttons) if mode == "practice" and MP.GHOST.is_active() and MP.SP.ruleset then default_ruleset = MP.SP.ruleset else - default_ruleset = string.match(buttons[1].buttons[1].button_id, "(.+)_%w+_button") + default_ruleset = string.match(buttons[1].buttons[1].button_id, "(.+)_ruleset_button$") end + print(default_ruleset) if mode == "sp" or mode == "practice" then MP.SP.ruleset = "ruleset_mp_" .. default_ruleset @@ -136,13 +140,14 @@ function G.UIDEF.ruleset_selection_options(mode, buttons) end MP.LoadReworks(default_ruleset) + MP.UI.ruleset_selection_mode = mode local default_ruleset_area = UIBox({ definition = G.UIDEF.ruleset_info(default_ruleset, mode), config = { align = "cm" }, }) - MP.UI.ruleset_selection_mode = mode + return MP.UI.Main_Lobby_Options( "ruleset_area", From f3e45f8b4bb34e8431518d06d7d7f237839ab9b6 Mon Sep 17 00:00:00 2001 From: makatymba2001 Date: Thu, 30 Apr 2026 16:56:54 +0300 Subject: [PATCH 03/14] Fix lobby buttons appear when they shouldn't --- lovely/pause.toml | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/lovely/pause.toml b/lovely/pause.toml index 84cb4960..1cd1704f 100644 --- a/lovely/pause.toml +++ b/lovely/pause.toml @@ -3,6 +3,19 @@ version = "1.0.0" dump_lua = true priority = 2147483600 +[[patches]] +[patches.pattern] +target = 'functions/UI_definitions.lua' +pattern = '''local customize = nil''' +position = 'after' +payload = ''' +local unstuck_button = nil +local return_to_lobby = nil +local leave_lobby = nil +''' +match_indent = true +times = 1 + [[patches]] [patches.pattern] target = 'functions/UI_definitions.lua' From 134be963bdec927ec47d0bfcea25e84fcba61cb6 Mon Sep 17 00:00:00 2001 From: makatymba2001 Date: Thu, 30 Apr 2026 17:02:17 +0300 Subject: [PATCH 04/14] Fix various visual issues; Balance no-animation timer --- layers/no_anim_timer.lua | 1 + layers/speedlatro_timer.lua | 4 ++-- ui/game/timer.lua | 11 ++++++++++- ui/main_menu/play_button/ruleset_selection.lua | 7 ++----- 4 files changed, 15 insertions(+), 8 deletions(-) diff --git a/layers/no_anim_timer.lua b/layers/no_anim_timer.lua index 5846ce1e..a03d2d55 100644 --- a/layers/no_anim_timer.lua +++ b/layers/no_anim_timer.lua @@ -1,4 +1,5 @@ MP.Layer("no_animation_timer", { preview_calculate_delay = 1.5, preview_calculate_cost = 3.5, + timer_base_multiplier = 4/5, }) diff --git a/layers/speedlatro_timer.lua b/layers/speedlatro_timer.lua index 93e78152..de730bec 100644 --- a/layers/speedlatro_timer.lua +++ b/layers/speedlatro_timer.lua @@ -2,8 +2,8 @@ -- i can't be bothered to do run_start hooks and risk that being janky so it'll be initialized in gupdate MP.Layer("speedlatro_timer", { - preview_calculate_delay = 0, - preview_calculate_cost = 5, + preview_calculate_delay = 5, + preview_calculate_cost = 0, timer_speedup_multiplier = 2, }) diff --git a/ui/game/timer.lua b/ui/game/timer.lua index 557c740e..011d298c 100644 --- a/ui/game/timer.lua +++ b/ui/game/timer.lua @@ -235,6 +235,10 @@ function G.FUNCS.set_timer_box(e) end end +local animation_budget_capacity = 40 +local animation_budget_restore_rate = 2.5 +local animation_budget_decay_rate = 1 + local gameUpdateRef = Game.update ---@diagnostic disable-next-line: duplicate-set-field function Game:update(dt) @@ -253,6 +257,7 @@ function Game:update(dt) local new_time = love.timer.getTime() local timer_dt = new_time - (MP.TIMER_CLOCK or new_time) MP.TIMER_CLOCK = new_time + MP.TIMER_ANIMATION_BUDGET = math.min(animation_budget_capacity, (MP.TIMER_ANIMATION_BUDGET or animation_budget_capacity) + timer_dt * animation_budget_restore_rate) -- Bail fast: not an MP PvP-timer context if G.STATE == G.STATES.GAME_OVER then return end @@ -277,7 +282,11 @@ function Game:update(dt) -- Don't tick during animations, unless the user is paused or has a menu open local interactive = not (G.CONTROLLER.locked or (G.GAME.STOP_USE or 0) > 0) local menu_or_paused = G.SETTINGS.paused or G.OVERLAY_MENU - if not (interactive or menu_or_paused) then return end + + if not (interactive or menu_or_paused) then + MP.TIMER_ANIMATION_BUDGET = math.max(0, MP.TIMER_ANIMATION_BUDGET - timer_dt * (animation_budget_restore_rate + animation_budget_decay_rate)) + if MP.TIMER_ANIMATION_BUDGET > 0 then return end + end else if not (MP.GAME.timer_started or MP.GAME.nemesis_timer_started) then return end end diff --git a/ui/main_menu/play_button/ruleset_selection.lua b/ui/main_menu/play_button/ruleset_selection.lua index 962d69e0..6648ae89 100644 --- a/ui/main_menu/play_button/ruleset_selection.lua +++ b/ui/main_menu/play_button/ruleset_selection.lua @@ -131,7 +131,6 @@ function G.UIDEF.ruleset_selection_options(mode, buttons) else default_ruleset = string.match(buttons[1].buttons[1].button_id, "(.+)_ruleset_button$") end - print(default_ruleset) if mode == "sp" or mode == "practice" then MP.SP.ruleset = "ruleset_mp_" .. default_ruleset @@ -141,14 +140,12 @@ function G.UIDEF.ruleset_selection_options(mode, buttons) MP.LoadReworks(default_ruleset) MP.UI.ruleset_selection_mode = mode + MP.UI.ruleset_selection_default_button = default_ruleset .. "_ruleset_button" local default_ruleset_area = UIBox({ definition = G.UIDEF.ruleset_info(default_ruleset, mode), config = { align = "cm" }, }) - - - return MP.UI.Main_Lobby_Options( "ruleset_area", default_ruleset_area, @@ -166,7 +163,7 @@ function G.FUNCS.change_ruleset_selection(e) -- this currently doesn't work properly -- local default_button = mode == "sp" and "vanilla_ruleset_button" or "standard_ranked_ruleset_button" - local default_button = "standard_ranked_ruleset_button" + local default_button = MP.UI.ruleset_selection_default_button or "standard_ranked_ruleset_button" MP.UI.Change_Main_Lobby_Options( e, From 8cfc90c77d03261c8c68899962418a30ca9bd6e2 Mon Sep 17 00:00:00 2001 From: makatymba2001 Date: Thu, 30 Apr 2026 17:12:44 +0300 Subject: [PATCH 05/14] Add time on skip for opponent immediately --- networking/action_handlers.lua | 8 ++++++++ ui/game/functions.lua | 2 +- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/networking/action_handlers.lua b/networking/action_handlers.lua index 2f1c3553..fd42e060 100644 --- a/networking/action_handlers.lua +++ b/networking/action_handlers.lua @@ -275,6 +275,14 @@ local function action_enemy_info(score_str, hands_left_str, skips_str, lives_str if MP.GAME.enemy.skips ~= skips then for i = 1, skips - MP.GAME.enemy.skips do MP.GAME.enemy.spent_in_shop[#MP.GAME.enemy.spent_in_shop + 1] = 0 + if + MP.GAME.enemy.skips < skips + and MP.is_layer_active("no_animation_timer") + and not MP.GAME.timer_started + and (MP.LOBBY.config.timer_increment_seconds or 0) > 0 + then + MP.GAME.timer = MP.GAME.timer + MP.LOBBY.config.timer_increment_seconds + end end end diff --git a/ui/game/functions.lua b/ui/game/functions.lua index 56a8abad..5793e4d2 100644 --- a/ui/game/functions.lua +++ b/ui/game/functions.lua @@ -75,7 +75,7 @@ G.FUNCS.skip_blind = function(e) skip_blind_ref(e) if MP.LOBBY.code then -- pressure_timer applies pressure throughout the round, so skipping must not buy time. - if not MP.is_layer_active("pressure_timer") + if not (MP.is_layer_active("pressure_timer")) and not MP.GAME.timer_started and (MP.LOBBY.config.timer_increment_seconds or 0) > 0 then MP.GAME.timer = MP.GAME.timer + MP.LOBBY.config.timer_increment_seconds From ab374789f3fd3f94ac3a83e28022f600be108bb4 Mon Sep 17 00:00:00 2001 From: makatymba2001 Date: Thu, 30 Apr 2026 17:18:11 +0300 Subject: [PATCH 06/14] text adjustments --- localization/en-us.lua | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/localization/en-us.lua b/localization/en-us.lua index cf1954b4..775be393 100644 --- a/localization/en-us.lua +++ b/localization/en-us.lua @@ -1315,13 +1315,13 @@ return { k_experimental = "Experimental", k_experimental_description = "Standard's bleeding edge.\n\nHeavier balance changes being trialed\nfor a future Standard ruleset.\nExpect things to shift between versions.\n\n(See bans and reworks tabs for details)", k_experimental_pressure = "Pressure timer + Balance", - k_experimental_pressure_description = "One of versions of Multiplayer 0.4.0 update.\n\nNew timer and balance changes included.\n\nLonger timer which ticks from the start\nof a game but stops during animations.\n240 seconds per ante, no additional time from skipping.\n\n(See bans and reworks tabs for new balance changes)", + k_experimental_pressure_description = "One of versions of Multiplayer 0.4.0 update.\n\nNew timer and balance changes included.\n\nLonger timer which ticks from the start but stops during animations.\n250 seconds per ante, no additional time from skipping.\n\n(See bans and reworks tabs for new balance changes)", k_experimental_no_animation = "No-Animation timer + Balance", - k_experimental_no_animation_description = "One of versions of Multiplayer 0.4.0 update.\n\nAlternative timer and balance changes included.\n\nSmaller timer which stops during animations.\n\n(See bans and reworks tabs for new balance changes)", + k_experimental_no_animation_description = "One of versions of Multiplayer 0.4.0 update.\n\nAlternative timer and balance changes included.\n\nSmaller timer which stops during animations.\n120 seconds per ante.\n\n(See bans and reworks tabs for new balance changes)", k_experimental_pressure_only = "Pressure timer", - k_experimental_pressure_only_description = "One of versions of Multiplayer 0.4.0 update.\n\nOnly new timer included.\n\nLonger timer which ticks from the start\nof a game but stops during animations.\n240 seconds per ante, no additional time from skipping.", + k_experimental_pressure_only_description = "One of versions of Multiplayer 0.4.0 update.\n\nOnly new timer included.\n\nLonger timer which ticks from the start but stops during animations.\n250 seconds per ante, no additional time from skipping.", k_experimental_no_animation_only = "No-Animation timer", - k_experimental_no_animation_only_description = "One of versions of Multiplayer 0.4.0 update.\n\nOnly alternative timer included.\n\nSmaller timer which stops during animations.", + k_experimental_no_animation_only_description = "One of versions of Multiplayer 0.4.0 update.\n\nOnly alternative timer included.\n\nSmaller timer which stops during animations.\n120 seconds per ante.", k_traditional = "Traditional", k_traditional_description = "Multiplayer content without time pressure.\n\nIncludes Multiplayer jokers and balance changes,\nbut removes time-based mechanics for methodical play.\n\nTime-based jokers are banned.\nTimer is disabled.\n\n(See bans and reworks tabs for details)", k_majorleague = "Major League", From 1eb817dca677fba16019086762752ffa043762ce Mon Sep 17 00:00:00 2001 From: makatymba2001 Date: Thu, 30 Apr 2026 17:56:18 +0300 Subject: [PATCH 07/14] remove edition before applying new one (crash preventing) --- ui/main_menu/title_card.lua | 1 + 1 file changed, 1 insertion(+) diff --git a/ui/main_menu/title_card.lua b/ui/main_menu/title_card.lua index fadc2255..c7912bb2 100644 --- a/ui/main_menu/title_card.lua +++ b/ui/main_menu/title_card.lua @@ -47,6 +47,7 @@ local function wheel_of_fortune_the_card(card) { name = "e_negative", weight = 1 }, } local edition = poll_edition("main_menu" .. os.time(), nil, nil, true, editions) + card:set_edition(nil, true, true) card:set_edition(edition, true) Juice_up(card, 0.3, 0.5) G.CONTROLLER.locks.edition = false -- if this isn't done, set_edition will block inputs for 0.1s From e73284da6b842593b8caebe3c6ed90c97d4c08c4 Mon Sep 17 00:00:00 2001 From: makatymba2001 Date: Thu, 30 Apr 2026 21:20:42 +0300 Subject: [PATCH 08/14] Fix not proper gamemode UI display --- .../play_button/gamemode_selection.lua | 28 +++++++++++++++++++ .../play_button/play_button_callbacks.lua | 2 +- 2 files changed, 29 insertions(+), 1 deletion(-) diff --git a/ui/main_menu/play_button/gamemode_selection.lua b/ui/main_menu/play_button/gamemode_selection.lua index 502a265f..f05c09bc 100644 --- a/ui/main_menu/play_button/gamemode_selection.lua +++ b/ui/main_menu/play_button/gamemode_selection.lua @@ -30,6 +30,34 @@ function G.UIDEF.gamemode_selection_options() ) end +function G.UIDEF.gamemode_selection_tabs() + local tabs = { + { + label = localize("k_gamemodes"), + tab_definition_function = function() + return G.UIDEF.gamemode_selection_options() + end, + chosen = true, + } + } + local t = create_UIBox_generic_options({ + back_func = "create_lobby", + contents = { + { + n = G.UIT.R, + config = { align = "cm", padding = 0 }, + nodes = { + create_tabs({ + tabs = tabs, + colour = G.C.BOOSTER, + }), + }, + }, + }, + }) + return t +end + function G.FUNCS.change_gamemode_selection(e) MP.UI.Change_Main_Lobby_Options( e, diff --git a/ui/main_menu/play_button/play_button_callbacks.lua b/ui/main_menu/play_button/play_button_callbacks.lua index 31003f36..9d7dd3b0 100644 --- a/ui/main_menu/play_button/play_button_callbacks.lua +++ b/ui/main_menu/play_button/play_button_callbacks.lua @@ -45,7 +45,7 @@ function G.FUNCS.select_gamemode(e) G.SETTINGS.paused = true G.FUNCS.overlay_menu({ - definition = G.UIDEF.gamemode_selection_options(), + definition = G.UIDEF.gamemode_selection_tabs(), }) end From d2e33861dce1e236ba7627418e16304e86e32eee Mon Sep 17 00:00:00 2001 From: makatymba2001 Date: Thu, 30 Apr 2026 22:58:49 +0300 Subject: [PATCH 09/14] Fix blind name size scale --- ui/game/blind_hud.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ui/game/blind_hud.lua b/ui/game/blind_hud.lua index 24a43155..136da888 100644 --- a/ui/game/blind_hud.lua +++ b/ui/game/blind_hud.lua @@ -26,7 +26,7 @@ function MP.UI.update_blind_HUD(blind, reset, silent) name_element.config.object.scale = name_element.config.object.config.scale name_element.config.object:update_text(true) if name_element.config.object.config.maxw then - name_element.config.object.scale = name_element.config.object.scale * (name_element.config.object.config.maxw/name_element.config.object.config.W) + name_element.config.object.scale = name_element.config.object.scale * math.min(1, name_element.config.object.config.maxw/name_element.config.object.config.W) end name_element.config.object:update_text(true) name_element.states.visible = false @@ -91,7 +91,7 @@ function MP.UI.reset_blind_HUD() name_element.config.object.scale = name_element.config.object.config.scale name_element.config.object:update_text(true) if name_element.config.object.config.maxw then - name_element.config.object.scale = name_element.config.object.scale * (name_element.config.object.config.maxw/name_element.config.object.config.W) + name_element.config.object.scale = name_element.config.object.scale * math.min(1, name_element.config.object.config.maxw/name_element.config.object.config.W) end name_element.config.object:update_text(true) end From f9cd8c08ea10b924dcfcdde2827fb25ba41496d9 Mon Sep 17 00:00:00 2001 From: makatymba2001 Date: Fri, 1 May 2026 00:42:07 +0300 Subject: [PATCH 10/14] timer is 300 now, +50 seconds --- layers/pressure_timer.lua | 2 +- localization/en-us.lua | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/layers/pressure_timer.lua b/layers/pressure_timer.lua index 067f270a..c366809d 100644 --- a/layers/pressure_timer.lua +++ b/layers/pressure_timer.lua @@ -6,5 +6,5 @@ MP.Layer("pressure_timer", { preview_calculate_delay = 1.5, preview_calculate_cost = 3.5, timer_speedup_multiplier = 2, - timer_base_multiplier = 5 / 3, + timer_base_multiplier = 2, }) diff --git a/localization/en-us.lua b/localization/en-us.lua index 775be393..3ce36840 100644 --- a/localization/en-us.lua +++ b/localization/en-us.lua @@ -1315,11 +1315,11 @@ return { k_experimental = "Experimental", k_experimental_description = "Standard's bleeding edge.\n\nHeavier balance changes being trialed\nfor a future Standard ruleset.\nExpect things to shift between versions.\n\n(See bans and reworks tabs for details)", k_experimental_pressure = "Pressure timer + Balance", - k_experimental_pressure_description = "One of versions of Multiplayer 0.4.0 update.\n\nNew timer and balance changes included.\n\nLonger timer which ticks from the start but stops during animations.\n250 seconds per ante, no additional time from skipping.\n\n(See bans and reworks tabs for new balance changes)", + k_experimental_pressure_description = "One of versions of Multiplayer 0.4.0 update.\n\nNew timer and balance changes included.\n\nLonger timer which ticks from the start but stops during animations.\n300 seconds per ante, no additional time from skipping.\n\n(See bans and reworks tabs for new balance changes)", k_experimental_no_animation = "No-Animation timer + Balance", k_experimental_no_animation_description = "One of versions of Multiplayer 0.4.0 update.\n\nAlternative timer and balance changes included.\n\nSmaller timer which stops during animations.\n120 seconds per ante.\n\n(See bans and reworks tabs for new balance changes)", k_experimental_pressure_only = "Pressure timer", - k_experimental_pressure_only_description = "One of versions of Multiplayer 0.4.0 update.\n\nOnly new timer included.\n\nLonger timer which ticks from the start but stops during animations.\n250 seconds per ante, no additional time from skipping.", + k_experimental_pressure_only_description = "One of versions of Multiplayer 0.4.0 update.\n\nOnly new timer included.\n\nLonger timer which ticks from the start but stops during animations.\n300 seconds per ante, no additional time from skipping.", k_experimental_no_animation_only = "No-Animation timer", k_experimental_no_animation_only_description = "One of versions of Multiplayer 0.4.0 update.\n\nOnly alternative timer included.\n\nSmaller timer which stops during animations.\n120 seconds per ante.", k_traditional = "Traditional", From f62227a50e4b57584c72c15f91b9422ccbbc952b Mon Sep 17 00:00:00 2001 From: makatymba2001 Date: Fri, 1 May 2026 00:48:38 +0300 Subject: [PATCH 11/14] Make glass 1.5x bro --- objects/enhancements/mp_glass.lua | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/objects/enhancements/mp_glass.lua b/objects/enhancements/mp_glass.lua index 052be211..8a80a419 100644 --- a/objects/enhancements/mp_glass.lua +++ b/objects/enhancements/mp_glass.lua @@ -13,6 +13,16 @@ MP.ReworkCenter("m_glass", { config = { Xmult = 1.5, extra = 4 }, }) +MP.ReworkCenter("m_glass", { + layers = "experimental", + config = { Xmult = 1.5, extra = 4 }, +}) + +MP.ReworkCenter("m_glass", { + layers = "ranked", + config = { Xmult = 1.5, extra = 4 }, +}) + -- This is a glass that is permanently at X1.5 to be shown in ruleset descriptions -- (Because glass will show at X2 in rulesets otherwise) SMODS.Enhancement({ From aad9d46e0235db54079ca9fbe5ca041f96d64a24 Mon Sep 17 00:00:00 2001 From: makatymba2001 Date: Fri, 1 May 2026 01:14:49 +0300 Subject: [PATCH 12/14] Fix gold cards & ticket --- objects/enhancements/mp_gold.lua | 2 +- objects/jokers/standard/ticket.lua | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/objects/enhancements/mp_gold.lua b/objects/enhancements/mp_gold.lua index fba6a0c0..b52933d8 100644 --- a/objects/enhancements/mp_gold.lua +++ b/objects/enhancements/mp_gold.lua @@ -1,5 +1,5 @@ MP.ReworkCenter("m_gold", { - layers = "standard", + layers = "experimental", config = { h_dollars = 4, mp_balanced = true }, }) diff --git a/objects/jokers/standard/ticket.lua b/objects/jokers/standard/ticket.lua index 1479e0ac..3445afa4 100644 --- a/objects/jokers/standard/ticket.lua +++ b/objects/jokers/standard/ticket.lua @@ -33,3 +33,9 @@ SMODS.Joker({ end end, }) + +MP.ReworkCenter("j_mp_ticket", { + layers = "experimental", + config = { extra = { dollars = 4 }, mp_balanced = true }, + rarity = 2, +}) \ No newline at end of file From 7916668c402ae541a213557208415ab03bd58e5a Mon Sep 17 00:00:00 2001 From: makatymba2001 Date: Fri, 1 May 2026 20:34:37 +0300 Subject: [PATCH 13/14] force game speed when no-animation part comes into play --- lovely/timer.toml | 17 +++++++++++++++++ ui/game/timer.lua | 7 ++++++- 2 files changed, 23 insertions(+), 1 deletion(-) create mode 100644 lovely/timer.toml diff --git a/lovely/timer.toml b/lovely/timer.toml new file mode 100644 index 00000000..decb0181 --- /dev/null +++ b/lovely/timer.toml @@ -0,0 +1,17 @@ +[manifest] +version = "1.0.0" +dump_lua = true +priority = 2147483600 + +# Force gamespeed to vanilla 4x when we need to +[[patches]] +[patches.regex] +target = 'game.lua' +pattern = 'self.TIMERS.TOTAL = self.TIMERS.TOTAL + dt*(self.SPEEDFACTOR)' +position = 'before' +payload = ''' +if MP.TIMER_FORCE_GAMESPEED and (G.STAGE == G.STAGES.RUN and not G.SETTINGS.paused and not G.screenwipe) then + self.SPEEDFACTOR = math.max(self.SPEEDFACTOR, 4 + math.max(0, math.abs(G.ACC) - 2)) +end +''' +times = 1 \ No newline at end of file diff --git a/ui/game/timer.lua b/ui/game/timer.lua index 011d298c..a36138e7 100644 --- a/ui/game/timer.lua +++ b/ui/game/timer.lua @@ -258,6 +258,7 @@ function Game:update(dt) local timer_dt = new_time - (MP.TIMER_CLOCK or new_time) MP.TIMER_CLOCK = new_time MP.TIMER_ANIMATION_BUDGET = math.min(animation_budget_capacity, (MP.TIMER_ANIMATION_BUDGET or animation_budget_capacity) + timer_dt * animation_budget_restore_rate) + MP.TIMER_FORCE_GAMESPEED = false -- Bail fast: not an MP PvP-timer context if G.STATE == G.STATES.GAME_OVER then return end @@ -269,12 +270,16 @@ function Game:update(dt) local is_no_animation_timer = MP.is_layer_active("no_animation_timer") + local is_pressure_timer = MP.is_layer_active("pressure_timer") -- Tick gating differs by layer: -- pressure_timer ON -> tick during regular play (not ready_blind, not pvp boss) -- pressure_timer OFF -> tick whenever someone pressed a timer button. -- timer_started = YOU pressed it; nemesis_timer_started = OPPONENT pressed it -- (i.e. they're timering you). Either way your local timer should tick. - if MP.is_layer_active("pressure_timer") or is_no_animation_timer then + if is_pressure_timer or is_no_animation_timer then + if is_pressure_timer or (is_no_animation_timer and MP.GAME.nemesis_timer_started) then + MP.TIMER_FORCE_GAMESPEED = true + end if MP.GAME.ready_blind or MP.is_pvp_boss() then return end -- Tick when "unready" blind or old timer, and opponent "timering" you if (MP.GAME.pvp_reached or is_no_animation_timer) and not MP.GAME.nemesis_timer_started then return end From 2897924bbd03769028a78be07fb8ddd3839402bc Mon Sep 17 00:00:00 2001 From: makatymba2001 Date: Sat, 2 May 2026 00:42:49 +0300 Subject: [PATCH 14/14] proper patch --- lovely/timer.toml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lovely/timer.toml b/lovely/timer.toml index decb0181..8960b247 100644 --- a/lovely/timer.toml +++ b/lovely/timer.toml @@ -5,7 +5,7 @@ priority = 2147483600 # Force gamespeed to vanilla 4x when we need to [[patches]] -[patches.regex] +[patches.pattern] target = 'game.lua' pattern = 'self.TIMERS.TOTAL = self.TIMERS.TOTAL + dt*(self.SPEEDFACTOR)' position = 'before' @@ -14,4 +14,5 @@ if MP.TIMER_FORCE_GAMESPEED and (G.STAGE == G.STAGES.RUN and not G.SETTINGS.paus self.SPEEDFACTOR = math.max(self.SPEEDFACTOR, 4 + math.max(0, math.abs(G.ACC) - 2)) end ''' -times = 1 \ No newline at end of file +times = 1 +match_indent = true