Skip to content
4 changes: 2 additions & 2 deletions lovely/better_calc.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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
'''
Expand All @@ -966,7 +966,7 @@ position = 'at'
pattern = '''(?<indent>[\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
'''

Expand Down
119 changes: 80 additions & 39 deletions lovely/blind.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Expand Down Expand Up @@ -587,4 +549,83 @@ pattern = "local blindTable = {"
position = "after"
payload = '''
effect = self.effect,
'''
'''

### 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'''
2 changes: 1 addition & 1 deletion lovely/scaling.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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]]
Expand Down
18 changes: 0 additions & 18 deletions lovely/weights.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Expand Down
128 changes: 128 additions & 0 deletions src/game_object.lua
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand All @@ -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
Expand Down
31 changes: 16 additions & 15 deletions src/utils/weights.lua
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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)
Expand Down