[manifest] version = "1.0.0" dump_lua = true priority = -10 ### Fixes for either base game code or general mod compatibility ## Mods assume Game:start_run() is called with non-nil argument # G.FUNCS.start_run() [[patches]] [patches.pattern] target = 'functions/button_callbacks.lua' pattern = "G.FUNCS.start_run = function(e, args)" position = 'after' match_indent = true payload = "args = args or {}" ## Allows running the game without Steam being active # love.load() [[patches]] [patches.regex] target = 'main.lua' pattern = "(?[\t ]*)if not \\(st.init and st:init\\(\\)\\) then\n[\t ]*(?love.event.quit\\(\\))" position = 'at' root_capture = 'quit' payload = 'st = nil' ## Prevents the game from crashing when hitting play with a corrupt/invalid save file # G.FUNCS.can_continue(e) [[patches]] [patches.pattern] target = 'functions/button_callbacks.lua' pattern = "if G.SAVED_GAME ~= nil then G.SAVED_GAME = STR_UNPACK(G.SAVED_GAME) end" position = 'after' match_indent = true payload = """ if G.SAVED_GAME == nil then e.config.colour = G.C.UI.BACKGROUND_INACTIVE e.config.button = nil return _can_continue end """ ## Fix loading a blind with $0 reward # Blind:load() [[patches]] [patches.regex] target = 'blind.lua' pattern = ''' (?[\t ]*) G\.HUD_blind\.alignment\.offset\.y = 0 [\t ]*end''' position = 'at' payload = ''' end if G.GAME.blind.name and G.GAME.blind.name ~= '' then G.HUD_blind.alignment.offset.y = 0 end''' line_prepend = '$indent' ## Remove incorrect check for Moveable alignment change # Moveable:align_to_major() [[patches]] [patches.regex] target = 'engine/moveable.lua' pattern = ''' (?[\t ]*)if +self\.alignment\.prev_offset\.x == self\.alignment\.offset\.x[\s\S]*?return end ''' position = 'at' payload = 'if not self.alignment.type_list then return end' line_prepend = '$indent' ## Prevent softlock if booster pack is empty ## Crashes the game when you skip too fast on this PR, along with being the culprit for allowing you to skip boosters early # G.FUNCS.can_skip_booster() # [[patches]] # [patches.pattern] # target = 'functions/button_callbacks.lua' # pattern = 'if G.pack_cards and (G.pack_cards.cards[1]) and' # position = 'at' # payload = 'if G.pack_cards and' # match_indent = true ## Set `G.your_collection.config.collection` to true in all cases # create_UIBox_your_collection_seals() [[patches]] [patches.regex] target = 'functions/UI_definitions.lua' pattern = '''\{card_limit = 4, type = 'title', highlight_limit = 0\}''' position = 'at' payload = '''{card_limit = 4, type = 'title', highlight_limit = 0, collection = true}''' ## Save and load Card.unique_val # Card:save() [[patches]] [patches.pattern] target = 'card.lua' pattern = "bypass_lock = self.bypass_lock," position = "after" payload = """ unique_val = self.unique_val, unique_val__saved_ID = self.ID, ignore_base_shader = self.ignore_base_shader, ignore_shadow = self.ignore_shadow,""" match_indent = true # Card:load() [[patches]] [patches.pattern] target = 'card.lua' pattern = "self.bypass_lock = cardTable.bypass_lock" position = "after" payload = """ self.unique_val = cardTable.unique_val or self.unique_val if cardTable.unique_val__saved_ID and G.ID <= cardTable.unique_val__saved_ID then G.ID = cardTable.unique_val__saved_ID + 1 end self.ignore_base_shader = cardTable.ignore_base_shader or {} self.ignore_shadow = cardTable.ignore_shadow or {}""" match_indent = true ## Vars in card descriptions should use `card.ability` instead of `_c.config` where possible ## Allow passing in custom vars # generate_card_ui() [[patches]] [patches.pattern] target = 'functions/common_events.lua' pattern = 'function generate_card_ui(_c, full_UI_table, specific_vars, card_type, badges, hide_desc, main_start, main_end)' position = 'at' match_indent = true payload = '''function generate_card_ui(_c, full_UI_table, specific_vars, card_type, badges, hide_desc, main_start, main_end, card) if _c.specific_vars then specific_vars = _c.specific_vars end''' [[patches]] [patches.pattern] target = 'functions/common_events.lua' pattern = "if _c.set == 'Other' then" position = 'before' match_indent = true payload = "local cfg = (card and card.ability) or _c['config']" # string index to make sure the next patch doesn't eat it [[patches]] [patches.pattern] target = 'functions/common_events.lua' pattern = "if _c.name ~= 'Stone Card' and ((specific_vars and specific_vars.bonus_chips) or _c.config.bonus) then" position = 'at' match_indent = true payload = "if _c.name ~= 'Stone Card' and ((specific_vars and specific_vars.bonus_chips) or (cfg.bonus ~= 0 and cfg.bonus)) then" [[patches]] [patches.regex] target = 'functions/common_events.lua' pattern = '_c.config' position = 'at' payload = 'cfg' ## When overriding with set_ability and card is added to deck, call add / remove effects # Card:set_ability() [[patches]] [patches.pattern] target = 'card.lua' pattern = "self.config.center = center" position = 'before' match_indent = true payload = ''' if self.added_to_deck and old_center and not self.debuff then self:remove_from_deck() self.added_to_deck = true end''' [[patches]] [patches.pattern] target = 'card.lua' pattern = "if G.consumeables and self.area == G.consumeables then" position = 'before' match_indent = true payload = ''' if self.added_to_deck and old_center and not self.debuff then self.added_to_deck = false self:add_to_deck() end''' ## set_ability() transfers over old fields # special cases: # extra_value should be transferred # name, effect, set, extra should always be overwritten [[patches]] [patches.regex] target = 'card.lua' pattern = ''' (?[\t ]*)self\.ability = (\{[\s\S]*? [\t ]*\}) ''' position = 'at' line_prepend = '$indent' payload = ''' local new_ability = $2 self.ability = self.ability or {} new_ability.extra_value = nil self.ability.extra_value = self.ability.extra_value or 0 for k, v in pairs(new_ability) do self.ability[k] = v end -- reset keys do not persist an ability change local reset_keys = {'name', 'effect', 'set', 'extra', 'played_this_ante'} for _, mod in ipairs(SMODS.mod_list) do if mod.set_ability_reset_keys then local keys = mod.set_ability_reset_keys() for _, v in pairs(keys) do table.insert(reset_keys, v) end end end for _, k in ipairs(reset_keys) do self.ability[k] = new_ability[k] end ''' ## Fix crash if self.config.card == nil for non-vanilla set_ability() calls # Card:set_ability() [[patches]] [patches.pattern] target = 'card.lua' pattern = "self.label = center.label or self.config.card.label or self.ability.set" position = 'at' match_indent = true payload = "self.label = center.label or self.config.card and self.config.card.label or self.ability.set" ### Fix Matador # These patches have been removed for altering vanilla behavior. Git blame this line to see what they were ### Fix Crimson Heart ## Blind:drawn_to_hand() [[patches]] [patches.pattern] target = 'blind.lua' pattern = "if self.name == 'Crimson Heart' and self.prepped and G.jokers.cards[1] then" position = 'after' match_indent = true payload = """ local prev_chosen_set = {} local fallback_jokers = {}""" # Fix bad logic if not enough choices for debuff [[patches]] [patches.regex] target = 'blind.lua' pattern = ''' (?[\t ]*)for i = 1, #G\.jokers\.cards do [\t ]*if not G\.jokers\.cards\[i\]\.debuff or #G\.jokers\.cards < 2 then jokers\[#jokers\+1\] = ?G\.jokers\.cards\[i\] end [\t ]*G\.jokers\.cards\[i\]:set_debuff\(false\) [\t ]*end''' position = 'at' line_prepend = '$indent' payload = """ for i = 1, #G.jokers.cards do if G.jokers.cards[i].ability.crimson_heart_chosen then prev_chosen_set[G.jokers.cards[i]] = true G.jokers.cards[i].ability.crimson_heart_chosen = nil if G.jokers.cards[i].debuff then SMODS.recalc_debuff(G.jokers.cards[i]) end end end for i = 1, #G.jokers.cards do if not G.jokers.cards[i].debuff then if not prev_chosen_set[G.jokers.cards[i]] then jokers[#jokers+1] = G.jokers.cards[i] end table.insert(fallback_jokers, G.jokers.cards[i]) end end if #jokers == 0 then jokers = fallback_jokers end""" # Add variable for Crimson Heart's choice [[patches]] [patches.pattern] target = 'blind.lua' pattern = "_card:set_debuff(true)" position = "at" match_indent = true payload = """ _card.ability.crimson_heart_chosen = true SMODS.recalc_debuff(_card)""" ## Blind:debuff_card() [[patches]] [patches.regex] target = 'blind.lua' pattern = ''' if self\.name == 'Crimson Heart' and not self\.disabled and card\.area == G\.jokers then\s+ ((?[\t ]*)return)''' root_capture = '$1' position = "at" line_prepend = '$indent' payload = """ if card.ability.crimson_heart_chosen then card:set_debuff(true); if card.debuff then card.debuffed_by_blind = true end return end""" ## Blind:press_play() # Shouldn't work with Matador # yes it should ## Blind:disable() [[patches]] [patches.pattern] target = 'blind.lua' pattern = "if self.name == 'The Water' then" position = 'before' payload = """ if self.name == 'Crimson Heart' then for _, v in ipairs(G.jokers.cards) do v.ability.crimson_heart_chosen = nil end end""" match_indent = true ## Blind:defeat() [[patches]] [patches.pattern] target = 'blind.lua' pattern = "if self.name == 'The Manacle' and not self.disabled then" position = 'before' payload = """ if self.name == 'Crimson Heart' then for _, v in ipairs(G.jokers.cards) do v.ability.crimson_heart_chosen = nil end end""" match_indent = true ## Fix Manacle's unnecessary card draw after positive G.hand:change_size() # Blind:disable() [[patches]] [patches.regex] target = 'blind.lua' pattern = 'G\.hand:change_size\(1\)(\s+G\.FUNCS\.draw_from_deck_to_hand\(1\))' root_capture = '$1' position = 'at' payload = "" # # Money scaling fix # ## create_UIBox_HUD [[patches]] [patches.regex] target = "functions/UI_definitions.lua" pattern = ''' string = \{\{ref_table = G\.GAME\, ref_value = 'dollars'\, prefix = localize\('\$'\)\}\}\,''' position = "after" payload = ''' scale_function = function () return scale_number(G.GAME.dollars, 2.2 * scale, 99999, 1000000) end,''' ## DynaText:update_text [[patches]] [patches.pattern] target = "engine/text.lua" pattern = 'self.config.H = 0' position = "after" payload = "self.scale = self.config.scale_function and self.config.scale_function() or self.scale" match_indent = true # # Fix gold stake legendary infloop # Do not try to roll for jokers that are not in_pool # # generate_starting_seed() [[patches]] [patches.pattern] target = "functions/misc_functions.lua" pattern = '''if win_ante and (win_ante >= 8) then''' match_indent = true position = "at" payload = '''if win_ante and (win_ante >= 8) or (v.in_pool and type(v.in_pool) == 'function' and not v:in_pool()) then''' # # Fix G.GAME.blind:set_blind(nil, true, nil) # being called when not in blind. # # Card:add_to_deck # Card:remove_from_deck [[patches]] [patches.regex] target = "card.lua" pattern = 'if G\.GAME\.blind then' position = "at" payload = "if G.GAME.blind and G.GAME.blind.in_blind then" # Blind:set_blind [[patches]] [patches.pattern] target = "blind.lua" match_indent = true pattern = "if not reset then" position = "after" payload = ''' if blind then self.in_blind = true end''' # end_round() [[patches]] [patches.pattern] target = "functions/state_events.lua" pattern = "local game_over = true" position = "before" payload = "G.GAME.blind.in_blind = false" match_indent = true # Make sure new param is loaded [[patches]] [patches.pattern] target = "blind.lua" match_indent = true pattern = "function Blind:load(blindTable)" position = "after" payload = ''' self.in_blind = blindTable.in_blind''' # Make sure new param is saved [[patches]] [patches.pattern] target = "blind.lua" match_indent = true pattern = "local blindTable = {" position = "after" payload = ''' in_blind = self.in_blind,''' # Cartomancer and astronomer unlock when *actually all* Tarot/Planet cards are discovered # check_for_unlock() [[patches]] [patches.pattern] target = "functions/common_events.lua" match_indent = true pattern = "if card.unlock_condition.tarot_count <= args.tarot_count then" position = "at" payload = 'if #G.P_CENTER_POOLS.Tarot <= args.tarot_count then' [[patches]] [patches.pattern] target = "functions/common_events.lua" match_indent = true pattern = "if card.unlock_condition.planet_count <= args.planet_count then" position = "at" payload = 'if #G.P_CENTER_POOLS.Planet <= args.planet_count then' # wtf [[patches]] [patches.pattern] target = "engine/animatedsprite.lua" match_indent = true pattern = "for _, v in pairs(G.ANIMATIONS) do" position = "at" payload = 'for k, v in pairs(G.ANIMATIONS) do' [[patches]] [patches.pattern] target = "engine/animatedsprite.lua" match_indent = true pattern = "for _, v in pairs(G.I.SPRITE) do" position = "at" payload = 'for k, v in pairs(G.I.SPRITE) do' ## ## Card:draw() - improved mod compatibility ## # Add option for sprites to not be drawn [[patches]] [patches.pattern] target = "card.lua" match_indent = true pattern = ''' if k ~= 'focused_ui' and k ~= "front" and k ~= "back" and k ~= "soul_parts" and k ~= "center" and k ~= 'floating_sprite' and k~= "shadow" and k~= "use_button" and k ~= 'buy_button' and k ~= 'buy_and_use_button' and k~= "debuff" and k ~= 'price' and k~= 'particles' and k ~= 'h_popup' then v:draw() end''' position = "at" payload = ''' if not v.custom_draw and k ~= 'focused_ui' and k ~= "front" and k ~= "back" and k ~= "soul_parts" and k ~= "center" and k ~= 'floating_sprite' and k~= "shadow" and k~= "use_button" and k ~= 'buy_button' and k ~= 'buy_and_use_button' and k~= "debuff" and k ~= 'price' and k~= 'particles' and k ~= 'h_popup' then v:draw() end''' # This check is not necessary? [[patches]] [patches.pattern] target = "card.lua" match_indent = true pattern = ''' if self.edition or self.seal or self.ability.eternal or self.ability.rental or self.ability.perishable or self.sticker or ((self.sticker_run and self.sticker_run ~= 'NONE') and G.SETTINGS.run_stake_stickers) or (self.ability.set == 'Spectral') or self.debuff or self.greyed or (self.ability.name == 'The Soul') or (self.ability.set == 'Voucher') or (self.ability.set == 'Booster') or self.config.center.soul_pos or self.config.center.demo then''' position = "at" payload = ''' if true then''' ## Make vanilla enhancement jokers work with extra enhancements # Steel Joker [[patches]] [patches.pattern] target = "card.lua" match_indent = true pattern = "if v.config.center == G.P_CENTERS.m_steel then self.ability.steel_tally = self.ability.steel_tally+1 end" position = "at" payload = "if SMODS.has_enhancement(v, 'm_steel') then self.ability.steel_tally = self.ability.steel_tally+1 end" # Stone Joker [[patches]] [patches.pattern] target = "card.lua" match_indent = true pattern = "if v.config.center == G.P_CENTERS.m_stone then self.ability.stone_tally = self.ability.stone_tally+1 end" position = "at" payload = "if SMODS.has_enhancement(v, 'm_stone') then self.ability.stone_tally = self.ability.stone_tally+1 end" # Golden Ticket [[patches]] [patches.pattern] target = "card.lua" match_indent = true pattern = "context.other_card.ability.name == 'Gold Card' then" position = "at" payload = "SMODS.has_enhancement(context.other_card, 'm_gold') then" # Golden Ticket Unlock [[patches]] [patches.pattern] target = "functions/common_events.lua" match_indent = true pattern = "if args.cards[j].ability.name == 'Gold Card' then" position = "at" payload = "if SMODS.has_enhancement(args.cards[j], 'm_gold') then" # Glass Joker [[patches]] [patches.pattern] target = "card.lua" match_indent = true pattern = "if val.ability.name == 'Glass Card' then shattered_glass = shattered_glass + 1 end" position = "at" payload = "if SMODS.has_enhancement(val, 'm_glass') then shattered_glass = shattered_glass + 1 end" # Driver's License [[patches]] [patches.pattern] target = "card.lua" match_indent = true pattern = "if v.config.center ~= G.P_CENTERS.c_base then self.ability.driver_tally = self.ability.driver_tally+1 end" position = "at" payload = "if next(SMODS.get_enhancements(v)) then self.ability.driver_tally = self.ability.driver_tally+1 end" # Basegame fix for the reroll vouchers redeem function when only the center but no card object exists # Card:apply_to_run [[patches]] [patches.pattern] target = 'card.lua' pattern = """G.GAME.round_resets.reroll_cost = G.GAME.round_resets.reroll_cost - self.ability.extra""" position = 'at' match_indent = true payload = """G.GAME.round_resets.reroll_cost = G.GAME.round_resets.reroll_cost - center_table.extra""" # Card:apply_to_run [[patches]] [patches.pattern] target = 'card.lua' pattern = """G.GAME.current_round.reroll_cost = math.max(0, G.GAME.current_round.reroll_cost - self.ability.extra)""" position = 'at' match_indent = true payload = """G.GAME.current_round.reroll_cost = math.max(0, G.GAME.current_round.reroll_cost - center_table.extra)""" # Add h_chips as a viable hand effect [[patches]] [patches.pattern] target = 'functions/state_events.lua' pattern = '''card_eval_status_text(G.hand.cards[i], 'h_mult', effects[ii].h_mult, percent) end''' position = 'after' match_indent = true payload = ''' if effects[ii].h_chips then if effects[ii].card then juice_card(effects[ii].card) end hand_chips = mod_chips(hand_chips + effects[ii].h_chips) update_hand_text({delay = 0}, {chips = hand_chips}) card_eval_status_text(effects[ii].card, 'chips', effects[ii].h_chips, percent) end ''' # Fix booster skip issues maybe? [[patches]] [patches.pattern] target = "functions/button_callbacks.lua" pattern = "if G.pack_cards and (G.pack_cards.cards[1]) and" position = "at" payload = ''' if G.pack_cards and (not (G.GAME.STOP_USE and G.GAME.STOP_USE > 0)) and ''' match_indent = true # Due to STOP_USE being used we can remove this dumb check # could probably remove the rest of it since it serves no purpose otherwise, but whatever [[patches]] [patches.regex] target = "functions/button_callbacks.lua" pattern = '''and \(G\.hand\.cards\[1\] or \(G\.hand\.config\.card_limit <= 0\)\)''' position = "at" payload = ''' ''' match_indent = true # Fixes Steam API not loading on unix [[patches]] [patches.pattern] target = 'main.lua' match_indent = true position = 'after' pattern = '--To control when steam communication happens, make sure to send updates to steam as little as possible' payload = '''local cwd = NFS.getWorkingDirectory() NFS.setWorkingDirectory(love.filesystem.getSourceBaseDirectory()) ''' [[patches]] [patches.pattern] target = 'main.lua' match_indent = true position = 'before' pattern = '--Set up the render window and the stage for the splash screen, then enter the gameloop with :update' payload = '''NFS.setWorkingDirectory(cwd) ''' [[patches]] [patches.pattern] target = "main.lua" pattern = '''if os == 'OS X' or os == 'Windows' then''' position = "at" payload = '''if os == 'OS X' or os == 'Windows' or os == 'Linux' then''' overwrite = true match_indent = true [[patches]] [patches.pattern] target = "main.lua" pattern = '''if os == 'OS X' then''' position = "at" payload = '''if os == 'OS X' or os == 'Linux' then''' overwrite = true match_indent = true [[patches]] [patches.pattern] target = "main.lua" pattern = "st = require 'luasteam'" position = "at" payload = """local success, _st = pcall(require, 'luasteam') if success then st = _st else sendWarnMessage(_st); st = {} end""" overwrite = true match_indent = true