diff --git a/lovely/better_calc.toml b/lovely/better_calc.toml index 5239d5b62..d35d5406f 100644 --- a/lovely/better_calc.toml +++ b/lovely/better_calc.toml @@ -953,7 +953,7 @@ payload = ''' SMODS.saved = false G.GAME.saved_text = nil SMODS.last_hand = SMODS.last_hand or {scoring_hand = {}, full_hand = {}} -SMODS.calculate_context({end_of_round = true, game_over = game_over, beat_boss = G.GAME.blind.boss, scoring_hand = SMODS.last_hand.scoring_hand, scoring_name = SMODS.last_hand.scoring_name, full_hand = SMODS.last_hand.full_hand }) +SMODS.calculate_context({end_of_round = true, game_over = game_over, beat_boss = G.GAME.blind:is_type("Boss"), scoring_hand = SMODS.last_hand.scoring_hand, scoring_name = SMODS.last_hand.scoring_name, full_hand = SMODS.last_hand.full_hand }) if SMODS.saved then game_over = false end -- TARGET: main end_of_round evaluation ''' @@ -966,7 +966,7 @@ position = 'at' pattern = '''(?[\t ]*)for i=1, #G\.hand\.cards do\n\s+--Check for hand doubling\n(.*\n)*?\s+delay\(0\.3\)''' line_prepend = '$indent' payload = '''for _,v in ipairs(SMODS.get_card_areas('playing_cards', 'end_of_round')) do - SMODS.calculate_end_of_round_effects({ cardarea = v, end_of_round = true, beat_boss = G.GAME.blind.boss }) + SMODS.calculate_end_of_round_effects({ cardarea = v, end_of_round = true, beat_boss = G.GAME.blind:is_type("Boss") }) end ''' diff --git a/lovely/blind.toml b/lovely/blind.toml index 9d1771fa1..a190e6053 100644 --- a/lovely/blind.toml +++ b/lovely/blind.toml @@ -462,44 +462,6 @@ payload = ''' {n=G.UIT.O, config={object = DynaText({string = localize(target), colours = {G.C.WHITE},shadow = true, float = true,maxw = 2.2, scale = 0.45})}} ''' -# get_new_boss() -[[patches]] -[patches.pattern] -target = 'functions/common_events.lua' -pattern = ''' -if not v.boss then - -elseif not v.boss.showdown and (v.boss.min <= math.max(1, G.GAME.round_resets.ante) and ((math.max(1, G.GAME.round_resets.ante))%G.GAME.win_ante ~= 0 or G.GAME.round_resets.ante < 2)) then - eligible_bosses[k] = true -elseif v.boss.showdown and (G.GAME.round_resets.ante)%G.GAME.win_ante == 0 and G.GAME.round_resets.ante >= 2 then - eligible_bosses[k] = true -end -''' -match_indent = true -position = 'at' -payload = ''' -local res, options = SMODS.add_to_pool(v) -options = options or {} -if not v.boss then - -elseif options.ignore_showdown_check then - eligible_bosses[k] = res and true or nil -elseif v.in_pool and type(v.in_pool) == 'function' then - if - ( - ((G.GAME.round_resets.ante)%G.GAME.win_ante == 0 and G.GAME.round_resets.ante >= 2) == - (v.boss.showdown or false) - ) - then - eligible_bosses[k] = res and true or nil - end -elseif not v.boss.showdown and (v.boss.min <= math.max(1, G.GAME.round_resets.ante) and ((math.max(1, G.GAME.round_resets.ante))%G.GAME.win_ante ~= 0 or G.GAME.round_resets.ante < 2)) then - eligible_bosses[k] = res and true or nil -elseif v.boss.showdown and (G.GAME.round_resets.ante)%G.GAME.win_ante == 0 and G.GAME.round_resets.ante >= 2 then - eligible_bosses[k] = res and true or nil -end -''' - # G.UIDEF.challenge_description_tab [[patches]] [patches.pattern] @@ -587,4 +549,83 @@ pattern = "local blindTable = {" position = "after" payload = ''' effect = self.effect, -''' \ No newline at end of file +''' + +### Account for changed Blind:get_type() function +# Blind.set_blind : Use new Blind:is_types() +[[patches]] +[patches.pattern] +target = "blind.lua" +match_indent = true +pattern = "if G.GAME.modifiers.no_blind_reward and G.GAME.modifiers.no_blind_reward[self:get_type()] then self.dollars = 0 end" +position = "at" +payload = '''if G.GAME.modifiers.no_blind_reward and self:is_types(G.GAME.modifiers.no_blind_reward) then self.dollars = 0 end''' +# game.lua : Game:start_run() : account for "Showdown" type with "no_reward" rule +[[patches]] +[patches.pattern] +target = "game.lua" +match_indent = true +pattern = "self.GAME.modifiers.no_blind_reward.Boss = true" +position = "after" +payload = '''self.GAME.modifiers.no_blind_reward.Showdown = true''' +# card.lua : Card:generate_UIBox_ability_table() +[[patches]] +[patches.pattern] +target = "game.lua" +match_indent = true +pattern = "local disableable = G.GAME.blind and ((not G.GAME.blind.disabled) and (G.GAME.blind:get_type() == 'Boss'))" +position = "at" +payload = '''local disableable = G.GAME.blind and ((not G.GAME.blind.disabled) and (G.GAME.blind:is_type("Boss")))''' +# card.lua : Card:calculate_joker() : Luchador !-- could be taken ownership of instead +[[patches]] +[patches.pattern] +target = "game.lua" +match_indent = true +pattern = "if G.GAME.blind and ((not G.GAME.blind.disabled) and (G.GAME.blind:get_type() == 'Boss')) then " +position = "at" +payload = '''if G.GAME.blind and ((not G.GAME.blind.disabled) and (G.GAME.blind:is_type("Boss"))) then ''' +# functions/button_callbacks.lua : G.FUNCS.HUD_blind_reward() +[[patches]] +[patches.pattern] +target = "functions/button_callbacks.lua" +match_indent = true +pattern = "if G.GAME.modifiers.no_blind_reward and (G.GAME.blind and G.GAME.modifiers.no_blind_reward[G.GAME.blind:get_type()]) then" +position = "at" +payload = ''' +if G.GAME.modifiers.no_blind_reward and (G.GAME.blind and blind:is_types(G.GAME.modifiers.no_blind_reward)) then +''' +# functions/state_events.lua : end_round() +[[patches]] +[patches.pattern] +target = "functions/state_events.lua" +match_indent = true +pattern = '''if G.GAME.blind:get_type() == 'Boss' and not G.GAME.seeded and not G.GAME.challenge then''' +position = "at" +payload = ''' +if G.GAME.blind:is_type("Boss") and not G.GAME.seeded and not G.GAME.challenge then +''' +[[patches]] +[patches.pattern] +target = "functions/state_events.lua" +match_indent = true +pattern = '''if G.GAME.blind:get_type() == 'Boss' then''' +position = "at" +payload = ''' +if G.GAME.blind:is_type("Boss") then +''' +[[patches]] +[patches.pattern] +target = "functions/state_events.lua" +match_indent = true +pattern = '''if not G.GAME.won and G.GAME.round_resets.ante >= G.GAME.win_ante and G.GAME.blind_on_deck == 'Boss' then''' +position = "at" +payload = ''' +if G.GAME.round_resets.ante == G.GAME.win_ante and G.GAME.blind:is_type("Boss") then +''' +# game.lua : Game:init_game_object() +[[patches]] +[patches.regex] +target = "game.lua" +pattern = '''if v.boss then bosses_used[k] = 0 end''' +position = "at" +payload = '''if SMODS.get_blind_types(blind_obj).Boss then bosses_used[k] = 0 end''' diff --git a/lovely/scaling.toml b/lovely/scaling.toml index f0f1d80c5..bf1995aa4 100644 --- a/lovely/scaling.toml +++ b/lovely/scaling.toml @@ -275,7 +275,7 @@ if self.ability.name == 'Madness' and not context.blueprint and not context.blin position = 'at' match_indent = true payload = ''' -if self.ability.name == 'Madness' and not context.blueprint and not context.blind.boss then +if self.ability.name == 'Madness' and not context.blueprint and not (context.blind:get_types() or {}).Boss then ''' # Madness pt2 [[patches]] diff --git a/lovely/weights.toml b/lovely/weights.toml index c2c113a72..c4c31f352 100644 --- a/lovely/weights.toml +++ b/lovely/weights.toml @@ -3,24 +3,6 @@ version = "1.0.0" dump_lua = true priority = -10 -# Bypass get_new_boss() -[[patches]] -[patches.pattern] -target = 'functions/common_events.lua' -match_indent = true -position = 'before' -pattern = ''' -local eligible_bosses = {} -''' -payload = ''' --- Use SMODS object weight system when enabled -if SMODS.optional_features.object_weights then - local ret_boss = SMODS.poll_object({type = 'Blind', seed = 'boss'}) - G.GAME.bosses_used[ret_boss] = G.GAME.bosses_used[ret_boss] + 1 - return ret_boss -end -''' - # Bypass create_card_for_shop() [[patches]] [patches.pattern] diff --git a/src/game_object.lua b/src/game_object.lua index 9f7242eb4..7f4fa3527 100644 --- a/src/game_object.lua +++ b/src/game_object.lua @@ -1817,6 +1817,8 @@ SMODS.UndiscoveredCompat = { debuff = {}, vars = {}, config = {}, + blind_types = nil, -- Map of types, e.g. {Boss = true, Big = true} + -- [type] = {min=..., max=...} -> Per-type constraints for appearing. dollars = 5, mult = 2, atlas = 'blind_chips', @@ -1838,8 +1840,134 @@ SMODS.UndiscoveredCompat = { end G.P_BLINDS[self.key] = self if self.modifies_draw then SMODS.Blinds.modifies_draw[self.key] = true end + end, + get_types = function(self) + return self.blind_types end } + + function SMODS.get_blind_types(blind_obj) + local ret + if type(blind_obj.get_types) == "function" then + ret = blind_obj:get_types() + end + if not ret then + if blind_obj.boss then + if not blind_obj.boss.showdown then + return {Boss = true} + else + return {Showdown = true} + end + else + if blind_obj.name == "Small Blind" then + return {Small = true} + else + return {Big = true} + end + end + end + return ret + end + + function get_new_boss() + -- sendWarnMessage("get_new_boss() is deprecated; Call SMODS.get_new_blind() instead.", "utils") + local b_types = SMODS.is_showdown_ante() and {Showdown = true} or {Boss = true} + local boss = SMODS.get_new_blind(b_types) + return boss + end + + function SMODS.get_new_blind(blind_types) + -- Use SMODS object weight system when enabled + if SMODS.optional_features.object_weights then + local ret = SMODS.poll_object({type = 'Blind', blind_types = blind_types}) + G.GAME.bosses_used[ret] = G.GAME.bosses_used[ret] + 1 + return ret + end + if not blind_types or (blind_types.Boss or blind_types.Showdown) then + G.GAME.perscribed_bosses = G.GAME.perscribed_bosses or {} + if G.GAME.perscribed_bosses and G.GAME.perscribed_bosses[G.GAME.round_resets.ante] then + local ret_boss = G.GAME.perscribed_bosses[G.GAME.round_resets.ante] + G.GAME.perscribed_bosses[G.GAME.round_resets.ante] = nil + G.GAME.bosses_used[ret_boss] = G.GAME.bosses_used[ret_boss] + 1 + return ret_boss + end + if G.FORCE_BOSS then return G.FORCE_BOSS end + end + + local eligible_bosses = {} + for k, v in pairs(G.P_BLINDS) do + local res = SMODS.add_to_pool(v) + if res then + local b_types = SMODS.get_blind_types(v) + local blind_config = v.boss or (table_length(b_types) == 1 and next(b_types)) or {} + for b_type, _ in pairs(b_types) do + if blind_types[b_type] then + if v.in_pool or not (blind_config and (blind_config.min and blind_config.min > math.max(1, G.GAME.round_resets.ante) or blind_config.max and blind_config.max <= G.GAME.round_resets.ante)) then + eligible_bosses[k] = true + break + end + end + end + end + end + for k, v in pairs(G.GAME.banned_keys) do + if eligible_bosses[k] then eligible_bosses[k] = nil end + end + + local min_use = 100 + for k, v in pairs(G.GAME.bosses_used) do + if eligible_bosses[k] then + eligible_bosses[k] = v + if eligible_bosses[k] <= min_use then + min_use = eligible_bosses[k] + end + end + end + for k, v in pairs(eligible_bosses) do + if eligible_bosses[k] then + if eligible_bosses[k] > min_use then + eligible_bosses[k] = nil + end + end + end + local _, boss = pseudorandom_element(eligible_bosses, pseudoseed('boss')) + G.GAME.bosses_used[boss] = G.GAME.bosses_used[boss] + 1 + + return boss + end + + function Blind:get_type() + return SMODS.get_blind_types(self.config.blind) + end + + function Blind:is_type(b_type) + return self:is_types({[b_type] = true}, false) + end + + function Blind:is_types(b_types_map, all) + for k, v in pairs(self:get_type()) do + if v and b_types_map[k] or (k == "Showdown" and b_types_map.Boss) then + if not all then + return true + end + elseif all then + return false + end + end + return all + end + + SMODS.Joker:take_ownership("j_matador", { + check_for_unlock = function (self, args) + return G.GAME.current_round.hands_played == 1 and G.GAME.current_round.discards_left == G.GAME.round_resets.discards and G.GAME.blind:is_type("Boss") + end + }) + + SMODS.Joker:take_ownership("j_hanging_chad", { + check_for_unlock = function (self, args) + return G.GAME.last_hand_played == self.unlock_condition.extra and G.GAME.blind:is_type("Boss") + end + }) SMODS.Blind:take_ownership('eye', { set_blind = function(self, reset, silent) if not reset then diff --git a/src/utils/weights.lua b/src/utils/weights.lua index 47f51b614..dec6825da 100644 --- a/src/utils/weights.lua +++ b/src/utils/weights.lua @@ -147,25 +147,26 @@ function SMODS.get_poll_key(type, infill) return t.str .. (t.block_infill and "" or infill or "") .. (t.ante and G.GAME.round_resets.ante or "") end -function SMODS.create_blind_pool(blind_type, skip_cull) - assert(type(blind_type) == 'string', "SMODS.create_blind_pool called with a non-string type argument."..SMODS.log_crash_info(debug.getinfo(2))) +function SMODS.create_blind_pool(_blind_type, skip_cull) + assert(type(_blind_type) == 'string' or type(_blind_type) == 'table', "SMODS.create_blind_pool called with an invalid argument."..SMODS.log_crash_info(debug.getinfo(2))) + local blind_types + if type(_blind_type) == "string" then + blind_types = {[_blind_type] = true} + else + blind_types = _blind_type + end local eligible_bosses = {} + _blind_type = (table_length(blind_types) == 1 and next(blind_types)) or "" -- If a string was passed, remains unchanged. for k, v in pairs(G.P_BLINDS) do local res, options = SMODS.add_to_pool(v) options = options or {} - if not v[blind_type] then - elseif options.ignore_showdown_check then + local blind_config = v.boss or v[_blind_type] or {} + if v:is_types(blind_types) and ( + v.in_pool or blind_config and ((blind_config.min or G.GAME.round_resets.ante) <= math.max(1, G.GAME.round_resets.ante) and + (blind_config.max or G.GAME.round_resets.ante) >= G.GAME.round_resets.ante) + ) + then eligible_bosses[k] = res and true or nil - elseif blind_type == 'boss' then - if - ((SMODS.is_showdown_ante()) == (v.boss.showdown or false)) and ((v[blind_type].min or G.GAME.round_resets.ante) <= math.max(1, G.GAME.round_resets.ante)) and ((v[blind_type].max or G.GAME.round_resets.ante) >= G.GAME.round_resets.ante) - then - eligible_bosses[k] = res and true or nil - end - else - if (v[blind_type].min or G.GAME.round_resets.ante) <= math.max(1, G.GAME.round_resets.ante) and (v[blind_type].max or G.GAME.round_resets.ante) >= G.GAME.round_resets.ante then - eligible_bosses[k] = res and true or nil - end end end for k, v in pairs(G.GAME.banned_keys) do @@ -289,7 +290,7 @@ function SMODS.create_poll_pool(labels, args) local join_func = (args.attributes and not args.union) and SMODS.intersect_lists or join_lists for i=1, #(args.rarities or {true}) do if label == "Booster" then SMODS.poll_object_allow_duplicates = true end - local _p = label == 'Blind' and SMODS.create_blind_pool(args.blind_type or 'boss') or SMODS.Attributes[label] and SMODS.get_attribute_pool(label) or get_current_pool(label, args.rarities and args.rarities[i], nil, args.append) + local _p = label == 'Blind' and SMODS.create_blind_pool(args.blind_types or {Boss = true}) or SMODS.Attributes[label] and SMODS.get_attribute_pool(label) or get_current_pool(label, args.rarities and args.rarities[i], nil, args.append) SMODS.poll_object_allow_duplicates = nil if SMODS.Attributes[label] then _p = SMODS.cull_pool(_p, args)