#💻・modding-dev
1 messages · Page 120 of 1
i ont know i just put it there because mail in rebate has its own thing
cant i just use mail_car anyway?
NIIIICE :D
im pretty sure it'll be fine
if you want it to always hit the same rank as mail-in rebate then yeah
does context.buying_card trigger when buying the joker it's attached to?
otherwise, i need a way of doing a calculation when it's instantiated
check the other center methods
hmm, none seem relevant
local rcc = reset_castle_card
function reset_castle_card()
rcc()
G.GAME.current_round.shuffle_card.rank = 'Ace'
local valid_shuffle_cards = {}
for k, v in ipairs(G.playing_cards) do
if v.ability.effect ~= 'Stone Card' then
if not SMODS.has_no_rank(v) then
valid_shuffle_cards[#valid_shuffle_cards+1] = v
end
end
end
if valid_shuffle_cards[1] then
local shuffle_card = pseudorandom_element(valid_shuffle_cards, pseudoseed('shuffle'..G.GAME.round_resets.ante))
G.GAME.current_round.shuffle_card.rank = shuffle_card.base.value
G.GAME.current_round.shuffle_card.id = shuffle_card.base.id
end
end
shamelessly copied from mail in rebate but here's a hook that gives you a specified shuffle card
also mail in rebate doesn't have a specified fallback id? that seems like a funny oversight
reset_game_globals:
so i've tried set_ability, but that doesn't seem to work
the ever helpful steamodded docs:
Buying Anything: sets
context.buying_card = trueandcontext.card = card(ie. a joker/consumable/card/voucher object)
Also, if buying a joker, that joker is evaluated with the same context as above.
ya, but spawning the joker doesn't work
i was creating a replica of steel joker with smods to test a particular function, but when compared to the vanilla steel joker, it always seemed to be 1 steel tally/card behind, is there a reason for this behaviour?
well no because you wouldn't be buying the joker?
exactly
so it doesn't trigger that
hmm, my attempted fix didn't work. here's one that i held in my save
and here's one i just spawned
if you're trying to do a calculation when it's instantiated, be aware that jokers can appear through other means (such as Judgement)
well that can easily be solved, the issue is I'm not sure it's the "proper" way of doing it
now im worrying about mod compat lol this first try at making this ive patched so many different locations
who would ever use multiple mods at the same time?!?!?! /j
most of them arent destructive but i do have to overwrite Card:get_chip_mult() like almost entirely
You may as well use the "update" function for such.
always off by 1? so it starts at -1?
if i turn a card into steel, steel joker lists 1.2, but the smods version lists it as 1.0, then when another chariot is played, steel joker lists 1.4, the smods version lists 1.2
doesn't seemt o update at the same time
when i had something like this i wrote my own function and then called it in loc_vars and calculate
"the smods version"? 🤔
i just copied the code of steel joker into the calculate function in a smods mod
sounds like it's not in the loc_vars
woo I'm working on a mod that does the exact same thing using some lovely patches and this hook
local old_Card_get_chip_mult = Card.get_chip_mult
function Card:get_chip_mult()
-- hook seems best for mod compat?
local mult = old_Card_get_chip_mult(self)
return mult + (self.ability.perma_mult or 0)
end
hokai, trying! can i see how you did that?
could be context issues too, since code in balatro's card.lua is so spread out lol
you couldve just missed something when copying steel joker
trueee
function lassCount()
local queens = 0
for k, v in ipairs(G.playing_cards or {}) do
if v:is_suit("Clubs") and v.base.id == 12 then
queens = queens + 1
end
end
return queens
end
SMODS.Joker({
key = "lass",
config = { xmult_per_queen = 1 },
loc_vars = function(self, info_queue, card)
return {
vars = { card and card.ability.xmult_per_queen or self.config.xmult_per_queen,
math.max(lassCount() * (card and card.ability.xmult_per_queen or self.config.xmult_per_queen), 1) }
}
end,
rarity = 3,
pos = { x = 1, y = 0 },
atlas = "jokers",
cost = 7,
blueprint_compat = true,
calculate = function(self, card, context)
if context.joker_main and lassCount() * card.ability.xmult_per_queen > 1 then
return {
message = localize({ type = "variable", key = "a_xmult", vars = { math.max(lassCount() * card.ability.xmult_per_queen, 1) } }),
Xmult_mod = math.max(lassCount() * card.ability.xmult_per_queen, 1),
}
end
end,
})
yeah i'll continue checking the code for anything missing
I definitely use update for one of my Jokers to animate it.
my version if you're interested
function count_wild_cards()
local wild_card_counter = 0
if G.playing_cards then
for _, v in pairs(G.playing_cards) do
if SMODS.has_enhancement(v, 'm_wild') then
wild_card_counter = wild_card_counter + 1
end
end
return wild_card_counter
end
return 0
end
SMODS.Joker {
key = "wildcardcharlie",
loc_vars = function(self, info_queue, card)
return { vars = {card.ability.extra.hand_gain,
card.ability.extra.discard_loss,
card.ability.extra.wild_card_ratio,
count_wild_cards()}}
end,
calculate = function(self, card, context)
if context.setting_blind and not (context.blueprint_card or self).getting_sliced then
local wild_card_counter = count_wild_cards()
if wild_card_counter > 0 then
...
end
end
end
}
omw to not make an api so 100 mods do their own variation instead (its kinda funny)
is it a good idea to annotate my lovely patches with where they act on, so future modders reading my code will have an easier time of it
ctrl+f
if you're talking about the # Card#start_dissolve card.lua:L2130 stuff then yeah I 100% suggest keeping that
yeah that's what i was referring to
ctrl+f is messy and annoying lol
yes i agree
hey quick question, what could cause that to happen with a joker that destroys itself?
The Toot
i wonder why match_indent isn't optional in lovely patches
it is very annoying to have to set it to true every single patch
straight tootin' it
just make it optional and default to true
its very funny to me that its an option at all
what do they think this is, pyhton
but yeah, is there someway to innately clear the event queue or am i going to have to hear the Pop sound 56 thousand times 
maybe try not playing 56 thousand pop sounds
you can clear the event queue instantly if you want?
not sure if I recommend doing that though
-# oh dear god i forgot to put a repetition check on my deletion code
...why 
could delete other stuff
Would mislead after any Balatro update though
because you're not the only person putting stuff in the event queue and clearing it deletes other people's events
m
btw out of curiosity how exactly do you hook (as in, where would be best place for code like this)
would it stop the "cash out" event queue? this happened as i won the blind, and a joker died
then remove the line number and just keep # Card:start_dissolve
probably
afaik it'll stop everything
send audio pls
i'll try, but OBS might cause my computer to explode
dont worry, you have 56 thousand chances to get it right

anywhere globally, this is the hooking part (nothing actually got changed about the function)
local old_Card_get_chip_mult = Card.get_chip_mult
function Card:get_chip_mult()
local mult = old_Card_get_chip_mult(self)
return mult
end
and this is the modification part
+ (self.ability.perma_mult or 0)
it sounds like a fucking woodpecker oml 😭
alright thanks! this is the first time ive actually tried to wrap my head around modding a game instead of encountering something slightly confusing and giving up lol
lol
you seem to be doing pretty well so far 👍
incredible
this was the only joker i forgot a repetition check on
every other single one that deletes itself has a repetition check

its crazy how much work you can get done when you come up with a really stupid joke idea and commit to it

the absolute destroyer of balatro
i presume reloading will just set me back to 56k events right
it creates the card of itself, then again, then again, then again, until it crashes the game
yea
my creation is complete. i have created 7-Up:trademark: joker
peak
and this is game-crashing joker card
oh hey diet speculo
oh is this like a fictional brands mod
i had this idea at like 3am one day im mostly just putting a bunch of random ideas into a mod. also 7up is a real brand
yea its a soda
hmm, my code still doesn't work.
calculate = function(self, card, context)
if context.joker_main then
if card.ability.extra.current_x_mult > 1 then
return {
message = localize { type = 'variable', key = 'a_xmult', vars = { card.ability.extra.current_x_mult } },
Xmult_mod = card.ability.extra.current_x_mult
}
end
end
if count_tarots() ~= card.ability.extra.tarots and not context.blueprint
then
card.ability.extra.tarots = count_tarots()
card.ability.extra.current_x_mult = 1 + (card.ability.extra.x_mult * card.ability.extra.tarots)
card_eval_status_text(
card,
"extra",
nil,
nil,
nil,
{ message = localize({ type = "variable", key = "a_xmult", vars = { card.ability.extra.current_x_mult } }) }
)
end
end
end```
same reason
new function:
function count_tarots()
local tarot_counter = 0
if context.selling_card then
tarot_counter = -1
end
if G.consumeables.cards then
for i = 1, #G.consumeables.cards, 1
do
if G.consumeables.cards[i].ability.set == "Tarot" then
tarot_counter = tarot_counter + 1
end
end
return tarot_counter
end
return 0
end```
ooh cool!!
okay well I can see an issue
that return should be at the bottom
can't you not execute code after a return?
what's with this card eval status text stuff at the bottom
what are you trying to do with this joker
why do you set tarot_counter to -1 if context.selling_card
did you mean tarot_counter = tarot_counter - 1? setting it to -1 doesn't seem like a good idea for a counter
i see it should be adding an amount of xmult based on the number of tarots
hihi!
so this card's ability is
Gives X0.75 Mult for each Tarot card in your consumable area
that's kinda busted i'd lower it to X0.25 or X0.2 but that's a balance thing not a code thing
that's tiny
also the context.selling_card is redundant if count_tarots will only be run during context.joker_main
you can hold at max 3 without negatives
steel joker gives 0.2X mult
also it's rare
madness gains X0.5 mult per small or big blind and destroys a joker every time it procs
it's also rare and has been nerfed already to not count boss blinds
ya but you can have more than 3 steels in your deck
eternal is NOT A DOWNSIDE
mine doesn't upgrade
selling a tarot decreases it
max of like 3.25X or something right
mhm
madness has always been uncommon right?
and it also hogs your consumable slots
it is time to play with the boss spawning logic
madness is rare...
0.75X * 3 = 2.25X
i thought it was uncommon
i have played with almost every other part of this game so far. let's touch that why don't we
it's always been rare
ya so X3.25, since it starts with 1 base
and if its not gaining? yeah seems fine
https://balatrogame.fandom.com/wiki/Madness
nope, uncommon
oh yeah true
what
okay, not so bad
^^
i thought it scaled 😭
i swear to god that thing was rare
I wonder if that was a balance change
doesn’t feel like an uncommon but yeah fair
anyway so
the bug i've encountered is the joker not updating when spawned
madness has been uncommon since 1.0.0
i can't testify about it when it's bought, since it's hard to test that
but spawning it doesn't keep it in the loop
guys I HAVE A PROBLEM NOW
i tried that, i can try it again?
unnecessary
just do this
calculate line goes here
card.ability.extra.xmult = 0
if context.joker_main then
card.ability.extra.xmult = (count_tarots() * card.ability.extra.increase
if card.ability.extra.xmult > 0 then
return {
message = localize { type = 'variable', key = 'a_xmult', vars = { card.ability.extra.current_x_mult } },
Xmult_mod = card.ability.extra.current_x_mult,
card = card
}
end
end
end
I might have the wrong number of ends here? but this should work.
and I didn't name the values properly, idk which is which
i'm not really sure what all the extra code was for
oh that just updates on locvars and calculate
probably smarter
that won't do what i want it to do
why not
i want it to keep tabs on it, so it can display its current value
debuff card during calculation 🔫🥺
setting it to 0 destroys that
like have a card do something and then debuff? tough luck
yeah
that's not needed for a joker that doesn't scale
do it again during locvars
I mean it does technically scale
my code works, just plug your function please
can't you count how many tarots you have in your consumable slots
that's... exactly what i'm doing
ho wdoes it work?
i mean manually
?
idk if i'll get shouted at for telling you to hook the update function for it
calculate the number when needed
you will, DON'T DO THAT
where is your code?
wait doesn't this just work if you add a card.ability.extra.tarots = count_tarots() into the loc vars
no?
it would
oh yeah right
but then i can't display the message easily
björk
wait what message
Ghost what exactly are you trying to do? I am late to this conversation
give xmult per held tarot card
this
i'll paste my code in its entirety, hold on
sure
function count_tarots()
local tarot_counter = 0
if context.selling_card then
tarot_counter = -1
end
if G.consumeables.cards then
for i = 1, #G.consumeables.cards, 1
do
if G.consumeables.cards[i].ability.set == "Tarot" then
tarot_counter = tarot_counter + 1
end
end
return tarot_counter
end
return 0
end
SMODS.Joker {
key = 'ghostjoker',
loc_txt = {
name = 'Ghost Joker',
text = {
"Gives {X:mult,C:white}X#1#{} Mult for",
"each {C:tarot}Tarot{} card in",
"your {C:attention}consumable{} area",
"{C:inactive}(Currently {X:mult,C:white}X#2#{C:inactive} Mult)"
}
},
config = { extra = { x_mult = 0.75, current_x_mult = 1, tarots = -1 } },
rarity = 3,
atlas = 'Phanta',
pos = { x = 0, y = 0 },
cost = 8,
loc_vars = function(self, info_queue, card)
return { vars = { card.ability.extra.x_mult, card.ability.extra.current_x_mult, card.ability.extra.tarots } }
end,
calculate = function(self, card, context)
if context.joker_main then
if card.ability.extra.current_x_mult > 1 then
return {
message = localize { type = 'variable', key = 'a_xmult', vars = { card.ability.extra.current_x_mult } },
Xmult_mod = card.ability.extra.current_x_mult
}
end
end
if count_tarots() ~= card.ability.extra.tarots and not context.blueprint
then
card.ability.extra.tarots = count_tarots()
card.ability.extra.current_x_mult = 1 + (card.ability.extra.x_mult * card.ability.extra.tarots)
card_eval_status_text(
card,
"extra",
nil,
nil,
nil,
{ message = localize({ type = "variable", key = "a_xmult", vars = { card.ability.extra.current_x_mult } }) }
)
end
end
end
}```
barely fits on discord lol
okay, do you have other things that need to count tarot cards?
the rogue value i gave tarots isn't too important
no
not right now, anyway
okay, gimme 2 mins to rewrite this for you then
eremel carry
i didn't think this one joker would have so much discussion around its implementation lol
would be awesome if Balatro had a function for when the list of consumables changes
this should work
wait hang on
okie
function count_tarots()
local tarot_counter = 0
if G.consumeables.cards then
for _, card in pairs(G.consumeables.cards) do
if card.ability.set == "Tarot" then
tarot_counter = tarot_counter + 1
end
end
end
return tarot_counter
end
SMODS.Joker {
key = 'ghostjoker',
loc_txt = {
name = 'Ghost Joker',
text = {
"Gives {X:mult,C:white}X#1#{} Mult for",
"each {C:tarot}Tarot{} card in",
"your {C:attention}consumable{} area",
"{C:inactive}(Currently {X:mult,C:white}X#2#{C:inactive} Mult)"
}
},
config = { extra = { x_mult = 0.75, base_x_mult = 1} },
rarity = 3,
atlas = 'Phanta',
pos = { x = 0, y = 0 },
cost = 8,
loc_vars = function(self, info_queue, card)
return { vars = { card.ability.extra.x_mult, card.ability.extra.x_mult + (count_tarots() * card.ability.extra.base_x_mult) } }
end,
calculate = function(self, card, context)
if context.joker_main then
if count_tarots() then
return {
message = localize { type = 'variable', key = 'a_xmult', vars = { card.ability.extra.x_mult + (count_tarots() * card.ability.extra.base_x_mult) } },
Xmult_mod = card.ability.extra.x_mult + (count_tarots() * card.ability.extra.base_x_mult)
}
end
end
end
}
that's better
pairs just iterates over a table
would this be useful for modders? made a small thing that lets you test enhancements + seals on decks. i coudl easily port it over to photoshop or other tools if better
ah kay :D
only thing missing RN are the FOJ stuff
could be cool
lpus idk if im using the X2 or X1 ones as a bsae lemme check
anims don't work but the "1 trigger only" bit works great!
it doesn't need to
i feel like it should though
No other jokers like that would display a message
hahahaha yes
constellation is specifically about using cards and gaining something though
constellation's functionality is completely different
fairfair
yours is just a passive counting effect, like stencil
well, okay
nvm im wrong its the x2 one
oh yeah, Stencil doesn't!
not for playing cards it doesn't
ya you're right it shouldn't have one then
digital, what are you trying to do?
hmm. game crashed immediately after loading the shop
the perpetrator is if context.selling_card then
code?
function count_tarots()
local tarot_counter = 0
if context.selling_card then
tarot_counter = -1
end
if G.consumeables.cards then
for _, card in pairs(G.consumeables.cards) do
if G.consumeables.cards[i].ability.set == "Tarot" then
tarot_counter = tarot_counter + 1
end
end
end
return tarot_counter
end```
yeah get rid of that shit at the top
but that'll break it
no it wont
that's not the code I sent you
oh, eek. i missed that line
lemme check everything again
okay seems good
bingo, that works! thank you so much!! :D
sorry for indirectly causing a fuss in here btw ^^
G.E_MANAGER:add_event(Event({trigger = 'after', delay = 0.4, func = function()
attention_text({
text = "Pair Unlocked",
scale = 1.3,
hold = 1.4,
major = used_tarot,
backdrop_colour = G.C.SECONDARY_SET.Tarot,
align = (G.STATE == G.STATES.TAROT_PACK or G.STATE == G.STATES.SPECTRAL_PACK) and 'tm' or 'cm',
offset = {x = 0, y = (G.STATE == G.STATES.TAROT_PACK or G.STATE == G.STATES.SPECTRAL_PACK) and -0.2 or 0},
silent = true
})
return true end }))
I just directly copied code from the source code to make this message play when a certain hand is scored, what does major do, what's the difference between align and offset, and what does silent do?
My goal is to align the message in the top left corner to be in the middle of the screen
major should reference what it aligns to
Is there any other format useful i should convert this to
I think you probably want G.play as the major here
since i know some dont use photoshop
Convert in what way
Like to export the sprite sheet to use for a mod?
Also @wintry solar unfortunately in_pool = false doesn't keep Enhancements out of Standard Packs
SMODS.Enhancement {
key = "twin_headed_snake",
loc_txt = {
name = "Twin-Headed Snake",
text = {
"{X:dark_edition,C:white}WISH{}",
"A wish to be understood and known."
}
},
atlas = 'Enhancements',
no_rank = true,
no_suit = true,
always_scores = true,
in_pool = false,
overrides_base_rank = true,
replace_base_card = true,
pos = {x=8, y=0}
}
I've seen some people use Krita or Aseprite
needs to be a function that returns false instead
I just convert them into pngs
How do you write that, and where do I put it?
fair but this is setup in layers for testin
in_pool = function(self)
return false
end```
got playing cards working but broke jokers in the process
Gotcha, thank you!
right now i have this
local hook3 = Card.calculate_joker
function Card:calculate_joker(context)
local ret, trig = hook3(self, context)
if G.GAME.blind.name == "bl_pencil_arrow" and (ret or trig) and not context.post_trigger then
SMODS.debuff_card(self, true, "bl_pencil_arrow")
G.GAME.blind.debuffs = G.GAME.blind.debuffs or {}
table.insert(G.GAME.blind.debuffs, self)
SMODS.recalc_debuff(self)
end
return ret, trig
end
local hook4 = eval_card
function eval_card(card, context)
local ret = hook4(card, context)
if G.GAME.blind.name == "bl_pencil_arrow" and ret and not context.post_trigger and not context.repetition_only and not context.discard and not context.pre_discard and not ret.jokers then
SMODS.debuff_card(card, true, "bl_pencil_arrow")
G.GAME.blind.debuffs = G.GAME.blind.debuffs or {}
table.insert(G.GAME.blind.debuffs, card)
SMODS.recalc_debuff(card)
end
return ret
end
local hook5 = G.FUNCS.evaluate_play
G.FUNCS.evaluate_play = function(e)
local ret = hook5(e)
for k, v in ipairs(G.GAME.blind.debuffs or {}) do
SMODS.debuff_card(v, false, "bl_pencil_arrow")
SMODS.recalc_debuff(v)
end
G.GAME.blind.debuffs = nil
return ret
end
hooyah!!




this is successfully drawing 2 cards but if it gets randomized via glitched or anything else it will display properly but not draw the randomized value
SMODS.Consumable{
key = 'LTMLaunchPad', -- key
set = 'LTMConsumableType', -- the set of the card
atlas = 'Jokers', -- atlas
pos = {x = 1, y = 4}, -- position in atlas
loc_txt = {
name = 'Launch Pad', -- name of the consumable
text = {
'Draw {C:attention}#1#{} additional cards'
}
},
config = {
extra = {
cards = 2, -- configurable value (number of cards to draw)
}
},
loc_vars = function(self, info_queue, center)
if center and center.ability and center.ability.extra then
return {vars = {center.ability.extra.cards}}
end
return {vars = {}}
end,
can_use = function(self, card)
if G and G.hand and G.hand.highlighted then
if #G.hand.highlighted >= 0 and #G.hand.highlighted <= self.config.extra.cards then
return true
end
end
return false
end,
use = function(self, card, area, copier)
if G and G.hand then
-- Use the Launch Pad to draw extra cards
G.FUNCS.draw_from_deck_to_hand(self.config.extra.cards)
end
end,
}
Because you're using self instead of card
the classic blunder
how does this ability sound, for P5 Joker?
Gains +2 Mult per hand played that is not your most played poker hand
too strong, too weak?
is this consecutive?
no
then rephrase as "[...] hand played that is not your most played poker hand"
fair, thanks :D
hmm, it feels a bit weak. what about +3?
that feels about on-par with other Uncommons to me
ah, thinking about Flash Card, maybe +2 is fine
if you have a lot of hands, it could easily surpass Flash Card
okay, i'm happy with that :D
I wonder if it would be possible to replace the background shader
peak
that was easier than expected! (though, i did reference Jimball's code)
OBELISK SUPPORT LFG
lol
obelisk's evil twin
quite so ^^
ah yes, consumeable
it's actually not "consumeable"s but it is just as bad
my challenge modifier's ID spells it correctly lmao
the source is... very interesting at times
I think in game.lua there are a couple comments saying "REMOVE THIS"
shoutouts to the top of challenges.lua that just has a test challenge with a bunch of random shit in it
yeah it's great
i'm trying to make the XXX will appear as the boss blind of Ante Y text coloured based on what blind it is prescribing
e.g. violet vessel is purple, amber acorn is orange, verdant leaf is green etc etc
all other boss blinds would just show as the usual {C:attention}
i just think that'd be a nice touch
...and i think i know exactly how to do it, and it involves a metatable.
just now taking a proper look at it i love that you get 5 eggs and egg is also a banned card. fire
oh i have a challenge that starts you with a card that is otherwise banned
"Escort Mission"
you gotta bring jimbo through ante 9, and if he gets sold or destroyed future blinds will become much harder
(the ID of the penalty modifier is a misnomer, a holdover from an earlier version, it's not 2x it's 1.7x)
What level of scaling is standard in the challenge?
HOLY FUCK IT WORKS
white stake
all challenges that do not explicitly say "required score scales faster" are white stake scaling
THE HOOK ON ANTE 6?!?!
😨
this challenge is impossible
YOU DO NOT WANT TO KNOW HOW THIS SHIT WORKS
The hook is rough when you skip the first 2 blinds
hey that's the debug challenge
what i did was make the text colour change based on which blind it is
fucking context-sensitive localisation colour tag
I understand what all the code is doing for the most part but I don’t think I could do this off of memory. I need to reference the YouTube tutorial I saw
this does BULLSHIT
that is what it does
i'm using a hidden locvar to essentially "smuggle" the blind's ID into the localisation system
all this for some colours.
Do the bosses actually appearing work?
I think you should require the jimbo to be kept then, x2 blind size doesn’t seem too impactful
4 joker slots
it's hard!
playtesters have told me the balancing was actually too harsh on 2x
what the fuck
fun fact
the colouring on that text
took me 2x longer to implement than the effect of the modifier
that is one modifier
whose colour changes based on what parameters are passed into it
isnt it a challenge?
yes!
due to the way challenge text works, each modifier's colouring is defined in the localisation table
i got around this by defining a custom colour tag whose meaning changes depending on what arguments are passed into the string
oh i thought moDifier was referring to the challenge
sorry
i'm using modifier to refer to custom rules
you know
like "you do not receive interest at the end of a blind" etc
I have a couple of effects that debuff Jokers, but if I somehow un-debuff them and debuff them again, they will stack their effects (happens with troubadour, merry andy, etc.) - any idea how I could avoid that?
I use G.GAME.blind:debuff_card for debuffing, and mod.content.set_debuff(card) for determining the outcome
Is from_debuff being passed to the add and remove methods?
How do I check that?
add and remove to_deck?
Yes
I didn't understand what are you reffering to, where those methods are?
I think they’re defined in the Card class no?
So in the Jokers
applying the effect twice might mean you add the Jokers back to the deck twice
I guess trying to print will tell you how many function calls there are even if this isn’t the issue
Maybe I still do not understand what are you trying to say but I'm talking about vanilla jokers, not custom ones
Me too
Most methods in SMODS.Joker come from vanilla
I know, but it feels like I'd need to take ownership of the vanilla jokers from that information
As I said you can just hook the method
Although the specific information might not be important, you could still learn how many function calls there are
Calling add_to_deck twice when buffing them might be the issue
I'm still very confused, oof
So you're suggesting that I hook the general function of adding the joker to deck while checking for the debuff, but there's no arguments to check to begin with, as mod.content.set_debuff(card) returns bool value
Not sure how that function is relevant
You want to check the arguments received by add_to_deck
so the argument and return of the other function isn’t relevant at the moment
I finally managed to spawn a standard card with a custom enhancement 🙏
@night pagoda
local card_add_to_deck_ref = Card.add_to_deck
Card:add_to_deck = function(from_debuff)
sendDebugMessage(tostring(from_debuf))
return card_add_to_deck_ref(from_debuff)
end
yup, already checking (but with lovely)
and same here
so far from_debuff is true for the add_to_deck
but does it print the correct number of times?
yes, only one time
It should be once per joker per buff
same with removing
WHY 😭
only one time, and true
You're missing colour = G.C.whatever in the config, you can probably do a colour = G.C.CLEAR if needed.
did you hook or did you patch it?
Try with a hook
If the function isn't being called twice I wonder if there's like a copy of it like in Steamodded or something
im trying to get deck creator to work, is it not like this?
All seems the same
where's mod.content.set_debuff(card) from?
maybe theres a bit i didnt fix tho
Do you take ownership of those Jokers?
Or do you have a mod that does?
oh, my bad, reffered to it in the wrong way, one sec
it'd be SMODS.current_mod.set_debuff(card)
Nope
How about trying to hook set_debuff then? To count how many times it's called?
Oh wait, nope, I forgot and I actually take ownership, but only for sprite
Let me check without the take_ownership
Also, another thought: try to print the ability.extra table of those Jokers, and maybe the center's table too
I removed the take ownership and it doesn't act weird now
Maybe it's not calling add_to_deck twice, but for like Troubador maybe it's accidentally changing the table
Since Steamodded and vanilla use slightly different representations, I'm wondering if by taking ownership it's changing how the function is called and it ends up changing a variable in the wrong table
or changing it twice but calling different functions
@night pagoda does your mod have a lower or higher priority than Steamodded?
I remember that when I was first implementing the thing it also had the exact same bug, but I didn't have any take_ownership before so it confused me greatly now
How do I check the priority?
I'm just wondering if the priority would make the print appear different
Because when you take ownership Steamodded might copy the object but into its own structure
So it may or may not copy the print
how would i combine these 2 messages into 1?
message = localize{type = 'variable', key = 'a_mult', vars = {card.ability.extra.mult}}```
```lua
message = localize{type = 'variable', key = 'a_chips', vars = {card.ability.extra.chips}}```
obviously i cant keep them separate since they use the same variable
say hello to the Crescent! brainstorming abilities, and thinking of Creates a Tarot card when a Planet card is sold (Must have room)
I observe no difference in the output messages when changing priority (tried -999 and 999)
and a duplicate index won't break anything?
ok, try to check what Troubador's tables look like
I mean the second index will overwrite the first
so if i just said
if context.joker_main then
return{
chip_mod = card.ability.extra.chips,
mult_mod = card.ability.extra.mult,
message = localize{type = 'variable', key = 'a_chips', vars = {card.ability.extra.chips}},
message = localize{type = 'variable', key = 'a_mult', vars = {card.ability.extra.mult}}
}
end
thatd be fine
no
What should I search for?
I meant in-game
yes, I printed the entire joker table with debugplus
Pastebin.com is the number one paste tool since 2002. Pastebin is a website where you can store text online for a set period of time.
was this while the bug was active?
yes, I brought the ownership changing back
you can see by the line 4171 that I changed atlas
The variables in the Joker seem normal
unless there's a hidden one somewhere that isn't displayed
tryna figure out two things here:
- how would i get this to actually work? i tested it with the provided test numbers (an Ace (14) and 2) and it doesnt give Xmult when they're scored
- how would i add more than 2 cards to the function pool?
honestly the idea of runs have set bosses is a cool idea
is the return correct?
kid named seeded run
I recall having this bug even before taking ownership, so I feel like it might be some other thing
i think it is, though idk
Xmult_mod = card.ability.extra.Xmult
I can try doing like a test modded joker that is supposed to work like troubadour but isn't from vanilla
hmmm
same for mult, and chips when in a return
is anything in smods consistent /s
tested that, still doesnt work
oh wait
your id code is only checking for aces, or 2’s
Try x_mult =.
ok ima try both and get back to this
It's not SMODS, it's certified
programming.
not Steamodded's fault
ok so neither x_mult or xmult_mod worked
im not at my computer right now, so i cant grab my code so gimme a second
Photograph uses x_mult
but is it placed somewhere else in its code or no
because i feel as if somethings misplaced or typed wrong
it looks fine to me
Try card = card, just in case, probably not it, though.
but yeah its definitely Xmult_mod, its what i use
was gonna mention that
try this return (but self should be card)
well, and the variable should be whatever you variable is naturally
im trying to find an actual example in my mod that has Xmult, that doesnt use joker_main
Is there an API method for centers that gets called when they're rearranged in a card area? Not seeing anything in the docs, but it seems like something I'd expect to exist already
I have one but I haven't ported it, it seems
I don't think Steamodded is that advanced no
https://github.com/MrSmoothieHuman1/MOARJokers/blob/main/MoarJokers.lua#L29 look at this though, the first joker (x-ray) - its doing the same thing you are doing, all you need to do is change it to Xmult
ok so the methods given kinda worked, but it produced a less than acceptable result
alright, ima check this out
That's far easier to deal with, what's in your config?
you didn't do what I said ;P
x_mult = card.ability.extra.Xmult
you used extra instead of extra.whatever
Check that extra = {Xmult = 3} or whatever number and not extra = {Xmult = {3}}.
i forgot, what did you say? im tryna keep track of all the suggestions
oop- nvm its working now
thanks guys
why does the game crash when i hover over the joker which i gave a custom rarity?
Any recommendations on how to ensure I get a new joker in a run to test it?
debugplus, so you can spawn jokers in 
use the debug menu of balatro itself

Had no idea balatro had a debug menu
well im using this cuz debugplus gives a crash when i try to use it
its a bit complicated to access
you should use debugplus though
i mean jeg
Is debugplus a mod?
ye
yeah
if you cant get it working though you can use the balatro version
i didnt even know it had its own debug menu
well its hidden in the game files
By wilsonthewolf? Want to make sure I have the right git repo
mhm
bump
so its seen
-# it might be useful to send the crash
oh sorry
all good
well first off your steamodded is severely out of date
like, over 2 months
i went to the github page
oh tyh its old
dang
i never checked if it got any updates
the answer to the question if steamodded got an update is almost always yes
except that one week
we don't talk about that one week
I just thought of an idea
Guillotine: if it’s the first hand of the round, destroy any played face cards, +10 mult per card destroyed
Not only is it good canio synergy, it can be used with pareidolia for deck thinning
Looking at the list of commands given by the help command in debugplus, I'm not seeing anything that would spawn in a joker. What am I missing?
Or do I just have to evaluate some code that populates it
While in a run, click 3 while hovering a joker in the collection
Other commands are shown if you hold tab
Wow, I guess their marketing really fell off at some point, lol, that people don't even know it's real
now uhh
how would i do the same with xchips
it does partially work when i swap out chip_mod or Xchips with chips, but i want it to act as an actual chip multiplier
???
can i get a sanity check
for k, v in pairs(G.GAME.hands) do
v.played = 0
v.played_this_round = 0
end```
does this reset the stats of every played hand
i cant wrap my head around pairs for some reason
i still need help here if possible please 🙏
yes
pairs iterates over all key-value pairs in a table, that's really all it does
idk how pools work
key = 'wip',
loc_txt = {
name = 'Wip',
},
badge_colour = '808080',
pools = {
["Joker"] = true,
},
}```
Is there a function like is_suit but for ranks?
I had in mind a joker that made 6s and 9s be the same rank
not exactly
only get_id()
you could always make both act as 9s
but you couldn't make them both 6s and 9s
But then it wouldn't work for both jokers that affect 6s and 9s
Which would make them both odd and even
Hmm might be more complicated than I thought
-# or I would need to take ownership of said jokers
quick question im going about making a custom deck texture and found a templates for the cards themselves but is there any for all the consumables?
hey gang my game keeps crashing and giving me this message
Oops! The game crashed:
main.lua:1841: stack overflow
Additional Context:
Balatro Version: 1.0.1n-FULL
Modded Version: 1.0.0~ALPHA-1304a-STEAMODDED
LÖVE Version: 11.5.0
Lovely Version: 0.6.0
Steamodded Mods:
1: Aurinko by jenwalter666 [ID: aurinko, Priority: 9.9e+301, Version: 0.4.7]
2: Cryptid by MathIsFun_, Cryptid and Balatro Discords [ID: Cryptid, Priority: 1e+299, Version: 0.5.3a, Uses Lovely]
3: Loop by jenwalter666 [ID: loop, Priority: -9999999999, Version: 0.1.1]
4: Talisman by MathIsFun_, Mathguy24, jenwalter666, cg-223 [ID: Talisman, Version: 2.0.3~dev, Uses Lovely]
Break Infinity: omeganum
5: Incantation by jenwalter666, MathIsFun_ [ID: incantation, Priority: 9e+301, Version: 0.5.4]
6: Jen's Library by jenwalter666 [ID: JenLib, Version: 0.2.2]
7: Nopeus by jenwalter666, stupxd [ID: nopeus, Version: 2.2.0]
Lovely Mods:
Stack Traceback
(3) global C function 'error'
(4) Lua field 'update' at file 'main.lua:1841'
Local variables:
dt = number: 0.0524234
success = boolean: false
msg = string: "stack overflow"
(5) Lua function '?' at file 'main.lua:931' (best guess)
(6) global C function 'xpcall'
(7) LÖVE function at file 'boot.lua:377' (best guess)
Local variables:
func = Lua function '?' (defined at line 902 of chunk main.lua)
inerror = boolean: true
deferErrhand = Lua function '(LÖVE Function)' (defined at line 348 of chunk [love "boot.lua"])
earlyinit = Lua function '(LÖVE Function)' (defined at line 355 of chunk [love "boot.lua"])
idk why it pasted like that sorry
reinstall cryptid from source
as in, not from the release but off the main branch on github
youre reffering to this correct?
(don't mind the branch not being main there, that's me messing with stuff)
how is refactor already 69 commits behind main 😭
modding is very fun at times
Did you ever find an answer to this? I too am trying to figure out how to do this and can't find anything
you have no idea the amount of work the "all X blinds are Y" took to implement
the game doesn't actually have the words "boss blind" anywhere on their own so these are custom localisation keys
Curious how you did it
I think doing it without the UI would be easy
it is
But integrating the UI and making sure nothing else breaks sounds hard
I have multi-blind Boss Blinds already thanks to L Corp
okay so you see this text
well there's already something notable here, in the colour key ns_blind which is entirely custom
I've done custom colors too
but like this is part of the logic for choosing what to display here
nonono
ns_blind isn't JUST a custom colour
ns_blind is a context-sensitive custom colour
the colour of the text changes based upon which blind it's referring to, all other blinds get the default attention colour, but finisher blinds get their colour
Couldn't you use the same control as Castle to control both color and context
so blue for cerulean, green for verdant violet for vessel etc
Maybe not the exact same control since that one might be hardcoded for suit colors
it almost definitely is
But it's context-sensitive too
no piece of text in the vanilla game does context-sensitive colouring for blinds
not text but it does do other nodes
But I think the harder part of the UI would be replacing the selection boxes
and the logic around them
maybe
but yes uh context-sensitive colouring aside
i manually pull the blind name out of localisation
and poke around by hand in the locvars for the string
Hey, quick hook-related question, sorry to bother-
So I'm still doin' that "get money for overkilling" voucher, and I thought of just adding onto the function that creates/check the flames already, which is the flames handler
So I have this, but with this active, it actually doesn't show the flames and I'm not quite sure why? I'm guessing it has something to do with fucking up the hook itself 😩 otherwise, it works as I intend to- just without the flames showing up anymore, which, y'know, isn't ideal
(ignore the "overkillactivate()" bit, i tried adding it but it just make the game crash)
oh that's an old style of hook
i would heavily recommend using lovely hooks rather than straight lua
it lets you inject code into the original function

Also I think you can do something like Mr. Bones to check for the result
lovely hooks are plenty good please use them
Lovely hooks are good when Lua hooks won't work
😭 Yeah I'm not sure what to do now fhdjsk
If you hook or patch at the end of the round you can do this
looks like that function is responsible for just updating the flame effect
it's a graphics routine
you shouldn't be relying on it for gameplay logic
check at the end of the round, yeah
So I replace the flame handler function in this stuff with end_round instead?
I suggest looking at end_round and seeing where you'd like your code to go
Then hooking or patching as appropriate
right
do you have a file for every patch you're doing?
there's around 2 or 3 in each file
but broadly yes
this is MUCH easier on my brain than one giant lovely.toml file
of course
one file for each type of change
that's fair, seems a bit OTT for my liking but I understand the reasoning
this mod does touch almost all of base balatro's gameplay features so there are a lot of patches
@wintry swallow how to replace the UI selection elements without breaking the game anyways
because my challenges remix vanilla balatro in some way each
you... you don't?
i'm not doing that
don't you just change what the blind is?
set round_resets.blinds
and then it draws the ui for you
[manifest]
version = "1.0.0"
priority = 0
# Game#start_run
[[patches]]
[patches.pattern]
target = "game.lua"
pattern = "self.GAME.selected_back:apply_to_run()"
position = "after"
payload = '''
self.GAME.modifiers.blind_forced = {}
'''
match_indent = true
# Game#start_run
[[patches]]
[patches.pattern]
target = "game.lua"
pattern = "self.GAME.joker_rate = 0"
position = "after"
payload = '''
elseif v.id == 'nsc_force_blinds' then
self.GAME.modifiers.blind_forced[v.class] = v.blind
'''
match_indent = true
# Game#start_run
[[patches]]
[patches.pattern]
target = "game.lua"
pattern = "self.GAME.round_resets.hands = self.GAME.starting_params.hands"
position = "before"
payload = '''
self.GAME.round_resets.blind_choices = {
Small = self.GAME.modifiers.blind_forced.Small or 'bl_small',
Big = self.GAME.modifiers.blind_forced.Big or 'bl_big'
}
'''
match_indent = true
# get_new_boss
[[patches]]
[patches.pattern]
target = 'functions/common_events.lua'
pattern = 'if G.FORCE_BOSS then return G.FORCE_BOSS end' # No, we're not using this.
position = 'after'
payload = '''
if G.GAME.modifiers.blind_forced.Boss then return G.GAME.modifiers.blind_forced.Boss end
'''
match_indent = true
that's all my blind patches
I thought it would break if you fought an unexpected Blind at the wrong time
it does not!
self.GAME.round_resets.blind_choices has the info
the game still counts the first two blinds as small and big respectively, even if they are not actually bl_small and bl_big
so you will only ante up after beating the third
have you tried reloading mid blind?
reloading? like
I've noticed that it restarts the ante and ahvent got round to fixing it yet
a save?
yeah
uh nevermind
nevermind don't listen to me
it DOES break if you do this, i need further patching to make it not do that
...should've seen that coming
nah it's weird jank coding in vanilla
I just havent had the time to investigate it properly
@wintry swallow Do you set it at the start of the game?
Yeah I was thinking of stapling it onto other effects
Which means it would need to update the UI at an arbitrary timing
i like making it explicit
then again i am doing challenges
anyway uh here's the logic for anteing up
right now this just checks "is the blind a boss blind"
now
i could fix this one of two ways
Like a Joker that changes the Small and Big Blinds to Boss Blinds
-
implementing it properly to check if you've just beaten the third blind of an ante, and only ante up then
-
or the extremely lazy and stupid way which is that when the challenge is active, we only ante up if you've just beaten a showdown blind
I think a simple, scalable solution is to check if every Blind has been defeated
skips?
it just needs to check if the final one has been defeated
If you know which one is the final one yeah
oh god this SUCKS i hate this codebase
yeah they're called small big and boss in the table iirc
what if you had more?
I would imagine that the last element would always be the final blind
WELL THE GAME'S GOTTA ORDER EM SOMEHOW NOW DOESNT IT
how to make the card that changes the name?
so the solution ehre is to stare at the code until something starts making sense
not really
but again
aha.
a h a .
i see now. naruhodo ne. okay this is probably it
just?? check if G.GAME.round_resets.blind_states.Boss == 'Defeated' and pray
you can do ```lua
for k, v in pairs(table) do
if not (v.defeated or v.skipped) then
return
end
end
ante_up()
I think this is more scalable
for example misprint has unique description that changes over time
outside of blind_states
it is not it, rereading the function
that gets set late
anyway, for all you enhancement calculating folk, better calc is merged https://github.com/Steamodded/smods/discussions/372
like, after we've already figured out if to ante up or not
what is G.GAME.round_resets.blind_states then
not useful
the problem isn't ante'ing up though
thank you saint eremel
it's restarting the ante on a reload right?
@wintry swallow looks like it has the information you need
how to make the card that changes the name?
the problem IS that we're anteing up whenever ANY boss blind is defeated
oh I see
including notably not the third one of the ante
And I'm saying to check every blind in the ante
instead of checking if the blind is a boss
thats a different problem to mine
This lists the state of every blind
wait what the fuck i'm fucking dumb
just check if Small == "Defeated" and Big == "Defeated"
...except
no
@sick sparrow would need a hook to replace the name with a DynaText object depending on what you mean
that uh still doesn't work
well, if neither are "Upcoming"
maybe ~= "Upcoming"
i.e. they're both defeated or skipped
the game never actually sets Small and Big to defeated, because they are not small or big blinds
-# or hidden but whatever
it sets it to current too
... right
okay let me see here
I wonder if there's an external tracker
wait no why is boss current
this seems like you would just want to make your own counter
that's beyond my ability
yeah definitely
that resets when you ante up
Possible states
me when both the boss and big blinds are current at the same time
and increments when you defeat or skip a blind
i am beginner modder to here
Try something easier first then
you could just check if big blind is either defeated or skipped
-# but why is it current
the game is not counting the big blind as the big blind because it's not a big blind
it's a boss blind
blind_states is unreliable here!!!!
that's what i've been trying to say this entire time
you have two options
this is me trying to modify the name
fix the blind_states, or make your own counter
and fixing blind_states requires keeping a counter anyway, BUT would ultimately be more robust
ah yeah my boss gets set to current using ortalab small/big blinds
you can't put variables in names last I've checked
Maybe see when blind_on_deck is updated and updated the tracker there
guys i need help
how do I copy an enhancement from a card to another without using copy_card?
See what Enhancement it has then apply it
how to make it possible?
what if it uses SMODS.Enhancement
create_card('Consumables', G.consumables,nil,nil,nil,nil, "c_eris") is this correct?
whoops
i don't think it's possible with just loc_vars, you'd need a generate_ui function to do that probably
(you can return different keys with loc_vars, but that implies making localization entries for every random combination of letters in the name)
how does that work with smods.enhancement? is there a function or something in the newer versions of steamodded to check what enhancement a card has (modded or not)?
card = create_card('Consumables', G.consumables,nil,nil,nil,nil, "c_eris")
card:add_to_deck()
```goes into wrong spot, into joker deck
vanilla create_card mentioned
I can tell you why and the reason is really stupid
it's actually G.consumeables
because 
Trying to create the tooltip for a normal celestial pack, anyone know what I'm doing wrong?
info_queue[#info_queue + 1] = { set = "Booster", key = "p_celestial_normal_1", specific_vars = { 1, 3 } }
return { vars = {} }
end```
does info_queue[#info_queue+1] = G.P_CENTERS.p_celestial_normal_1 work?
and i created a glitched card
card = create_card('Consumeables', G.consumeables,nil,nil,nil,nil, "c_eris")
card:set_eternal(true)
card:add_to_deck()
card:start_materialize()
oh you still need to emplace it into the cardarea
too simple
(though there's a util function that does all of this for you and provides a better interface)
Yes! Thanks ❤️
local card = SMODS.add_card {
area = G.consumeables,
key = 'c_eris',
stickers = { 'eternal' },
}
maybe we should throw a G.consumables = G.consumeables in
lol holy shit please
lmao
...uh, that's a weird offset.
Am I doing debuffing right if this happens?
not the most unreasonable suggestion tbh
i am the greatest programmer to ever fucking live
boss-blinds-on-any-blind now work completely perfectly
you are, this is weird vanilla behavior
oof, so I have to deal with it?
ok well aside from the weird visual bug where the boss blind says current even though it isn't but i'm sure that's a trivial fix
it's this issue basically
@wintry solar @zealous glen fixed both your bugs in one
quitting out and reloading works
give patch pls
here patch
- ejwu
checks out, he was helping trying to figure this out when this bug was happening long ago
NOTE: SKIPS DO NOT WORK YET
but for defeating blinds, this works
oh wait hold on this isn't the full patch
I get why this is bad, but it would still mean changing vanilla behavior
which we don't really like to have as part of steamodded
Are there any good sources for people who wanna learn modding?
could have smods config
I already kinda want some config for existing smods features
such as rebinding the M key to something else
https://github.com/steamodded/smods/wiki there's a good bit of docs in here
the game's code itself is largely undocumented, you kind of just have to read into it
that is planned and will maybe have a different default
would be interested to see a list of what you think would be good
[ best restart key
match_indent REALLY needs to be optional holy shit
i have lost so much time to it
there's little reason to use a value that ISN'T true so why doesn't it default to true
ah wait I'm stupid, there's already smods config
it is if you use a regex patch 🙃
-# by means of being unsupported
-# it just makes you work harder to match your indent
I agree it should be optional and default to true
how to convert played cards into wild cards?
check midas mask code
not included in steamodded joker example
-# modders these days can't deal with vanilla code anymore
yeah this patch is good
-# we've done too much abstraction team
lol you're saying this while i'm also here, just been digging through vanilla logic for the past hour and a half
smods code is so much easier to read than vanilla code
anyway here's your patch @wintry solar
I don't blame people
how to convert played cards into wild cards?
please like idk write my name somewhere in your mod if you use this
I'll take a look at it and see what Ineed to do
my situation is slightly different but this will be helpful
I did not intend to generalize
you're good
in my view that applies mostly to joker calculation
this does persist across saving and loading
oh yeah for sure
not everyone necessarily uh, wants to rewrite a core game system just to make a challenge possible to implement
yup this patch works in all situations
just tested it on red deck by quitting mid game and reloading after skipping one blind
What's the best way to read the game's source code?
and i still ante'd up at the end like i should
I didn't even have lovely to work with when I first made that stuff 💀
oh aure, speaking of blinds, I was wondering if it made sense to modify SMODS.Blind to allow for custom small and big blinds
you can just extract the game executable with 7-zip
yeah sure that would be nice
trouble with that is that if the small/big blinds aren't the vanilla bl_small and bl_big then uh
you hit the exact bug i wrote that patch to fix
yes which is why implementing it into smods properly would be a good idea
i'm trying to override the steel card tallying logic of steel joker with take_ownership. I overwrite the update method, my custom tally calculates correctly as expected, but then when scoring actually happens it seems to be using the original tallying behavior. Any sense of why this would happen? Does take_ownership not stop the base functionality from happening?
how to create entire tarot deck into G.consumeables?
wdym
from the fool to the world
do you give it a new calculate?
you'd have to list each one manually
I did, for debugging purposes, but I figured that shouldn't be necessary, right? I just need to change the value of card.ability.steel_tally and then the original calculate should be fine right?
unless you mean create a copy of every tarot card
do you want modded tarot cards to appear too?
but we don't know if the card's internal name has underscore of them
only 0~21 (base tarot cards)
then you have to add each one individually
uhh yeah I guess the original calculate should be fine
my mod currently is more lovely patch than challenge
theoretically you could just keep making random tarots until you make the fallback tarot twice
but we don't know if the card's internal name has underscore of them
for me it's day 2
they said no modded tarots
my modded tarot :(
also showman completely breaks this method
oh, right
oh, due to the way update functions work the original wil lstill be triggered
showman exists
why do you care?
what exactly are you changing?
I'm trying to override card.ability.steel_tally
where is that set?
if there's no way to stop original update from happening, I guess I could add a new field to steel joker called modded_steel_tally and make a new calculate function that uses that to bypass the base steel tally?
feels messy, but I guess it'd work
that's where steel joker does its tally, so I figure that's where I should be doing mine
you don't need to count the number every frame
don't I? since it's displayed when you hover steel joker
unless calculate is also called when you hover
see midas mask
without the source code? no way
7zip on main executable.
tfym without the source code
ae
the neat thing about balatro is that you can just have the source code
I do feel a bit uneasy when doing things a different way from vanilla, but I guess it's fair to say that balatro's code is only so good
look at card.lua in the base game code and then search for midas mask
please do not feel uneasy about doing things differently from vanilla, vanilla's codebase is uh
vanilla is not the gold standard
balatro's code was made for the purposes thunk needed it for
please look at how much shit i had to do to get a boss blind to behave when it wasn't in the boss slot
there's a reason we've established different standards in steamodded for certain things
the wof/ankh/ectoplasm/hex code O_O

