--- Original: Divvy's Preview for Balatro - Core.lua if SMODS and SMODS.current_mod then SMODS.Atlas({ key = "modicon", path = "icon.png", px = 32, py = 32, }) end -- The functions responsible for running the simulation at appropriate times; -- ie. whenever the player modifies card selection or card order. function FN.PRE.simulate() -- Guard against simulating in redundant places: if not (G.STATE == G.STATES.SELECTING_HAND or G.STATE == G.STATES.DRAW_TO_HAND or G.STATE == G.STATES.PLAY_TAROT) then return {score = {min = 0, exact = 0, max = 0}, dollars = {min = 0, exact = 0, max = 0}} end if G.SETTINGS.FN.hide_face_down then for _, card in ipairs(G.hand.highlighted) do if card.facing == "back" then return nil end end if #(G.hand.highlighted) ~= 0 then for _, joker in ipairs(G.jokers.cards) do if joker.facing == "back" then return nil end end end end return FN.SIM.run() end -- -- SIMULATION UPDATE ADVICE: -- function FN.PRE.add_update_event(trigger) function sim_func() FN.PRE.data = FN.PRE.simulate() return true end if FN.PRE.enabled() then G.E_MANAGER:add_event(Event({trigger = trigger, func = sim_func})) end end -- Update simulation after a consumable (eg. Tarot, Planet) is used: local orig_use = Card.use_consumeable function Card:use_consumeable(area, copier) orig_use(self, area, copier) FN.PRE.add_update_event("immediate") end -- Update simulation after card selection changed: local orig_hl = CardArea.parse_highlighted function CardArea:parse_highlighted() orig_hl(self) FN.PRE.add_update_event("immediate") end -- Update simulation after joker sold: local orig_card_remove = Card.remove_from_area function Card:remove_from_area() orig_card_remove(self) if self.config.type == 'joker' then FN.PRE.add_update_event("immediate") end end -- Update simulation after joker reordering: local orig_update = CardArea.update function CardArea:update(dt) orig_update(self, dt) FN.PRE.update_on_card_order_change(self) end function FN.PRE.update_on_card_order_change(cardarea) if #cardarea.cards == 0 or not (G.STATE == G.STATES.SELECTING_HAND or G.STATE == G.STATES.DRAW_TO_HAND or G.STATE == G.STATES.PLAY_TAROT) then return end -- Important not to update on G.STATES.HAND_PLAYED, because it would reset the preview text! local prev_order = nil if cardarea.config.type == 'joker' and cardarea.cards[1].ability.set == 'Joker' then if cardarea.cards[1].edition and cardarea.cards[1].edition.mp_phantom then return end -- Note that the consumables cardarea also has type 'joker' so must verify by checking first card. prev_order = FN.PRE.joker_order elseif cardarea.config.type == 'hand' then prev_order = FN.PRE.hand_order else return end -- Go through stored card IDs and check against current card IDs, in-order. -- If any mismatch occurs, toggle flag and update name for next time. local should_update = false if #cardarea.cards ~= #prev_order then prev_order = {} end for i, c in ipairs(cardarea.cards) do if c.sort_id ~= prev_order[i] then prev_order[i] = c.sort_id should_update = true end end if should_update then if cardarea.config.type == 'joker' or cardarea.cards[1].ability.set == 'Joker' then FN.PRE.joker_order = prev_order elseif cardarea.config.type == 'hand' then FN.PRE.hand_order = prev_order end FN.PRE.add_update_event("immediate") end end -- -- SIMULATION RESET ADVICE: -- function FN.PRE.add_reset_event(trigger) function reset_func() FN.PRE.data = {score = {min = 0, exact = 0, max = 0}, dollars = {min = 0, exact = 0, max = 0}} return true end if FN.PRE.enabled() then G.E_MANAGER:add_event(Event({trigger = trigger, func = reset_func})) end end local orig_eval = G.FUNCS.evaluate_play function G.FUNCS.evaluate_play(e) orig_eval(e) FN.PRE.add_reset_event("after") end local orig_discard = G.FUNCS.discard_cards_from_highlighted function G.FUNCS.discard_cards_from_highlighted(e, is_hook_blind) orig_discard(e, is_hook_blind) if not is_hook_blind then FN.PRE.add_reset_event("immediate") end end -- -- USER INTERFACE ADVICE: -- -- Add animation to preview text: function G.FUNCS.fn_pre_score_UI_set(e) local new_preview_text = "" local should_juice = false if FN.PRE.data then if true and (FN.PRE.data.score.min ~= FN.PRE.data.score.max) then -- Format as 'X - Y' : if e.config.id == "fn_pre_l" then new_preview_text = FN.PRE.format_number(FN.PRE.data.score.min) .. " - " if FN.PRE.is_enough_to_win(FN.PRE.data.score.min) then should_juice = true end elseif e.config.id == "fn_pre_r" then new_preview_text = FN.PRE.format_number(FN.PRE.data.score.max) if FN.PRE.is_enough_to_win(FN.PRE.data.score.max) then should_juice = true end end else -- Format as single number: if e.config.id == "fn_pre_l" then if true then -- Spaces around number necessary to distinguish Min/Max text from Exact text, -- which is itself necessary to force a HUD update when switching between Min/Max and Exact. new_preview_text = " " .. FN.PRE.format_number(FN.PRE.data.score.min) .. " " if FN.PRE.is_enough_to_win(FN.PRE.data.score.min) then should_juice = true end else new_preview_text = number_format(FN.PRE.data.score.exact) if FN.PRE.is_enough_to_win(FN.PRE.data.score.exact) then should_juice = true end end else new_preview_text = "" end end else -- Spaces around number necessary to distinguish Min/Max text from Exact text, same as above ^ if e.config.id == "fn_pre_l" then if true then new_preview_text = " ?????? " else new_preview_text = "??????" end else new_preview_text = "" end end if (not FN.PRE.text.score[e.config.id:sub(-1)]) or new_preview_text ~= FN.PRE.text.score[e.config.id:sub(-1)] then FN.PRE.text.score[e.config.id:sub(-1)] = new_preview_text e.config.object:update_text() -- Wobble: if not G.TAROT_INTERRUPT_PULSE then if should_juice then G.FUNCS.text_super_juice(e, 5) e.config.object.colours = {G.C.MONEY} else G.FUNCS.text_super_juice(e, 0) e.config.object.colours = {G.C.UI.TEXT_LIGHT} end end end end function G.FUNCS.fn_pre_dollars_UI_set(e) local new_preview_text = "" local new_colour = nil if FN.PRE.data then if true and (FN.PRE.data.dollars.min ~= FN.PRE.data.dollars.max) then if e.config.id == "fn_pre_dollars_top" then new_preview_text = " " .. FN.PRE.get_sign_str(FN.PRE.data.dollars.max) .. FN.PRE.data.dollars.max new_colour = FN.PRE.get_dollar_colour(FN.PRE.data.dollars.max) elseif e.config.id == "fn_pre_dollars_bot" then new_preview_text = " " .. FN.PRE.get_sign_str(FN.PRE.data.dollars.min) .. FN.PRE.data.dollars.min new_colour = FN.PRE.get_dollar_colour(FN.PRE.data.dollars.min) end else if e.config.id == "fn_pre_dollars_top" then local _data = (G.SETTINGS.FN.show_min_max) and FN.PRE.data.dollars.min or FN.PRE.data.dollars.exact new_preview_text = " " .. FN.PRE.get_sign_str(_data) .. _data new_colour = FN.PRE.get_dollar_colour(_data) else new_preview_text = "" new_colour = FN.PRE.get_dollar_colour(0) end end else new_preview_text = " +??" new_colour = FN.PRE.get_dollar_colour(0) end if (not FN.PRE.text.dollars[e.config.id:sub(-3)]) or new_preview_text ~= FN.PRE.text.dollars[e.config.id:sub(-3)] then FN.PRE.text.dollars[e.config.id:sub(-3)] = new_preview_text e.config.object.colours = {new_colour} e.config.object:update_text() if not G.TAROT_INTERRUPT_PULSE then e.config.object:pulse(0.25) end end end