diff --git a/lovely/stake.toml b/lovely/stake.toml index e3016925b..a5dc579d6 100644 --- a/lovely/stake.toml +++ b/lovely/stake.toml @@ -132,7 +132,9 @@ match_indent = true target = "functions/misc_functions.lua" pattern = 'G.PROFILES[G.SETTINGS.profile].joker_usage[v.config.center_key] = {count = 1, order = v.config.center.order, wins = {}, losses = {}}' position = "at" -payload = 'G.PROFILES[G.SETTINGS.profile].joker_usage[v.config.center_key] = {count = 1, order = v.config.center.order, wins = {}, losses = {}, wins_by_key = {}, losses_by_key = {}}' +payload = ''' +G.PROFILES[G.SETTINGS.profile].joker_usage[v.config.center_key] = convert_usage_entry{count = 1, order = v.config.center.order, wins = {}, losses = {}, wins_by_key = {}, losses_by_key = {}} +''' match_indent = true # set_joker_win @@ -141,7 +143,7 @@ match_indent = true target = "functions/misc_functions.lua" pattern = 'G.PROFILES[G.SETTINGS.profile].joker_usage[v.config.center_key] = G.PROFILES[G.SETTINGS.profile].joker_usage[v.config.center_key] or {count = 1, order = v.config.center.order, wins = {}, losses = {}}' position = "at" -payload = 'G.PROFILES[G.SETTINGS.profile].joker_usage[v.config.center_key] = G.PROFILES[G.SETTINGS.profile].joker_usage[v.config.center_key] or {count = 1, order = v.config.center.order, wins = {}, losses = {}, wins_by_key = {}, losses_by_key = {}}' +payload = 'G.PROFILES[G.SETTINGS.profile].joker_usage[v.config.center_key] = G.PROFILES[G.SETTINGS.profile].joker_usage[v.config.center_key] or convert_usage_entry{count = 1, order = v.config.center.order, wins = {}, losses = {}, wins_by_key = {}, losses_by_key = {}}' match_indent = true #set_joker_win @@ -149,7 +151,7 @@ match_indent = true [patches.pattern] target = "functions/misc_functions.lua" pattern = 'G.PROFILES[G.SETTINGS.profile].joker_usage[v.config.center_key].wins[G.GAME.stake] = (G.PROFILES[G.SETTINGS.profile].joker_usage[v.config.center_key].wins[G.GAME.stake] or 0) + 1' -position = "after" +position = "at" payload = 'G.PROFILES[G.SETTINGS.profile].joker_usage[v.config.center_key].wins_by_key[SMODS.stake_from_index(G.GAME.stake)] = (G.PROFILES[G.SETTINGS.profile].joker_usage[v.config.center_key].wins_by_key[SMODS.stake_from_index(G.GAME.stake)] or 0) + 1' match_indent = true @@ -158,7 +160,7 @@ match_indent = true [patches.pattern] target = "functions/misc_functions.lua" pattern = 'G.PROFILES[G.SETTINGS.profile].joker_usage[v.config.center_key].losses[G.GAME.stake] = (G.PROFILES[G.SETTINGS.profile].joker_usage[v.config.center_key].losses[G.GAME.stake] or 0) + 1' -position = "after" +position = "at" payload = 'G.PROFILES[G.SETTINGS.profile].joker_usage[v.config.center_key].losses_by_key[SMODS.stake_from_index(G.GAME.stake)] = (G.PROFILES[G.SETTINGS.profile].joker_usage[v.config.center_key].losses_by_key[SMODS.stake_from_index(G.GAME.stake)] or 0) + 1' match_indent = true @@ -177,7 +179,7 @@ match_indent = true target = "functions/misc_functions.lua" pattern = 'if not G.PROFILES[G.SETTINGS.profile].deck_usage[deck_key] then G.PROFILES[G.SETTINGS.profile].deck_usage[deck_key] = {count = 1, order = G.GAME.selected_back.effect.center.order, wins = {}, losses = {}} end' position = "at" -payload = 'if not G.PROFILES[G.SETTINGS.profile].deck_usage[deck_key] then G.PROFILES[G.SETTINGS.profile].deck_usage[deck_key] = {count = 1, order = G.GAME.selected_back.effect.center.order, wins = {}, losses = {}, wins_by_key = {}, losses_by_key = {}} end' +payload = 'if not G.PROFILES[G.SETTINGS.profile].deck_usage[deck_key] then G.PROFILES[G.SETTINGS.profile].deck_usage[deck_key] = convert_usage_entry{count = 1, order = G.GAME.selected_back.effect.center.order, wins = {}, losses = {}, wins_by_key = {}, losses_by_key = {}} end' match_indent = true # G.UIDEF.viewed_stake_option diff --git a/lsp_def/classes/stake.lua b/lsp_def/classes/stake.lua index d937445c8..a49eced6e 100644 --- a/lsp_def/classes/stake.lua +++ b/lsp_def/classes/stake.lua @@ -14,6 +14,7 @@ ---@field unlocked? boolean Sets if the stake is unlocked by default. ---@field shiny? boolean Draws the shiny shader on State sticker. ---@field hide_from_run_info? boolean If true it hides the stake description on the Run Info screen +---@field vanilla_index? integer Only present on vanilla stakes. Stores original index for keeping save data compatible with an unmodified game state. ---@field __call? fun(self: SMODS.Stake|table, o: SMODS.Stake|table): nil|table|SMODS.Stake ---@field extend? fun(self: SMODS.Stake|table, o: SMODS.Stake|table): table Primary method of creating a class. ---@field check_duplicate_register? fun(self: SMODS.Stake|table): boolean? Ensures objects already registered will not register. diff --git a/lsp_def/utils.lua b/lsp_def/utils.lua index 8a5ae28ab..aa9c00c0f 100644 --- a/lsp_def/utils.lua +++ b/lsp_def/utils.lua @@ -520,6 +520,11 @@ function format_ui_value(value) end --- Returns the blind amount. function SMODS.get_blind_amount(ante) end +--- Converts save data for a single vanilla object. +---@param entry {count?:number,[("wins"|"losses")]?: table, [("wins_by_key"|"losses_by_key")]?:table}|table A deck/joker usage entry. +---@return table +function convert_usage_entry(entry) end + --- Converts save data for vanilla objects. function convert_save_data() end diff --git a/src/game_object.lua b/src/game_object.lua index 0c5a20220..c1f951655 100644 --- a/src/game_object.lua +++ b/src/game_object.lua @@ -736,7 +736,7 @@ Set `prefix_config.key = false` on your object instead.]]):format(obj.key), obj. for i = 1, #G.P_CENTER_POOLS[self.set] do G.C.STAKES[i] = G.P_CENTER_POOLS[self.set][i].colour or G.C.WHITE end - convert_save_data() + --convert_save_data() end, process_loc_text = function(self) -- empty loc_txt indicates there are existing values that shouldn't be changed or it isn't necessary @@ -802,7 +802,8 @@ Set `prefix_config.key = false` on your object instead.]]):format(obj.key), obj. sticker_pos = { x = 1, y = 0 }, colour = G.C.WHITE, loc_txt = {}, - hide_from_run_info = true + hide_from_run_info = true, + vanilla_index = 1, } SMODS.Stake { name = "Red Stake", @@ -816,7 +817,8 @@ Set `prefix_config.key = false` on your object instead.]]):format(obj.key), obj. G.GAME.modifiers.no_blind_reward.Small = true end, colour = G.C.RED, - loc_txt = {} + loc_txt = {}, + vanilla_index = 2, } SMODS.Stake { name = "Green Stake", @@ -829,7 +831,8 @@ Set `prefix_config.key = false` on your object instead.]]):format(obj.key), obj. G.GAME.modifiers.scaling = (G.GAME.modifiers.scaling or 1) + 1 end, colour = G.C.GREEN, - loc_txt = {} + loc_txt = {}, + vanilla_index = 3, } SMODS.Stake { name = "Black Stake", @@ -842,7 +845,8 @@ Set `prefix_config.key = false` on your object instead.]]):format(obj.key), obj. G.GAME.modifiers.enable_eternals_in_shop = true end, colour = G.C.BLACK, - loc_txt = {} + loc_txt = {}, + vanilla_index = 4, } SMODS.Stake { name = "Blue Stake", @@ -855,7 +859,8 @@ Set `prefix_config.key = false` on your object instead.]]):format(obj.key), obj. G.GAME.starting_params.discards = G.GAME.starting_params.discards - 1 end, colour = G.C.BLUE, - loc_txt = {} + loc_txt = {}, + vanilla_index = 5, } SMODS.Stake { name = "Purple Stake", @@ -868,7 +873,8 @@ Set `prefix_config.key = false` on your object instead.]]):format(obj.key), obj. G.GAME.modifiers.scaling = (G.GAME.modifiers.scaling or 1) + 1 end, colour = G.C.PURPLE, - loc_txt = {} + loc_txt = {}, + vanilla_index = 6, } SMODS.Stake { name = "Orange Stake", @@ -882,6 +888,7 @@ Set `prefix_config.key = false` on your object instead.]]):format(obj.key), obj. end, colour = G.C.ORANGE, loc_txt = {}, + vanilla_index = 7, } SMODS.Stake { name = "Gold Stake", @@ -894,7 +901,8 @@ Set `prefix_config.key = false` on your object instead.]]):format(obj.key), obj. end, colour = G.C.GOLD, shiny = true, - loc_txt = {} + loc_txt = {}, + vanilla_index = 8, } ------------------------------------------------------------------------------------------------- diff --git a/src/overrides.lua b/src/overrides.lua index 50583a8ff..68cf8939a 100644 --- a/src/overrides.lua +++ b/src/overrides.lua @@ -563,9 +563,9 @@ function G.UIDEF.deck_stake_column(_deck_key) local stake_col = {} local num_stakes = #G.P_CENTER_POOLS['Stake'] for i = #G.P_CENTER_POOLS['Stake'], 1, -1 do - local _wins = deck_usage and deck_usage.wins[i] or 0 + local _wins = deck_usage and deck_usage.wins_by_key[SMODS.stake_from_index(i)] or 0 local valid_option = nil - if (deck_usage and deck_usage.wins[i - 1]) or (not next(G.P_CENTER_POOLS.Stake[i].applied_stakes or {})) or G.PROFILES[G.SETTINGS.profile].all_unlocked then valid_option = true end + if (SMODS.stake_is_unlocked(SMODS.stake_from_index(i), _deck_key)) or (not next(G.P_CENTER_POOLS.Stake[i].applied_stakes or {})) or G.PROFILES[G.SETTINGS.profile].all_unlocked then valid_option = true end stake_col[#stake_col + 1] = {n = G.UIT.R, config = {id = i, align = "cm", colour = _wins > 0 and G.C.GREY or G.C.CLEAR, outline = 0, outline_colour = G.C.WHITE, r = 0.1, minh = 2 / num_stakes, minw = valid_option and 0.45 or 0.25, func = 'RUN_SETUP_check_back_stake_highlight'}, nodes = { {n = G.UIT.R, config = {align = "cm", minh = valid_option and 1.36 / num_stakes or 1.04 / num_stakes, minw = valid_option and 0.37 or 0.13, colour = _wins > 0 and get_stake_col(i) or G.C.UI.TRANSPARENT_LIGHT, r = 0.1}, nodes = {}}}} if i > 1 then stake_col[#stake_col + 1] = {n = G.UIT.R, config = {align = "cm", minh = 0.8 / num_stakes, minw = 0.04 }, nodes = {} } end @@ -2321,8 +2321,10 @@ function get_joker_win_sticker(_center, index) local applied = {} local _count = 0 local _stake = nil - for k, v in pairs(joker_usage.wins_by_key or {}) do - SMODS.build_stake_chain(G.P_STAKES[k], applied) + for k, stake in pairs(G.P_STAKES) do + if (joker_usage.wins_by_key or {})[k] then + SMODS.build_stake_chain(stake, applied) + end end for i, v in ipairs(G.P_CENTER_POOLS.Stake) do if applied[v.order] then @@ -2344,10 +2346,12 @@ function get_deck_win_stake(_deck_key) local deck_count = 0 for _, deck in pairs(G.PROFILES[G.SETTINGS.profile].deck_usage) do local deck_won_with = false - for key, _ in pairs(deck.wins_by_key or {}) do - deck_won_with = true - if (G.P_STAKES[key] and G.P_STAKES[key].stake_level or 0) > (_stake and G.P_STAKES[_stake].stake_level or 0) then - _stake = key + for key, stake in pairs(G.P_STAKES) do + if (deck.wins_by_key or {})[key] then + deck_won_with = true + if (stake.stake_level or 0) > (_stake and G.P_STAKES[_stake].stake_level or 0) then + _stake = key + end end end if deck_won_with then deck_count = deck_count + 1 end @@ -2360,9 +2364,11 @@ function get_deck_win_stake(_deck_key) end if G.PROFILES[G.SETTINGS.profile].deck_usage[_deck_key] and G.PROFILES[G.SETTINGS.profile].deck_usage[_deck_key].wins_by_key then local _stake = nil - for key, _ in pairs(G.PROFILES[G.SETTINGS.profile].deck_usage[_deck_key].wins_by_key) do - if (G.P_STAKES[key] and G.P_STAKES[key].stake_level or 0) > (_stake and G.P_STAKES[_stake].stake_level or 0) then - _stake = key + for key, stake in pairs(G.P_STAKES) do + if G.PROFILES[G.SETTINGS.profile].deck_usage[_deck_key].wins_by_key[key] then + if (stake.stake_level or 0) > (_stake and G.P_STAKES[_stake].stake_level or 0) then + _stake = key + end end end if _stake then return G.P_STAKES[_stake].order end @@ -2374,9 +2380,11 @@ function get_deck_win_sticker(_center) if G.PROFILES[G.SETTINGS.profile].deck_usage[_center.key] and G.PROFILES[G.SETTINGS.profile].deck_usage[_center.key].wins_by_key then local _stake = nil - for key, _ in pairs(G.PROFILES[G.SETTINGS.profile].deck_usage[_center.key].wins_by_key) do - if (G.P_STAKES[key] and G.P_STAKES[key].stake_level or 0) > (_stake and G.P_STAKES[_stake].stake_level or 0) then - _stake = G.sticker_map[key] and key or _stake + for key, stake in pairs(G.P_STAKES) do + if G.PROFILES[G.SETTINGS.profile].deck_usage[_center.key].wins_by_key[key] then + if (stake.stake_level or 0) > (_stake and G.P_STAKES[_stake].stake_level or 0) then + _stake = G.sticker_map[key] and key or _stake + end end end if _stake then return G.sticker_map[_stake] end @@ -2387,16 +2395,14 @@ function set_deck_win() if G.GAME.selected_back and G.GAME.selected_back.effect and G.GAME.selected_back.effect.center and G.GAME.selected_back.effect.center.key then local deck_key = G.GAME.selected_back.effect.center.key local deck_usage = G.PROFILES[G.SETTINGS.profile].deck_usage[deck_key] - if not deck_usage then deck_usage = { count = 1, order = + if not deck_usage then deck_usage = convert_usage_entry{ count = 1, order = G.GAME.selected_back.effect.center.order, wins = {}, losses = {}, wins_by_key = {}, losses_by_key = {} } end if deck_usage then - deck_usage.wins[G.GAME.stake] = (deck_usage.wins[G.GAME.stake] or 0) + 1 deck_usage.wins_by_key[SMODS.stake_from_index(G.GAME.stake)] = (deck_usage.wins_by_key[SMODS.stake_from_index(G.GAME.stake)] or 0) + 1 local applied = SMODS.build_stake_chain(G.P_STAKES[SMODS.stake_from_index(G.GAME.stake)]) or {} for i, v in ipairs(G.P_CENTER_POOLS.Stake) do if applied[i] then - deck_usage.wins[i] = math.max(deck_usage.wins[i] or 0, 1) - deck_usage.wins_by_key[SMODS.stake_from_index(i)] = math.max(deck_usage.wins_by_key[SMODS.stake_from_index(i)] or 0, 1) + deck_usage.wins_by_key[v.key] = math.max(deck_usage.wins_by_key[v.key] or 0, 1) end end end diff --git a/src/utils.lua b/src/utils.lua index e1929a08c..bae40290d 100644 --- a/src/utils.lua +++ b/src/utils.lua @@ -808,57 +808,42 @@ function SMODS.stake_from_index(index) return stake.key end -function convert_save_data() - local stakecount = #G.P_CENTER_POOLS.Stake - for _, v in pairs(G.PROFILES[G.SETTINGS.profile].deck_usage) do - if v.wins_by_key then - v.wins = {} - for kk,vv in pairs(v.wins_by_key) do - local index = G.P_STAKES[kk] and G.P_STAKES[kk].order - if index then v.wins[index] = vv end - end - else - v.wins_by_key = {} - for index=1,stakecount do - v.wins_by_key[SMODS.stake_from_index(index)] = v.wins[index] - end - end - if v.losses_by_key then - for kk,vv in pairs(v.losses_by_key) do - local index = G.P_STAKES[kk] and G.P_STAKES[kk].order - if index then v.losses[index] = vv end - end - else - v.losses_by_key = {} - for index=1,stakecount do - v.losses_by_key[SMODS.stake_from_index(index)] = v.losses[index] +function convert_usage_entry(entry) + for _,keys in ipairs{ {"wins","wins_by_key"},{"losses","losses_by_key"}} do + entry[keys[1]] = entry[keys[1]] or {} + entry[keys[2]] = entry[keys[2]] or {} + local data = entry[keys[1]] + local data_by_key = entry[keys[2]] + setmetatable(data_by_key, { + __index = function(t, k) + if (G.P_STAKES[k] or {}).vanilla_index then + return data[G.P_STAKES[k].vanilla_index] + end + return rawget(t,k) + end, + __newindex = function(t,k,w) + if (G.P_STAKES[k] or {}).vanilla_index then + data[G.P_STAKES[k].vanilla_index] = w + end + rawset(t,k,w) + end, + }) + for k,w in pairs(data_by_key) do + if (G.P_STAKES[k] or {}).vanilla_index then + data[G.P_STAKES[k].vanilla_index] = math.max(data[G.P_STAKES[k].vanilla_index] or 0, w) + rawset(data_by_key, k, nil) end end end + return entry +end + +function convert_save_data() + for _, v in pairs(G.PROFILES[G.SETTINGS.profile].deck_usage) do + convert_usage_entry(v) + end for _, v in pairs(G.PROFILES[G.SETTINGS.profile].joker_usage) do - if v.wins_by_key then - v.wins = {} - for kk,vv in pairs(v.wins_by_key) do - local index = G.P_STAKES[kk] and G.P_STAKES[kk].order - if index then v.wins[index] = vv end - end - else - v.wins_by_key = {} - for index=1,stakecount do - v.wins_by_key[SMODS.stake_from_index(index)] = v.wins[index] - end - end - if v.losses_by_key then - for kk,vv in pairs(v.losses_by_key) do - local index = G.P_STAKES[kk] and G.P_STAKES[kk].order - if index then v.losses[index] = vv end - end - else - v.losses_by_key = {} - for index=1,stakecount do - v.losses_by_key[SMODS.stake_from_index(index)] = v.losses[index] - end - end + convert_usage_entry(v) end G:save_settings() end @@ -3258,13 +3243,16 @@ function SMODS.challenge_is_unlocked(challenge, k) end function SMODS.stake_is_unlocked(stake_key, deck_key) - if not (SMODS.Stakes[stake_key] and G.PROFILES[G.SETTINGS.profile].deck_usage[deck_key]) then return stake_key == SMODS.stake_from_index(1) end if G.PROFILES[G.SETTINGS.profile].all_unlocked then return true end local stake = SMODS.Stakes[stake_key] + if not stake then return false end if stake.unlocked then return true end + if not G.PROFILES[G.SETTINGS.profile].deck_usage[deck_key] then + return not next(stake.applied_stakes) + end local unlocked = true local wins = G.PROFILES[G.SETTINGS.profile].deck_usage[deck_key].wins_by_key - for i,v in ipairs(stake.applied_stakes) do + for _,v in ipairs(stake.applied_stakes) do if not (wins[v] and wins[v] > 0) then unlocked = false end