#💻・modding-dev
1 messages · Page 680 of 1
do booster packs need inverted weights?
iam trying some stuff out but do you know how to modify it? iam trying to make a joker that takes half of your cashout value and turns it into rerolls at the shop but im having trouble actually modifying the cashout value
Hook add_round_eval_row
why do you have to put everything in extra anyways?
does config = {} discard all but certain variables?
u dont need to put everything in extra
config transfers everything to the base ability table afaik
No, it's just that if you don't put it in extra you could override something that's already in card.ability, or it could be overridden by something else if it has the same key.
How do I make this not say error?
so prefixing would help me eitherway
done, had to take care of some feature parity first
misc = {
dictionary = {
your_pack_group_name = "bluh Pack" -- e.g. bstuck2_title (this key doesnt have a prefix, i added it to mine manually in the code)
}
}
or group_name in loc_txt if you use that
I did "k_voicebank_normal" and put that as the group name in the pack code
hm
ok thx
np
ive been trying to work with interest modification and i just cant wrap my head around this
SMODS.Joker {
key = "to_the_moon",
blueprint_compat = false,
rarity = 2,
cost = 5,
pos = { x = 8, y = 13 },
config = { extra = { interest = 1 } },
loc_vars = function(self, info_queue, card)
return { vars = { card.ability.extra.interest } }
end,
add_to_deck = function(self, card, from_debuff)
G.GAME.interest_amount = G.GAME.interest_amount + card.ability.extra.interest
end,
remove_from_deck = function(self, card, from_debuff)
G.GAME.interest_amount = G.GAME.interest_amount - card.ability.extra.interest
end
}
why does this line work
G.GAME.interest_amount = G.GAME.interest_amount + card.ability.extra.interest
assuming you have 25+ bucks isnt this just
5 = 5 + 1 ?
because of this
interest = 1
why does this accurately give 10
it's because interest_amount isn't what you think it is
it's interest per $5 you have, not the interest limit or a flat bonus
this exists because of green deck
okay so im trying to make a joker that removes your interest (that part was simple)
and converts the amount removed into bonus rerolls in the shop
and i just cant get it to work for the life of me
like the interest system is so confusing
like for example for testing i copied the to_the_moon code and added this line
SMODS.change_free_rerolls(G.GAME.interest_amount)
but that always gives 2 no matter what
again interest_amount isn't the amount of interest gained this round
you have to get the value at cash out and potentially save it for later use
but why does it work with the way to the moon is coded then
i just cant really wrap my brain around the fact that modifying
G.GAME.interest_amount = G.GAME.interest_amount + card.ability.extra.interest
interest like this accurately does anything
SMODS.change_free_rerolls(G.GAME.interest_amount)
while this doesnt
G.GAME.interest_amount is the amount of interest gained per 5 dollars. Increasing it will give you an extra dollar interest per $5 owned, but at no point does it change depending on the money you have
it's just a static value used in calculating interest
look where cashout happens, I can't tell you off the top of my head
honestly it feels like ive been running in circles with this entire idea
my original idea was a joker that took half your cashout value and transformed it into rerolls
realised that was too difficult for me, now this also seems way above my league
i would wait for this
https://github.com/Steamodded/smods/pull/1197
Adds context.modify_final_cashout which is a context that triggers after calc_dollar_bonus calls that allow for manipulation of the final cashout reward. Has context.dollars to represent the final ...
(or copy what that does)
oh thats actually interesting
found the issue
its group key not group name ;-;
oh yeah i would LOVE that context
its not the first time ive been cucked out of a cool idea by that just being impossible bcs im a very bad lua programmer
also completely seperate question
is there a context check for adding/redeeming a tag?
context.tag_added and context.tag_triggered
all the contexts are listed in the smods documentation on this page https://github.com/Steamodded/smods/wiki/Calculate-Functions
i tried to find that page but couldnt find it TY
How do vanilla jokers calculate their effect? Card:calculate_joker just scales it and G.P_CENTERS[key] table doesn't have calculate function
calculate_joker is more than just scaling
it should be in calculate_joker, some vanilla jokers are grouped by card.ability.effect (e.g. gluttonous, lusty, etc. all have "Suit Mult" in their effect so they aren't checked for by name in the function)
in fact, that's also where modded jokers get called too
what joker are you looking for?
Banner but preferably all of them, I want to hook calculate function to modify return value
no need to hook, use context.post_trigger instead
damn you beat me
Isn't it gonna trigger twice though? (First normal return, second modified)
I don't need just banner
no, i'm pretty sure you can directly modify context.other_ret during context.post_trigger to modify the return value
i have an info queue on a joker that creates investment tags (think diet cola)
why is the money nil lol
Can you show where the info_queue is defined?
loc_vars = function(self, info_queue, center)
info_queue[#info_queue + 1] = { key = 'tag_investment', set = 'Tag' }
return { vars = { localize { type = 'name_text', set = 'Tag', key = 'tag_investment' } } }
end,
i just looked at diet cola from vanillaremade bcs its my first time doing an info_queue for a tag
yeah i had that mistake before, doing it this way only returns the loctext entry but not the vars for it
For the info_queue part try info_queue[#info_queue + 1] = G.P_TAGS['tag_investment']
you can return the vars separately as specific_vars or yeah do that and actually return the centre
What's happening here is it's not getting the variables from investment tag in the info_queue and thus is showing nil
I think the method I gave handles that for you
the return is fine since you just want the name for it there
yeah that fixes it thx
hell yeah
ok so im making a variant of abstract joker that gives mult per negaitve joker held in hand
idk how to program it though
-- Negative Nancy
SMODS.Joker({
key = "nancy",
config = { extra = { mult = 3 } },
loc_txt = {
["name"] = "Negative Nancy",
["text"] = {
{
"{C:mult}+#1#{} Mult for each",
"{C:dark_edition}Negative{} {C:attention}Joker{}",
'{C:inactive}(Currently{} {C:mult}+#1#{} {C:inactive}Mult){}',
}
},
},
pos = { x = 9, y = 7 },
cost = 9,
rarity = 3,
blueprint_compat = true,
eternal_compat = true,
perishable_compat = true,
unlocked = true,
discovered = false,
atlas = "CustomJokers",
loc_vars = function(self, info_queue, card)
return { vars = { card.ability.extra.mult, card.ability.extra.mult * (G.jokers and #G.jokers.cards or 0) } }
end,
calculate = function(self, card, context)
if context.joker_main then
return {
mult = card.ability.extra.mult * #G.jokers.cards
}
end
end,
})
im currently looking over abstract's code
i also forgot what an info_queue was 😭
abstract can get away with just using #G.jokers.cards to get the count of all the jokers because that's just the length of the table. you'll have to loop over G.jokers.cards and increment a counter every time you find a negative joker
ah, so just like steel joker
ye
info_queue is used to add tooltips next to card descriptions (e.g. how steel joker displays the description for steel cards), in your case you'll probably want to use it to display negative's description
o i see
yeas
-- Negative Nancy
SMODS.Joker({
key = "nancy",
config = { extra = { mult = 0, mult_mod = 3 } },
loc_txt = {
["name"] = "Negative Nancy",
["text"] = {
{
"{C:mult}+#2#{} Mult for each",
"{C:dark_edition}Negative{} {C:attention}Joker{}",
'{C:inactive}(Currently{} {C:mult}+#1#{} {C:inactive}Mult){}',
}
},
},
pos = { x = 9, y = 7 },
cost = 9,
rarity = 3,
blueprint_compat = true,
eternal_compat = true,
perishable_compat = true,
unlocked = true,
discovered = false,
atlas = "CustomJokers",
loc_vars = function(self, info_queue, card)
info_queue[#info_queue + 1] = G.P_CENTERS.e_negative
local nega_tally = 0
if G.jokers.cards then
for _, joker in ipairs(G.jokers.cards) do
if SMODS.has_enhancement(jokers, 'e_negative') then nega_tally = nega_tally + 1 end
end
end
return { vars = { card.ability.extra.mult, 1 + card.ability.extra.mult_mod * nega_tally } }
end,
calculate = function(self, card, context)
if context.joker_main then
local nega_tally = 0
for _, joker in ipairs(G.jokers.cards) do
if SMODS.has_enhancement(jokers, 'e_negative') then nega_tally = nega_tally + 1 end
end
return {
Xmult = 1 + card.ability.extra.xmult * nega_tally,
}
end
end,
in_pool = function(self, args)
for _, joker in ipairs(G.jokers.cards or {}) do
if SMODS.has_enhancement(jokers, 'e_negative') then
return true
end
end
return false
end
})```
idk what im doing
nvm i think i ve figured it out
G.jokers and G.jokers.cards or {})
theorty
nvm
same error
updated code
-- Negative Nancy
SMODS.Joker({
key = "nancy",
config = { extra = { mult = 0, mult_mod = 3 } },
loc_txt = {
["name"] = "Negative Nancy",
["text"] = {
{
"{C:mult}+#2#{} Mult for each",
"{C:dark_edition}Negative{} {C:attention}Joker{}",
'{C:inactive}(Currently{} {C:mult}+#1#{} {C:inactive}Mult){}',
}
},
},
pos = { x = 9, y = 7 },
cost = 9,
rarity = 3,
blueprint_compat = true,
eternal_compat = true,
perishable_compat = true,
unlocked = true,
discovered = false,
atlas = "CustomJokers",
loc_vars = function(self, info_queue, card)
info_queue[#info_queue + 1] = G.P_CENTERS.e_negative
local nega_tally = 0
if G.jokers.cards then
for _, joker in ipairs(G.jokers and G.jokers.cards or {}) do
if SMODS.has_enhancement(jokers, 'e_negative') then nega_tally = nega_tally + 1 end
end
end
return { vars = { card.ability.extra.mult, 1 + card.ability.extra.mult_mod * nega_tally } }
end,
calculate = function(self, card, context)
if context.joker_main then
local nega_tally = 0
for _, joker in ipairs(G.jokers and G.jokers.cards or {}) do
if SMODS.has_enhancement(jokers, 'e_negative') then nega_tally = nega_tally + 1 end
end
return {
Xmult = 1 + card.ability.extra.xmult * nega_tally,
}
end
end,
in_pool = function(self, args)
for _, joker in ipairs(G.jokers and G.jokers.cards or {}) do
if SMODS.has_enhancement(jokers, 'e_negative') then
return true
end
end
return false
end
})```
Check for G.jokers and not G.jokers.cards in your loc_vars
oke
also you're using SMODS.has_enhancement to look for an edition which will not work
oops 😭 meant to do has_edition
I don't think that exists
You'd check for if joker.edition and joker.edition.negative
attempt to index global 'jokers' (a nil value)
yeas
Here's a loop I've used for checking negative before but you can pretty easily change it
-# in reality it's checking for negative, non-eternal, and not getting destroyed but I digress
i see :o
so i could rewrite it as smth like
acc wait
nvm i think ive slowly begun figuring it out
w
-- Negative Nancy
SMODS.Joker({
key = "nancy",
config = { extra = { mult = 0, mult_mod = 3 } },
loc_txt = {
["name"] = "Negative Nancy",
["text"] = {
{
"{C:mult}+#2#{} Mult for each",
"{C:dark_edition}Negative{} {C:attention}Joker{}",
'{C:inactive}(Currently{} {C:mult}+#1#{} {C:inactive}Mult){}',
}
},
},
pos = { x = 9, y = 7 },
cost = 9,
rarity = 3,
blueprint_compat = true,
eternal_compat = true,
perishable_compat = true,
unlocked = true,
discovered = false,
atlas = "CustomJokers",
loc_vars = function(self, info_queue, card)
info_queue[#info_queue + 1] = G.P_CENTERS.e_negative
local nega_tally = 0
if G.jokers then
for _, joker in ipairs(G.jokers and G.jokers.cards or {}) do
if joker.edition and joker.edition.key == "e_negative" then nega_tally = nega_tally + 1 end
end
end
return { vars = { card.ability.extra.mult, card.ability.extra.mult_mod * nega_tally } }
end,
calculate = function(self, card, context)
if context.joker_main then
local nega_tally = 0
for _, joker in ipairs(G.jokers) do
if joker.edition and joker.edition.key == "e_negative" then nega_tally = nega_tally + 1 end
end
return {
Xmult = 1 + card.ability.extra.xmult * nega_tally,
}
end
in_pool = function(self, args)
for _, joker in ipairs(G.jokers or {}) do
if joker.edition and joker.edition.key == "e_negative" then
return true
end
end
return false
end
end
})```
this might be it
im so fucking scared
ok uh nvm it just gives +1 mult no matter what, for some reason
-- Negative Nancy
SMODS.Joker({
key = "nancy",
config = { extra = { mult = 0, mult_mod = 4 } },
loc_txt = {
["name"] = "Negative Nancy",
["text"] = {
{
"{C:mult}+#2#{} Mult for each",
"{C:dark_edition}Negative{} {C:attention}Joker{}",
'{C:inactive}(Currently{} {C:mult}+#1#{} {C:inactive}Mult){}',
}
},
},
pos = { x = 9, y = 7 },
cost = 9,
rarity = 3,
blueprint_compat = true,
eternal_compat = true,
perishable_compat = true,
unlocked = true,
discovered = false,
atlas = "CustomJokers",
loc_vars = function(self, info_queue, card)
info_queue[#info_queue + 1] = G.P_CENTERS.e_negative
local nega_tally = 0
if G.jokers then
for _, joker in ipairs(G.jokers and G.jokers.cards or {}) do
if joker.edition and joker.edition.key == "e_negative" then nega_tally = nega_tally + 1 end
end
end
return { vars = { card.ability.extra.mult, card.ability.extra.mult_mod, card.ability.extra.mult_mod * nega_tally } }
end,
calculate = function(self, card, context)
if context.joker_main then
local nega_tally = 0
for _, joker in ipairs(G.jokers) do
if joker.edition and joker.edition.key == "e_negative" then nega_tally = nega_tally + 1 end
end
return {
mult = 1 + card.ability.extra.mult * nega_tally,
}
end
in_pool = function(self, args)
for _, joker in ipairs(G.jokers or {}) do
if joker.edition and joker.edition.key == "e_negative" then
return true
end
end
return false
end
end
})
i have no idea what im doing wrong
ipairs(G.jokers.cards), not G.jokers
your return math is using card.ability.extra.mult which does not exist
or I mean it's zero
oop
swashbucklers lied to me,,,
you do not need the 1 + either since this is +mult and not Xmult
mmmm,
return {
mult = card.ability.extra.mult + (card.ability.extra.mult_mod * nega_tally),
}
end
swashbucklers does ipairs(G.jokers and G.jokers.cards or {})
x and y or z is shorthand for if x then y else z end
and you don't need card.ability.extra.mult at all
In all honestly you don't even need that mult value here lmao
i dont? :o
lmfao brainwave
interessant
G.jokers has info about the cardarea itself; G.jokers.cards is a list of all the cards it contains, but G.jokers also has info about its position on screen, its card limit, etc
:o i see
and that applies for all cardareas
yayyy
mhm
kinda seems like a worse abstract joker rn tbh
yeas but i cant rly think of any other changes atm
it will probably remain pretty crappy but thats fine
+12
ok
does it work if it itself is negative tho
yea looping through G.jokers.cards includes the card currently being calculated
this + paper plane is fucking broken
im curious to see how this setup would go
+11 mult is no joke
what the fuck is this glitch
HELP
wait wrong channel
Ok how the fuck do probabilities work post-0711a? My prob is getting affected twice as much by Oops and presumably similar stuff (ie it's going from 1 in 2 without to 4 in 2 with)
Code is:
local new_num, new_denom = SMODS.get_probability_vars(center, G.GAME.probabilities.normal, center.ability.extra.odds, "acornling")
return {vars = {new_num, new_denom}}```
Even for the actual check later, or just in the loc vars?
yes even for the actual check
even for the check
👍
all the probability functions that smods provides already take G.GAME.probabilities.normal into account
What's happening is oops will still double that value, and when you pass it into the probability func, it doubles it again
and oops all 6s is the only thing that should be touching that variable directly
we are really on the same wavelength today lmao
fr lmao
It's still doubling it twice 
Did you save the change/start a new run?
new run shouldn't matter
I meant new copy of the joker
I didn't save the file.
lol
shouldn't matter either
Why not? Wouldn't an already created joker still hold the old functions?
the functions actually get copied from the center when a save is loaded apparently
(because they can't be saved i assume)
mm I see
yeah functions are unserializable
how do i make a new starting_param? i want my card sleeve to change the card limit in my custom area
directly changing the card limit in the apply function crashes the gam
thank you
<@&1133519078540185692>
are they starting to avoid the honeypot channel?
activity data seems to imply it posted in it 
or the honeypot bot had an outage
oh true
They got hit by the honeypot. It just takes a second to ban them and sometimes when the bot bans at the same time it posts the message doesn't get deleted
ahhh, the bot can't delete all their messages
that's a classic discord moment
A annoying one at that
Sure is
a bit ago someone gave me a github guide for making balatro mods, I forgot to bookmark it and lost it does anyone know what im talking about, if so can you find it???
Tried to make it so player cant draw anymore after first draw, but it just seems to not draw at all (which is a soft lock as well), what did i do wrong?
if context.setting_blind and not context.getting_sliced then
G.hand:change_size(card.ability.extra.draw)
G.GAME.round_resets.temp_handsize = (G.GAME.round_resets.temp_handsize or 0) + card.ability.extra.draw
end
if not context.first_hand_drawn then
if context.stay_flipped and context.from_area == G.deck and context.to_area == G.hand then
return {modify = {to_area = G.deck}}
end
end
thanks
Also I'm just gonna plug https://canary.discord.com/channels/1116389027176787968/1486847115127947374
context.first_hand_drawn and context.stay_flipped don't happen at the same time.
it says if not context.first_hand_drawn
Yes, they don't happen at the same time so it doesn't do anything.
ok well that doesn't help the issue
shall i make a flag?
that goes true at first hand drawn, and is itself a condition to stay flipped stuff?
oh wait no yea i see the issue now
yea do that with the flag, but instead of doing stuff with context.stay_flipped, just check context.drawing_cards and return { cards_to_draw = 0 }
unless you really really want to block all other things that draw additional cards, that's a cleaner way to do it
will try this, thanks a lot !
i think it's intuitive that stuff that draws extra cards overrides this effect anyway
will have to word it nicer then, for now it's RED saying "You can't draw anymore" with a dramatic tone
lol fair enough
tried like this, but doesnt prevent drawing it seemeth, i did it wrong it seemeth
if context.setting_blind and not context.getting_sliced then
G.hand:change_size(card.ability.extra.draw)
G.GAME.round_resets.temp_handsize = (G.GAME.round_resets.temp_handsize or 0) + card.ability.extra.draw
end
if context.first_hand_drawn then
card.ability.extra.flag = 1
end
if context.drawing_cards and context.from_area == G.deck and context.to_area == G.hand and card.ability.extra.flag == 1 then
return {cards_to_draw = 0}
end
if context.end_of_round then
card.ability.extra.flag = 0
end
oh drawing_cards doesn't really have any extra stuff, remove the from_area and to_area checks
everything else looks good i think
i use thunderstore for smods do you have any idea where that file would be
does that look up to date to you?
lmoa
i use imm and downloaded smods from inside imm
im geussing that uses the up to date one
last question,, is there a way to check if there are zero cards in hand? In order to make the player lose instead of soft lock him
i don't think imm would be able to install the old smods at all
what smods version do you have?
lemme check
i would hope the game can handle that automatically
but if you do need to do that, check if #G.hand.cards == 0
1.0.0-beta-1224a
actually just checked and enabled 1503a
yeah no issues there
ok but how would i access the smods folder since i need to do that in vs code
that's covered on the smods wiki I sent..
You can click the installed mods button in the mods list
that too
lmao ok sorry
???
but how would that help access the file in vs code
you can drag the folder into vscode
where is the confusion coming from
you can locate the file, and then open it in vs code
or just find it in the open dialog
i cant find the folder
there is literally a button in-game that takes you straight to the folder
You can click the installed mods button in the mods list
-# -Astra, 3 minutes ago
is there a line to make you lose the blind actually?
ohh ok thank you so much
G.STATE = G.STATES.GAME_OVER; G.STATE_COMPLETE = false
works in calculate?
calculate = function(self, card, context)
if #G.hand.cards == 0 then
G.STATE = G.STATES.GAME_OVER; G.STATE_COMPLETE = false
end
end
make the condition if context.hand_drawn and #G.hand.cards == 0, that way it doesn't check for an empty hand before it actually draws any cards or if you beat the blind with an empty hand
how do i put an image into the mod (the 1x and 2x thing)
thx!
added this, doesn't seem to work
How do you add a shadow to a G.UIT.T node?
config.shadow = true
Doesn't work
what if you add hover = true
nope
are you sure?
like this?
Hmm
What does the parent node read?
...huh
I'll have to double check my code
Because like I do set shadow on the text node but it doesnt show shadow
oh huh
appears the ui engine doesn't actually care about config.shadow on text nodes
it is but not always
when a glass card procs and is gonna be destroyed, that's detectable in context.remove_playing_cards right? like the cards will be in context.removed
yes
smods also adds .glass_trigger
as part of context?
as part of the card
ahhhhh
i assume that if i want to copy_card the destroyed card, i need to set .getting_sliced to nil?
i dont think copy_card copies getting_sliced
oh, nice
if i want to mess with the ability table of a copied card, i can do smth like
local copy = copy_card(cardHere ... )
copy.ability ... = whatever
``` ?
yeah
so to specifically pick up breaking glass cards, i have to look through context.removed in context.remove_playing_cards and check for context.removed[i].glass_trigger?
or am i overcomplicating that
yes
Whats wrong with my localization, my description nor name show up for my joker in game?
j_modprefix_jokerkey
your localization just has j_jokerkey. you need your mod prefix
^
you defined it in your mod's json file
sanity check, scale_card adds the scalar value to the ref value, right?
Yes.
by default
you can define an operation to make it do a different thing
Could I get a hand with something? I'm trying to make lucky cards of a specific rank have a 5x probability mod, I've been told a while ago that I should be looking for lucky_mult & lucky_money in context.mod_probability but I'm unsure what that means I have to do, and how I do it within the card scoring.
Here is my code so far which is detecting when a 'lucky number' is being played
calculate = function(self, card, context)
if context.individual and context.cardarea == G.play then
if context.other_card.ability.name == "Lucky Card" and context.other_card:get_id() == G.GAME.current_round.idol_card.id then
print("Lucky Number Found")
end
end
end
if context.mod_probability and (context.identifier == 'lucky_mult' or context.identifier == 'lucky_money') and context.trigger_obj:get_id() == id then
return { numerator = context.numerator * 5 }
end
or something
Cheers N' I'm pretty sure you're the person who gave me the context advice like 7 months ago lmao
what was glass's identifier? just its key?
glass
just glass, alright
you can find out any identifier by putting in if context.mod_probability then print(context.identifier) end and then making sure you have debugplus installed or digging through the lovely console
Would anyone mind to explain to me what exactly a github repository does, and how to use it corectly?
Because there was some documentation that recommended I use it, but I just tried to commit a change and it spammed my code with a bunch of bullshit, talking bout some unmerged changes
source control is black magic and i refuse to attempt to comprehend it 
helps you manage changes you make to your code
version control
good for collaborating with other coders
Yeah i figured that much
A bunch of <<<<<<<<<<<<<<< and then random characters
maybe it was >>>>>>>>>>>
I dont remember
or are you talking about this
oh uhh
What the hell is that

Looks like the map to a metro station
commits
what repository were you trying to commit to
Mine? I guess
the unmerged changes stuff only pops up if the repository online has some changes that you didn't have on your local copy when you were trying to commit
would you mind linking it lol (if it's public)
It isnt
ah fair enough
it will be
I know literally nothing about github
it will be downloadable yes
that's the nature of open source
I dont want it downloadable until I have a release version of the mod
iirc you can keep your code private but won't be allowed to post it in modding
write it in your license or something
I mean i dont really care that much
Idk im on the fence about it
This is the first time the whole unmerged changes thing has happened so
but yeah it will be downloadable to anyone with repo access
i feel like this is not relevant to the conversation is it? meta was just asking to help with the problem
yes, but not because they want to post it
but because meta asked for them to share it now
Iet me look into it
Again idk how github works at all
Im toying with my github account
@red flower https://github.com/ITZ-Mekai-00/Risky-Balatro
When committing changes, should i be doing it pretty much any time I change a single thing
Or can i do it like a patch notes kinda thing, cuz thats what I've been doing
you should do it probably everytime you do a fix or add a feature
how are you using git?
from the console or some app?
which? the github one, vscode, another one?
vscode
Im using that to make the mod cause im familiar with it
Used it in high school
weird, it doesnt add that for me
you probably committed the merge conflict file or something
I might have just done my Mekai magic and caused a problem that noone knows how to
Anyways imma get back to making jokers. Im at like 5 out of 140 rn
Idr the exact joker count but im speeding through them
do you do it from here?
I think so
Also woah, how do you benefit from all those repositories
what does that do
this is the git extension, it commits to the repository
And listen, idk what im doing here. I write the funny words that put the little thing in my roguelike deck builder
it comes by default
yesd
just write a message and click commit and then push and it should work fine
yes
Thats what Ive been doing
wait push
hit the little traingle and hit commit and push?
Cuz Ive been hitting commit, and then a sync button pops up
idk probably a merge conflict file
w h a t
even after you push?
Little red triangled
when a merge conflict happens, git will create a copy of the file that contains both versions of the code, from your local side and from the repo side. the string of numbers and letters is the commit ID, and it's basically like this
<<<<<<<<< commit1
-- the version of the code from commit1
=======
-- the version of the code from commit2
>>>>>>>>> commit2
when the changes you have locally can't be automatically merged into the code on the repository, probably because you made too many changes in a single commit
when a merge conflict happens vscode should alert you and you should be able to open the merge conflict solver thingy
Okay there's my problem
I went like 3 hours without committing :3
you probably commited that file instead of solving the conflict
I think I did do that
yea that's most likely what happened lol
Ok got it
Ive got be more smarter written in my notes app
that should remind me
My risk of rain mod is coming to fruition...
this is what i mean
Ok then thats not what happened
Anywho, Ill figure it out eventually
This is my first programming project ever, that wasnt a school assignment
Does anyone know why this code would be crashing the game when I hover overtop of Lucky Cat??
calculate = function(self, card, context)
if context.mod_probability and (context.identifier == 'lucky_mult' or context.identifier == 'lucky_money') and context.trigger_obj:get_id() == G.GAME.current_round.idol_card.id then
return { numerator = context.numerator * 5 }
end
end
what is the crash
Because context.mod_probability is being called where context.trigger_obj is a fake card.
So you need Card.is(context.trigger_obj, Card)
Thanks :)
Does this replace context.trigger_obj?
no, just use it as a check before the get_id check you have
you need to make sure the trigger object is a real card before you do real card things to it
does SMODS.Back have an easy config setting for blacklisting jokers, vouchers, etc.?
You can add keys of items you wanna blacklist to G.GAME.banned_cards iirc
im guessing i can add those in the back's apply method?
yep
for some reason, this gave me an idea for a rene descartes joker that gains something whenever a card is checked to see if it is a card
G.GAME.banned_keys
and you do G.GAME.banned_keys[itemKey] = true
how do i exclude a joker from appearing in shop and how do i stop a joker appearing from a soul card
ty
can the eventmanager handle an event that just runs a loop until something is true and allows the rest of the game to continue on?
maybe using blocking = false, blockable = false ?
Yes, and you wouldn't use a loop, you would return false until the condition is true.
so i need it to run a loop to check if specific tags exist and then only perform its effect once none exist
oh! the functions automatically run each frame until they return true!
i totally misunderstood how these work
if i can get a confirmation, i will be at ease. will this event play each frame until there are no longer any tags with type == 'new_blind_choice'?
G.E_MANAGER:add_event(Event({ func = function()
local _stop = true
for k, v in ipairs(G.GAME.tags) do
if v.config.type == 'new_blind_choice' then _stop = false end
end
if _stop then
booster_tag = nil
busy_with_tag = nil
end
return _stop
return true end }))
you don't need the return true after return _stop
oh, oops, thanks
that might cause an error, actually
im also adding a break if it finds a tag, cuz no reason to keep searching. but this should be working now right?
i was about to mention that
but that should do that, yes - although that's probably going to cause the game to hang all the time there's a tag with the new_blind_choice type
trying to get booster pack tags to open automatically in the shop, but it was opening multiple at a time because closing a pack already tells the next one to open so it would open two at once and softlock the game x.x
i will check
not really...? have you ever used a double tag to get two tags that open packs? they get queued, and the first tag pops with it opening the pack, but the second tag sits there until you're done with the pack
if i make it trigger = 'after', delay = 1 will it only check once per second?
right, but i have code telling it to open a pack. so two are getting opened. one by my code and one by G.FUNCS.end_consumeable
that's when it starts, and then the false return makes it run again next frame
i think what you're looking for is for your event to repeat on a timer https://github.com/Steamodded/smods/wiki/Guide-‐-Event-Manager#repeating-on-a-timer
if it runs each frame without lag, it's fine. but i can try to space them out to reduce lag if needed
...you could try adding blocking = false to your event table in that case?
yeah i need to. now i just have a booster pack sitting on my screen lol
with blocking = false it works perfectly and no lag. so also no need to use a delay
thanks for your help
How do vanilla jokers get calculated?
in Card:calculate_joker, it's literally a giant bunch of if else statements that parse the effect based on context and joker name
It just scales the joker though
what?
For example it doesn't have j_joker
all of the different contexts are in the function. so jokers will get scaled in one context, but scored in another
Does something like decreasing ramen
Oh okay sorry, thanks
basic joker's scoring is on line 3980 of card.lua
Yes, it checks the name, not the key.
where do consumeable loc_vars get set in the vanilla code?
generate_card_ui
oh weird. there's a whole thing for this for the jokers in card.lua lol
Yeah the code is not really all that organized.
it feels very organized coming from hollow knight modding i will say
me: why is my code not affecting the hierophant
balatro: *has misspelled hierophant's key*
Caino and Selzer can relate.
Is there a function that's always called when changing the amount of money you have
ease_dollars()?
im pretty sure it's always used
does anyone need art for a mod if so id be happy to help
do custom jokers get added to the pool automatically or do i need to do that myself?
You gotta do it yourself
Check out the vanilla remade mod on GitHub it helped me a lot with that
looking at other mods that add jokers, they aren't including in_pool unless there's some special condition. that suggests to me that the default is return true
They do get added to the pool.
is there something randomly broken with c_lovers? im using some code to make the tarot cards update dynamically when they have editions. it's working perfectly for all the other tarot cards including the new ones ive added. but the exact same code, copy and pasted from a working tarot card, just isn't affecting lovers
loc_vars = function(self, info_queue, card)
if card.edition and card.edition.negative then
return { key = "j_busterb_roffle_heavy", vars = {card.ability.extra.xmultmod, card.ability.extra.xmult}}
else
return { vars = {card.ability.extra.xmultmod, card.ability.extra.xmult}}
end
end,
``` dawg i couldn't figure out how to get it to display roffle heavy when roffle is negative
Code?
SMODS.Consumable:take_ownership('c_lovers', -- object key (class prefix not required)
{ -- table of properties to change from the existing object
config = {mod_conv = 'm_wild', max_highlighted = 2},
loc_vars = function(self, info_queue, card)
info_queue[#info_queue + 1] = G.P_CENTERS[card.ability.mod_conv]
info_queue[#info_queue + 1] = G.P_CENTERS[card.ability.mod_conv]
card.ability.consumeable.max_highlighted = card.ability.max_highlighted + valueof(card.edition)
return {
vars = {
card.ability.consumeable.max_highlighted,
localize { type = 'name_text', set = 'Enhanced', key = card.ability.mod_conv },
}
}
end,
add_to_deck = function(self, card, from_debuff)
card.ability.consumeable = copy_table(card.ability.consumeable)
card.ability.consumeable.max_highlighted = card.ability.max_highlighted + valueof(card.edition)
end,
},
true -- silent | suppress mod badge
)
Is that key a valid entry in the localization?
valueof(edition) just returns 1 for foil and holo, and 2 for polychrome
wait, do put it in joker or other
i put it in joker
also yes that is the entry key
manlua return { key = card.edition and card.edition.negative and "j_busterb_roffle_heavy" or nil , vars = {card.ability.extra.xmultmod, card.ability.extra.xmult}}
still not working
it crash
i just found out the loc entry has no comma which is why it kept crashing
Working on an enhancement that gives +mult to playing cards if they score in the winning hand of a round, but I can't seem to have the effect be triggered by retrigger effects such as Red Seals. I'd just use end_of_round, but I couldn't get the played hand if I did
calculate = function(self, card, context)
if context.after and context.cardarea == G.hand and G.GAME.chips + SMODS.calculate_round_score() >= G.GAME.blind.chips then
local triggered = false
for _, v in ipairs(context.scoring_hand) do
if not v.debuff then
v.ability.perma_mult = (v.ability.perma_mult or 0) + card.ability.extra.mult
G.E_MANAGER:add_event(Event {
func = function()
v:juice_up()
return true
end
})
triggered = true
end
end
return triggered and {
message = localize('k_upgrade_ex'),
colour = G.C.MULT
}
end
end
can context.after also accept context.individual? that might get you there
Yes, playing cards are only retriggered in context.main_scoring and context.end_of_round
context.end_of_round and context.individual and context.cardarea == G.hand should be enough, no? it'll see the scored cards i think and will get repeated?
won't need a loop that way, can just apply the effect to context.other_card
from what i'm reading, context.end_of_round doesn't support G.play/unscored so that wouldn't offer much
context.individual isn't called on playing cards.
sad
how do you check if the player has a specific joker in their slots?
next(SMODS.find_card('jokerkey'))
this is what I'm trying to add it to, as a condition for this to activate if and only if you have a specific Joker
local create_card_ref = create_card
function create_card(_type, area, legendary, _rarity, skip_materialize, soulable, forced_key, key_append)
local card = create_card_ref(_type, area, legendary, _rarity, skip_materialize, soulable, forced_key, key_append)
if G.pack_cards and area == G.pack_cards then
if _type == "Base" then
local six_count = 0
for _, c in ipairs(G.pack_cards.cards) do
if c.base and c.base.id == 6 then
six_count = six_count + 1
end
end
if six_count < 3 then
card:set_base(G.P_CARDS["C_6"])
end
end
end
return card
end
if next(SMODS.find_card('jokerkey')) then
i've tried adding that with the key, but it doesn't activate when I have the joker
using the full key with the mod prefix?
You should be hooking CardArea:emplace
i did not add the prefix
!
what is the formatting for adding the prefix?
is it like prefix_key?
j_modprefix_key
the issue im running into is that this works on its own to modify standard packs to force them to contain 3 sixes:
local create_card_ref = create_card
function create_card(_type, area, legendary, _rarity, skip_materialize, soulable, forced_key, key_append)
local card = create_card_ref(_type, area, legendary, _rarity, skip_materialize, soulable, forced_key, key_append)
if G.pack_cards and area == G.pack_cards then
if _type == "Base" then
local six_count = 0
for _, c in ipairs(G.pack_cards.cards) do
if c.base and c.base.id == 6 then
six_count = six_count + 1
end
end
if six_count < 3 then
card:set_base(G.P_CARDS["C_6"])
end
end
end
return card
end
but when I add some kind of condition, it stops working, is this the correct place to put the if then check?
also i'm new to this so apologies if im doing something obviously wrong.
local create_card_ref = create_card
function create_card(_type, area, legendary, _rarity, skip_materialize, soulable, forced_key, key_append)
local card = create_card_ref(_type, area, legendary, _rarity, skip_materialize, soulable, forced_key, key_append)
if next(SMODS.find_card('j_nmb_triple_six')) then
if G.pack_cards and area == G.pack_cards then
if _type == "Base" then
local six_count = 0
for _, c in ipairs(G.pack_cards.cards) do
if c.base and c.base.id == 6 then
six_count = six_count + 1
end
end
if six_count < 3 then
card:set_base(G.P_CARDS["C_6"])
end
end
end
end
return card
end
try this
local emplace_ref = CardArea.emplace
function CardArea:emplace(card, location, stay_flipped)
emplace_ref(self, card, location, stay_flipped)
if self == G.pack_cards and G.STATE == G.STATES.STANDARD_PACK and next(SMODS.find_card('j_nmb_triple_six')) then
local six_count = 0
for _, c in ipairs(self.cards) do
if c.base and c.base.id == 6 then
six_count = six_count + 1
end
end
if six_count < 3 then
card:set_base(G.P_CARDS["C_6"])
end
end
end```
major differences being checking G.pack_cards after the card is already added and checking all the cards (both Base and Enhanced)
forgot to add the self to the ref call. fixed
its crashing saying length of field 'cards' is a nil value
It should be c:get_id() instead of c.base.id
info_queue is being mean again :(
Also G.STATE is never equal to G.STATES.STANDARD_PACK
Are you looking in the lovely dump?
Yes.
oh, my bad. i just comb the vanilla code when im trying to figure stuff out. i have a hard time finding things in SMODS
If you're looking at the vanilla code then you're not looking at the lovely dump.
sorry, what's the lovely dump then?
Mods/lovely/dump
huh. thanks
No, it should be c:get_id()
oh mb
mistyped
and before i was doing if G.pack_cards and area == G.pack_cards. this is doing G.STATE = G.STATES.STANDARD_PACK, which is not correct?
How do you info_queue an Edition that has two states (One for Jokers and one for Playing Cards), because it either just crashes (common_events::2975) or shows 'nil'
i assume the self == G.pack_cards is the same sort of thing?
listen to them. they have more experience than me :)
so what should I do with that line?
am i safe to just remove that condition?
i appreciate your help, I had no idea about emplace, and that has seemed to fix it
now i just wait until it breaks something :D
local oldcardareaemplace = CardArea.emplace
function CardArea:emplace(card, location, stay_flipped)
local g = oldcardareaemplace(self, card, location, stay_flipped)
if self == G.pack_cards and SMODS.OPENED_BOOSTER.config.center.kind == 'Standard' and next(SMODS.find_card('j_modprefix_key')) then
local sixes = {}
for k, v in pairs(self.cards) do
if v:get_id() == 6 then
table.insert(sixes, v)
end
end
if #sixes < 3 then
SMODS.change_base(card, nil, '6')
end
end
return g
end
what would happen if i didnt check if a standard pack was being opened?
It would make every card in every booster pack have three sixes in it, even if the cards are not playing cards.
that is cursed
6 of tarot
this didn't end up working, but I took what you said to make the other code work and this version seems to be working:
local emplace_ref = CardArea.emplace
function CardArea:emplace(card, location, stay_flipped)
emplace_ref(self, card, location, stay_flipped)
if self == G.pack_cards and SMODS.OPENED_BOOSTER.config.center.kind == 'Standard' and next(SMODS.find_card('j_nmb_triplesix')) then
local six_count = 0
for _, c in ipairs(self.cards) do
if c.base and c:get_id() == 6 then
six_count = six_count + 1
end
end
if six_count < 3 then
card:set_base(G.P_CARDS["C_6"])
end
end
end
probably bc i didnt read it now that i look at it

forgot to replace the key
thank you everyone
Usually I ask and try to figure it out while waiting, but I have given up on figuring out info_queue problems.. So any help would be greatly appreciated 
Update: I'm using a Workaround for now, if anyone has any Idea on how to do this proper, let me know!
(Workaround: Add a Duplicate Translation-Entry to the "Other" table.)
uhhhh???? my stakes are in reverse order???? how on earth did that happen. it persists through restarts
feature now
when you select them on the bottom do they also go in reverse order?
also im just reading up on the hone voucher (making editions more common)
SMODS.Voucher {
key = 'hone',
pos = { x = 4, y = 0 },
config = { extra = { rate = 2 } },
loc_vars = function(self, info_queue, card)
return { vars = { card.ability.extra.rate } }
end,
redeem = function(self, card)
G.E_MANAGER:add_event(Event({
func = function()
G.GAME.edition_rate = card.ability.extra.rate
return true
end
}))
end
}
is there a variable like this one but for rarities instead?
G.GAME.edition_rate
Yes, G.GAME['modprefix_raritykeylowercase_mod']
is rarity key the key of the rarity youre trying to increase (example modprefix_RARE(idkthe rarity key for rare) will make rares more common
they do
if its only for one deck
PLEASE keep it and call it smth like the hallucination deck
it's all decks
aww
Yes, but it would be G.GAME['rare_mod']
i didn't do anything to make it happen as far as i can tell. it just suddenly happened after i finished a run
one of the few things i havent added to my mod is a new stake so i cant really help with that sadly
on this topic, is it possible to make legendaries appear in shops? (existing legendaries not new ones you create)
My mod takes ownership of non-white stakes to ensure they behave differently in story mode. The problem is that #1058 causes overridden stakes to re-evaluate their orders, shown in the following sn...
interesting. i wonder why it only just now started happening to me. i didn't update any other mods today. strange. oh well. hopefully it's fixed before long. it doesn't break the game at least
how do i create a random joker from a specific group of jokers, while still considering rarity?
make an objecttype with rarities
how do you create a random card from an object type? i may be stupit
SMODS.add_card { set = "objecttypekey" }
awesome thx
yes
i dunno how but cryptid and UT/DR do it
I'll go and have a peak overthere
does joker display have support for consumables or just jokers?
I accidentally messed up my Linux distro nearly lost everything I was working on lol

Thank God I pushed a couple commits to a repo for the mod
ok am i stupid why this isnt working
key is the card you want to add, maybe you meant key_append?
this is covered in the docs :3
home
oh what the fuck
known smods bug
idk
fuuck
should I add a check to make sure consumables isnt in there already or do you handle that later
there will be a release soon to fix it
it's handled
sick
doesnt particularly help seeing as i am making this to send to one friend as soon as possible
tell them to use dev smods?
the current workaround is to take ownership from gold stake downwards instead of red stake upwards
have you tried mintys solution
or do i have to do that myself
well, no 😭
pls do if you have the time
I mean, the logic isn't that hard to understand
I should probably mention now in the issue that the fix works

is there a way to have custom text in the rules section of a challenge
yes, it is explained in the smods.challenge docs
How should I display values for a card that copies multiple cards for JokerDisplay?
I am 95% sure you can do card:set_sprites() to fix it
Or reimplement this in a patch https://github.com/Steamodded/smods/commit/41983a160c2da18618e51a309068cc0bdd0298dd
i would just write the names
ight
does this fix also apply to other blank-enhancement situations
do yall know whats wrong with this localization that this is crashing
descriptions = {
Back = {
b_bstuff_none = {
name = "None Deck",
text = {
"Applies most {C:red}negative{}",
"{C:red}effects{} of each {C:blue}vanilla{} deck",
"most {C:green}positive effects{}",
"become {C:red}negative effects{}",
},
},
},
Sleeve = {
sleeve_bstuffs_snone = {
name = "None Sleeve",
text = {
"Applies most {C:red}negative{}",
"{C:red}effects{} of each {C:blue}vanilla{} deck",
"most {C:green}positive effects{}",
"become {C:red}negative effects{}",
}
}
}
}
}```
the key in your file is sleeve_bstuffs_snone. the key it's looking for is sleeve_bstuff_snone
FINALLY
Optic cards transmuting enhancements and rendering them blank was a longstanding bug from Oblivion's pre-alpha that still plagues the demo so this is huge
is there a way to put these lines below or no?
well it depends on when your issues started
it was confirmed to be an smods bug after a lot of testing
no
rip
it displayed the front properly before
only one line below for screen space reasons
ah
also jokerdisplay is not for explaining the effect of the card, just for reminders
you don't need to say that it copies the card
for that you read the description
oh okay
this should work then
i would still put them in two lines, what if they have long names
-# Ride The Bus & Shoot The Moon
frick
3/5
I'd give it a solid 3.1/5.1
omg, im so dumb, thank you
hi question, i have a joker that increases handsize but prevents drawing (after first hand drawn). Although, when cards are played, the space between the remaining cards isn't equalized (as in, cards stay compressed as if they were 20+ when there are only 4 remaining), anyone would happen to know? I can provide the code too
On the left what i have, on the right what i should have (with two stuntman for the example, but works also when deleting cards)
halfway fixed
that's expected behavior due to the spacing depending on the area's card limit and not the amount of actual cards in the area
otherwise things would look quite weird while drawing cards
could you elaborate? I'm not sure to understand
???
the difference between pic 1 and pic 2 is your hand size. When your hand size is 26, your hand cards are always aligned with the spacing that makes 26 cards fit in your hand
if this weren't the case, cards would shrink together while drawing them
no, if i make my handsize 26 with voucher, then delete three quarters, it spaces correctly
i think this is just jokerdisplay being a bad mod
wait it does? I might be misremembering then 🤔
wait a sec, providing proof
D:
jokerdisplay rework confirmed 
no it's like when eremel found out he was working on something because someone told him
you've been told you're working on a rework
i see
i think that was also me, actually
with locked & undiscovered sprite support for malverk
lmao
will you tell me I'm working on some wicked smods feature no one asked for next?
does any1 know how to actually debuff the cards real time. it just appears as debuffed but the cards still works like normal
use context.debuff_card
I am not working on this
i thought it was happening at some point 😭 #1300851004186820690 message
it might do
hi
we're simply trying to make simple face card skins (+ and ace) but it crashes the game. anyone has any idea what might be the problem? thank you
what does the crash log say
Your key starts with " but ends with ', it's an unclosed string as a result.
so I just need to edit it?
Your key starts with " but ends with ', it's an unclosed string as a result.
:3
aight lemme try, thanks guys
well it doesnt crash the game but I can't select the skins in the settings lol
During context.modify_scoring_hand, is there a way to tell if the card is currently scoring or unscoring?
Ah I just noticed there's context.scoring_hand so I guess I can just check if it's in it
yeah that works ok
im extremely new to to this and have been following a specific guide
but why wont this
like
work
in the guide the "main_file"
turned blue
i dont know if it has any semblance or whatever
i must do
you're probably missing a comma before this line
oooh
you are right
its so weird that its a normal comma
and not ;
like on cpp
thank yo
I mean a semicolon usually ends a statement, which this isn't
it's just object notation
javascript object notation for that matter, which doesn't make it better
but object notation
I added a mod icon to my texture pack (bottom right) and around half of the mods without an icon switched to mine. How do I fix this
whats the modicon atlas definition
--- STEAMODDED HEADER
--- MOD_NAME: Qwerry's Playing Cards
--- MOD_ID: qwerrycards
--- MOD_AUTHOR: [Qwerry]
--- MOD_DESCRIPTION: Playing cards if they were good
------------MOD CODE -------------------------
function SMODS.INIT.qwerrycards()
sendDebugMessage("Launching Qwerry's Playing Cards!")
local tpt_mod = SMODS.findModByID("qwerrycards")
local sprite_deck1 = SMODS.Sprite:new("cards_1", tpt_mod.path, "cards_1.png", 71, 95, "asset_atli")
local sprite_enhancers = SMODS.Sprite:new("centers", tpt_mod.path, "centers.png", 71, 95, "asset_atli")
local sprite_jkr = SMODS.Sprite:new("Joker", tpt_mod.path, "Joker.png", 71, 95, "asset_atli")
sprite_deck1:register()
sprite_enhancers:register()
sprite_jkr:register()
end
SMODS.Atlas({
key = "modicon",
path = "icon.png",
px = 32,
py = 32,
raw_key = true
})
------------MOD CODE END----------------------
remove raw_key = true
it works
0.9.8 code in the wild this is like finding a live dinosaur on the street
pls use this as a basis
https://github.com/Steamodded/examples/tree/master/Mods/DeckSkinTemplate
nothing changed when i removed raw_key
its just the icon, the rest works fine
this code will break eventually
that code is deprecated and it's not guaranteed it will work forever and it might also be part of the issue
ill fix it when that happens
because this is extremely old functionality that is no longer updated
it doesn't show up at all?
did you save the files because it looks like you didn't in the screenshots
also the lovely.toml and the version.dll don't work like that they are not necessary
oh ok
THANK YOU SO MUCH
why is my 2 jokers not showing up though????? its meowl.lua and dorito.lua
its copied over from my main.lua in my other mod
how did you come up with that
my other mod was made from the visual studio code, ai
the icons issue is because you're using the old format, youre changing sprite data that doesn't belong to your mod because the old version didn't even have mod icons. do what you want but no one will support you with this if you insist on using this approach
hi, I've been tinkering more with the skin mod and now it displays, but as the default cards
this is my code after some fixing around
for the record, there's no hc version
you are removing the prfix from the atlas but then using it in the deckskin
prefix
so delte the prefix_config line?
yeah
hmm no idea
How should i format this effect for my flowerpot rework?
'Each scoring {C:attention}Wild Card{}',
'gives {C:chips}+#1#{} Chips per',
'{C:attention}Wild Card{} in played hand',
Basically, if you have 1 wild card, it'll give 15 chips, if you have 2, they both give 30, if you have 5, they all give 75
unlike yugioh cards, there's no "proper" way to phrase balatro effects. as long as its clear enough to be understood
I mean, yes, but i personally think this could be more clear
What would you have expected without being told this after reading it?
Huh, guess i did an alr job then
eh im guessing youre scared of it being read as "each scoring wild card gives #1# chips" or "gives #1# chips per wild card in played hand"
I mean, yesn't, you shouldn't have to check the description just to look at the number imo
spelling
when you removed prefix_config = {key = false}
the atlas key became kremboyz_kremboyz
but your deckskin looks for the key kremboyz_kremboys
played around with the code and the sprites appear, but the ace appeares as the jack for some reason
what's the sprite sheet look like
seems to be the pos style,
try setting it to
posStyle = 'suit'
cards became blank
'ranks'? 
yes it worked, thank you so much!
the ace is a bit jank but it's probably due to the location on the sprite itself
most likely
once again thank you very much
np
i've got a boss blind that randomises the rank of your played cards before scoring, but the chips they score that hand doesnt change with the new ranks
any way i could fix that?
dont do it in events
...it's possible to change the base amount of chips a card scores?
if you need the events for the animation you need to use a flag on the card for delaying the change_base animation but i dont recall what i was rn let me see if i can find it
hook card get_chip_bonus i think
well, at least it doesnt involve rewriting the entire thing
...wait a minute, i could have called :get_chip_bonus() the entire time to get the amount of chips the card will score? 
ok, you need to remove change_base from that event and put it outside and add true as the fourth argument
then replace it in the event with a call to v:set_sprites(nil, v.config.card)
what usually causes this crash?
last time i got it, it was because i was trying to serialise something that can't be serialized, which was a function in a G.GAME var 
Trying to save a function
Or something else that cant be serialized
ah
so i have this code
local main_menu_ref = Game.main_menu
Game.main_menu = function(change_context)
local ret = main_menu_ref(change_context)
for k, v in pairs(G.P_CENTERS) do
if v.set == "Joker" then
v.in_pool = v.in_pool or function()
return true
end
v.set_ability = v.set_ability or function(self, card, initial, delay_sprites) end
local my_key = v.key
local in_pool_ref = v.in_pool
v.in_pool = function()
local ret2 = in_pool_ref()
if not ret then return ret2 end
local blacklisted = false
if next(SMODS.find_card("j_mxfj_blacklisted")) then
for _, card in ipairs(SMODS.find_card("j_mxfj_blacklisted")) do
for _, vv in ipairs(card.ability.extra.blacklist or {}) do
if vv == my_key then return false end
end
end
end
return ret2
end
local set_ability_ref = v.set_ability
v.set_ability = function(self, card, initial, delay_sprites)
if v.rarity == 1 and next(SMODS.find_card("j_mxfj_blacklisted")) then
for _, card in ipairs(SMODS.find_card("j_mxfj_blacklisted")) do
if card.ability.extra.blacklist then card.ability.extra.blacklist[#card.ability.extra.blacklist + 1] = v end
end
end
return set_ability_ref(self, card, initial, delay_sprites)
end
end
end
return ret
end```
i'm assuming it's doing that somewhere
but i don't see where
so it does work now!
but the updated rank shows up before the card flips, which isnt game breaking, but it irritates me
did you move the event from where it was before?
wym? you only said to move the change_base outside an event, no?
yeah thats correct, idk why it's showing up earlier
weird...
how do I edit the name and the desc of a joker in a take ownership
cant
use a loc file
You need to override the localization entry in a locilation file
since localization file always takes priority over loc_txt
as far as i know
oh so by a patch
center there is not the center
it's the card
so it would be center.config.center.taw_data
this indeed can't be saved, but object references get turned into "MANUAL_REPLACE"; it doesn't crash the game
ghost was doing a thing that didn't need this anyway, but if one does, there are workarounds
if you implement a custom deck
and then beat it in an unseeded run for the first time, does the game store the fact that you beat a run with that deck anywhere? 
it doesn't seem to be stored anywhere; i was curious about how vanilla things worked where they had winning with a specific deck as an unlock condition, for example magic deck which needs a red deck win on any stake - which has the unlock condition that looks for type = 'win_deck', deck ='b_red'
it applies the stake sticker to the deck
in a check_for_unlock func, how can i capture a type == 'win_deck' check? is it in args? args.type == 'win_deck'?
ye.
this balance = true gets undone during scoring. is doing balance not intended during context.before? should i try during context.initial_scoring_step instead?
Changes to score during context.before is discarded pretty much.
context.initial_scoring_step is where scoring actually can be affected and onwards.
ahhh
Trying to make this seal only trigger on itself being destroyed
calculate = function(self,card,context)
if context.remove_playing_cards then
return {
card == self,
dollars = self.config.money
}
end
end
I've tried a few different ways but they either change nothing or stop it from working
If someone could give me some tips it would be much appreciated :)
have you tried
if context.remove_playing_cards then
for _, pcard in ipairs(context.removed) do
if pcard == card then
return {
dollars = self.config.money
}
end
end
end
idk if it will work because by this point the card might not be able to calculate
...is it possible to retrigger a probability check?
like if i do
if
context.pseudorandom_result
and not context.result
and context.trigger_obj.config.center.key == 'c_wheel_of_fortune'
-- insert other conditions here, like a probability check for example
then
return { repetitions = 1 }
end
could i retrigger a failed wheel of fortune?
or does the i am have stupid
which is more likely
You'd need to "re-use" the consumable.
how do I get the current edition of the card and its stickers too
of card or the card added?
the card's edition is stored in card.edition, but you'll need to nil check it because base is nil
stickers are weird.
to my understanding, card.ability['sticker key'] if the sticker is present
for card
yeah
do card:set_ability(key) then, it will keep everything else
oh ok
if i hook check_for_unlock and check for args.type == 'win_deck', how do i get the deck key?
i'm thinking that in those circumstances, G.GAME will always be a thing and therefore G.GAME.selected_back_key
How do you replace small and big blind with other custom blinds on certain stakes
Also, a sticker I have is meant to keep a joker debuffed until the end of the ante but it keeps removing the debuff when starting the next round
I'm trying to redo the skip tag so that it multiplies the next blind size by X0.5, and the game isn't recognizing chips. This worked for other effects within the blind but I guess it doesn't work for blind_on_deck. What do I do instead?
Are seals given by their names or keys?
Card:set_seal takes a key as an input.
Right I thought so
Damn I have no idea why this is crashing. Could I get a hand?
This is the code and crash error.
When specifically does it crash?
When using the spectral card
It should be modprefix_sealkey
_ _
Hook reset_blinds
How would that look
Like an example blind replacement
On an example stake
local oldresetblinds = reset_blinds
function reset_blinds()
local g = oldresetblinds()
G.GAME.round_resets.blind_choices.Small = 'bl_modprefix_key'
G.GAME.round_resets.blind_choices.Big = 'bl_modprefix_key2'
return g
end
So bl_modprefix_key is the blind you want to replace it with?
Alright
how would i go through the whole deck and get all the cards of a certain rank and apply a bonus to them when a consumable is used
local cards = {}
for k, v in pairs(G.playing_cards) do
if v:get_id() == id then
table.insert(cards, v)
end
end
for k, v in pairs(cards) do
-- Apply bonuses to the cards
end
thank you, that worked 🤑
I'm trying to redo the skip tag so that it multiplies the upcoming blind's size by X0.5, and the game isn't recognizing chips. This worked for other effects within the blind but I guess it doesn't work for blind_on_deck. What do I do instead?
G.GAME.blind_on_deck is a string of the current blind choice, also G.GAME.blind.chips doesn't exist until you enter the blind.
ok so instead of changing the current blind choice I should just change the chips when I enter the blind
that makes sense
ty
SMODS.calculate_effect({message = 'message'}, card)
thanksies
How would I make a consumable be able to show up multiple times without showman
in_pool = function(self, args) return true, {allow_duplicates = true} end
thank you
1.) this object inherents the same value across all other instances of it, how do I NOT make it do that
2.) How do i make it so it removes itself
(For context, the object is an extension of stickers that I based on pb's paperclips)
tjestuff.wp_stickers{
key = 'digitized_wpsticker',
atlas = 'wp_stickers_atlas',
pos = { x = 0, y = 0 },
badge_colour = G.C.BLUE,
config = {extra = {percentage = 200, increment = 50, tally = 0, tally_meet = 5}},
loc_vars = function(self, info_queue, card)
return {vars = {self.config.extra.percentage, self.config.extra.increment, self.config.extra.tally_meet, self.config.extra.tally_meet-self.config.extra.tally}}
end,
calculate = function(self, card, context)
if context.main_scoring and context.cardarea == G.play then
self.config.extra.tally = self.config.extra.tally+1
G.E_MANAGER:add_event(Event({
trigger = 'after',
delay = 0.5,
func = function()
play_sound('tje_digitized_pixel')
return true
end
}))
card_eval_status_text(card, 'extra', nil, nil, nil, {message = "Increased!", colour = G.C.BLUE})
self.config.extra.percentage = self.config.extra.percentage+self.config.extra.increment
local chipval = card:get_chip_bonus()
return { mult = -1*(math.abs(self.config.extra.percentage*(chipval/100)))}
end
if context.after and self.config.extra.tally == self.config.extra.tally_meet then
self = nil
G.E_MANAGER:add_event(Event({
trigger = 'after',
delay = 1,
func = function()
play_sound('tje_digitized_death')
return true
end
}))
end
end
}
You use card.ability[self.key] instead of self.config
Also you would do card:remove_sticker(self.key, true)
what happens if i wrote both the set and the soul set as the same consumable set?
well then it can only spawn from that single set
how did I create a card from a local table
What
Yo gang, is anyone familiar with how sets work? When you make a ConsumableType, does that stuff get added to Centers? Do you need to add anything to make create_card work?
No, you don't.
am i doing this right?lua G.C.EPILEPTIC = SMODS.Gradients["busterb_epileptic"]
doing right for what? there's no reason you would need your gradient in G.C
you can use it in localization as-is with {C:busterb_epileptic}
calculating messages
just use the gradients reference
G.C isn't special in any way, it just holds colour references. wherever you need the reference you can use the gradient directly
you can also hook SMODS.showman if that's easier for some reason (e.g. you want it to apply to an entire set instead of just one card)
if that function returns true (which, by default, happens iff showman is owned) then duplicate cards can appear
does anyone know how to do tags? i tried editing off of vanilla remade but it doesnt show up ingame
what's your code
the ones in vanillaremade are correct
are you loading this file?
that means the file doesnt exist
I think i messed up the patching
local function load_tags_file()
local mod_path = SMODS.current_mod.path
assert(SMODS.load_file("lunartag.lua"))()
end
is this right?
i think i got it we all good
guys what if
the health bar represents your unhealthy balatro addiction slowly eating away at you
That's what all the people say
so ive got no_faces and randomize_rank_suit with no_faces coming after, but its not even doing no_faces, how do i fix this
return { vars = { self.config.discards, self.config.extra_hand_bonus, self.config.extra_discard_bonus, self.config.no_interest, self.config.vouchers, self.config.consumables, self.config.consumable_slot, self.config.spectral_rate, self.config.hand_size, self.config.ante_scaling, self.config.randomize_rank_suit, self.config.no_faces , { localize { type = 'name_text', key = 'tag_double', set = 'Tag' } } } }
that's 52 cards, yea
no_faces isn't running at all
i thought maybe it was removing the face cards before randomizing the ranks but ig not
hmm
i do notice when looking thru vanilla remades code that it doesnt even return { vars = { self.config.no_faces } }
how do i replace textures in different mods?
Trying to make a joker that changes its card image based on the season. I have that taken care of, but the sprite is not setting when the joker is loaded. I had a method working before, but I want the current season to be permanent for whatever run that joker is currently in. Right now it always appears as summer
set_ability = function(self, card, initial, delay_sprites)
if initial then
local season = "summer"
if tonumber(os.date("%m")) <= 5 or tonumber(os.date("%m")) == 12 then
season = "winter"
else
season = "summer"
end
card.ability.extra.form = season
self:set_sprites(card)
end
end,
set_sprites = function(self, card, front)
if card.ability and card.ability.extra and card.ability.extra.form then
if card.ability.extra.season == "winter" then
card.children.center:set_sprite_pos({ x = 2, y = 11 })
else
card.children.center:set_sprite_pos({ x = 2, y = 8 })
end
end
end
variable issue oops. everything works as intended lol
Yes, because that value is true, try changing the config value to remove_faces
lemme try to get enough of the code where it can still be understood but also where i dont need nitro to send the message
key = "all",
-- atlas = 'decks',
pos = { x = 1, y = 0 },
config = {
discards = 1, -- red deck
dollars = 10, -- yellow deck
extra_hand_bonus = 2, -- green deck
extra_discard_bonus = 1, -- green deck
no_interest = true, -- green deck
vouchers = { 'v_crystal_ball', 'v_telescope', 'v_tarot_merchant', 'v_planet_merchant', 'v_overstock_norm' }, -- magic, nebula, zodiac deck
consumables = { 'c_fool', 'c_fool', 'c_hex' }, -- magic, ghost deck
consumable_slot = -1, -- nebula deck
spectral_rate = 2, -- ghost deck
randomize_rank_suit = true, -- erratic deck
no_faces = true, -- abandoned deck
hand_size = 2, -- painted deck
ante_scaling = 2, -- plasma deck
},
loc_vars = function(self, info_queue, back)
return { vars = { self.config.discards, self.config.dollars, self.config.extra_hand_bonus, self.config.extra_discard_bonus, self.config.no_interest, self.config.vouchers, self.config.consumables, self.config.consumable_slot, self.config.spectral_rate, self.config.hand_size, self.config.ante_scaling, self.config.randomize_rank_suit, self.config.no_faces, { localize { type = 'name_text', key = 'tag_double', set = 'Tag' } } } }
end,```
@daring fern
Are you trying to make a deck that is all vanilla decks combined?
yea thats what im trying to do
apply = function(self, back)
for k, v in pairs(G.P_CENTERS) do
if v.set == 'Back' and not v.original_mod then
SMODS.merge_defaults(self.config, v.config)
if v.name == 'Checkered Deck' then
Back.apply_to_run({effect = {config = {}, center = v}, name = v.name})
end
end
end
end
calculate = function(self, card, context)
local rerolls = card.ability.extra.rerolls
if context.end_of_round then
rerolls = rerolls + 2
end
end,
im probably missing something stupid but why doesnt this make the number go up(dont worry about the actual functionallity its more about the actual number bcs display still has it as its base value 0 at the end of the round)
rerolls is a number, not a reference
you're modifying the local variable, not the actual value
also do if context.end_of_round and context.main_eval then, otherwise it'll fling your rerolls to the sky
yeah i figured that out real quick XD
thx
calculate = function(self, card, context)
local rerolls = card.ability.extra.rerolls
if context.end_of_round and context.main_eval then
card.ability.extra.rerolls = rerolls + 2
end
if context.reroll_shop and not context.blueprint then
card.ability.extra.rerolls = card.ability.extra.rerolls - 1
end
end,
add_to_deck = function(self, card, from_debuff)
SMODS.change_free_rerolls(card.ability.extra.rerolls)
G.GAME.interest_amount = 0
G.E_MANAGER:add_event(Event({
func = function()
G.GAME.rare_mod = card.ability.extra.rate
return true
end
}))
end,
update = function(self, card, dt)
SMODS.change_free_rerolls(card.ability.extra.rerolls)
end
iam trying to make a joker that removes interest and gives you rerolls (and make rare jokers more common) however that rerolls dont really work accurately, upon testing the rerolls continued onto -5 on the joker. how do i make this more accurate?
from my understanding ive done everything correctly
yeah i figured out moving it to reroll_shop fixes it
but why would that approach be wrong?
what's the full ability
the amount of free rerolls every shop persists throughout the run
look at how Chaos The Clown works
how would i replace textures on other mods with my own?
and then put that function into a mod
this current code works fine (moving SMODS.change_free_rerolls(card.ability.extra.rerolls) that line down to reroll shop) except that it allows you to go 1 reroll in "debt" with the joker
right now i have this:
if SMODS.Mods["Bunco"] then
print("Debug Bunco")
SMODS.Atlas({
key = "bunc_bunco_cards",
path = "ExoticCards.png",
px = 71,
py = 95,
raw_key = true
})
SMODS.Atlas({
key = "bunc_bunco_cards_hc",
path = "ExoticCardsHC.png",
px = 71,
py = 95,
raw_key = true
})
end
if SMODS.Mods["Cryptid"] then
SMODS.Atlas({
key = "Cryptid_cry_misc",
path = "cry_misc.png",
px = 71,
py = 95,
raw_key = true
})
end
end```
but it doesnt work.
let me rephrase the effect, is it:
2 free rerolls per shop
Earn no interest
Rare jokers appear more often
or:
At end of round, gain 2 free rerolls
Earn no interest
Rare jokers appear more often
note that these two are different
oh i know
i was torn between that when making
but its the first one
but i think stacking is more fun
calculate = function(self, card, context)
local rerolls = card.ability.extra.rerolls
if context.end_of_round and context.main_eval then
card.ability.extra.rerolls = rerolls + 2
end
if context.reroll_shop and not context.blueprint then
card.ability.extra.rerolls = card.ability.extra.rerolls - 1
SMODS.change_free_rerolls(card.ability.extra.rerolls)
end
end,
add_to_deck = function(self, card, from_debuff)
SMODS.change_free_rerolls(card.ability.extra.rerolls)
G.GAME.interest_amount = 0
G.E_MANAGER:add_event(Event({
func = function()
G.GAME.rare_mod = card.ability.extra.rate
return true
end
}))
end,
currently the only bug is that you can sometimes go -1 reroll in debt which i find strange
add_to_deck = function(self, card, from_debuff)
SMODS.change_free_rerolls(card.ability.extra.rerolls)
G.GAME.rare_mod = G.GAME.rare_mod * card.ability.extra.rate
end,
remove_from_deck = function(self, card, from_debuff)
SMODS.change_free_rerolls(-card.ability.extra.rerolls)
G.GAME.rare_mod = G.GAME.rare_mod / card.ability.extra.rate
end,
the "earn no interest" part is a bit tricky so it's not yet included here
see i tried that for the rerolls but that doesnt work
i think the issue with is that add_to_deck runs once when obtained