#💻・modding-dev
1 messages · Page 148 of 1
damn i cant do that?
how do I make it so that the joker starts with a base X1 mult and then gains mult everytime the context is applied ?
config = { extra = { Xmult = 1, Xmult_mod = 0.1 } },
loc_vars = function(self, info_queue, card)
local glass_count = 0
(line 120) for _, v in pairs(G.playing_cards) do
if v.config.center == G.P_CENTERS.m_glass then glass_count = glass_count + 1
end
end
local current_multiplier = glass_count * card.ability.extra.Xmult_mod
return { vars = { current_multiplier } }
end,
calculate = function(self, card, context)
if context.joker_main then
local glass_count = 0
for _, v in pairs(G.playing_cards) do
if v.config.center == G.P_CENTERS.m_glass then
glass_count = glass_count + 1
end
end
local total_multiplier = glass_count * card.ability.extra.Xmult_mod
if total_multiplier > 0 then
return {
Xmult_mod = total_multiplier,
message = localize { type = 'variable', key = 'a_xmult', vars = { total_multiplier } },
card = card
}
end
end
end
}
why is it crashing when trying to read line 120 tho?
the locl var needs to be an self ability?
-# yes i just pushed a sizeable wiki update
hello chat, brand new to modding and lua here, how can i see exactly how vanilla's jokers are coded? I'm trying to copy some functionality from them. I've found the card.lua file but i cant really work out how it does what it does there (to be specific im trying to make a joker that retriggers played cards if you play a flush/flush variant)
you're looking at the right thing, you just don't know what to make of it. which is completely understandable because it's not very readable at all. Modded jokers often look quite a bit different, I'd recommend looking at the source of other mods rather than of the base game
the calculate function you'd want for this is actually pretty simple, it'd look something like
config = { extra = { repetitions = 1 } }, -- because we don't like to hardcode config values
calculate = function(self, card, context)
if context.repetition and context.cardarea == G.play and context.poker_hands['Flush'] then
return {
repetitions = card.ability.extra.repetitions,
message = localize('k_again_ex'),
card = card
}
end
end
can I not have 2 of these? it doesn't work
what doesn't work?
Hey, John smods
huh
I hate to do this, but I have no ideia what I'm doing wrong... Could you help me?
@frosty dock
what's the line it crashed on?
120
ahh thanks mate
i actually got really close to making this on my own but it was structured a bit differently and had context.scoring_name == Flush instead of context.poker_hands[Flush], thanks again a lot
okay
i have no idea what that means
i can't magically see the line numbers from a snippet of a file
its probably better to just post the actual line in question
like, copy paste, instead of snipping it
yeah that's extremely easy to miss
for _, v in pairs(G.playing_cards) do
if v.config.center == G.P_CENTERS.m_glass then glass_count = glass_count + 1
end
end
protip: you can surround your message with triple graves (`), found usually next to your 1 button in order to format it in a nice block.
like this
makes stuff easier to read (in my opinion)
uh that seems alright, what's the crash?
I did that in the snipped tho
fair
pairs is null... Imma send you a screen shot
i couldn't give you direct pointers but perhaps look at the drafting mod to see how that's done?
it crashes when I try to hover on the joker to see it's text
in that case that's more of tags being processed first
yeah you need a fallback
also uh
is this supposed to be a downside when you have 10 or less glass cards?
no? It should start at 1 and then increase by the xmult
trying to link the actual drafting mod i'm thinking of
you're not doing that though?
you're just passing the amount*0.1 as the mult
it's just a deck that gives you 10 "draft tags" at the start of the run
you'd want to add card.ability.extra.Xmult (or 1) to it
Can i just send you the file in your dm? I'm new at coding and it would be simpler I think
I just don't want to flood things here
its what the channel is (mostly) for
this is literally what the channel is for though
I'd rather not give DM support where it can be avoided
thats why we have modding-chat
fair enough
besides all you really need is to change glass_count * card.ability.extra.Xmult_mod to card.ability.extra.Xmult + glass_count * card.ability.extra.Xmult_mod
and check if G.playing_cards exists before trying to count glass cards
Imma try that but I'm not confident that's the solution (crashed)
yeah you might be missing a nested conditional
ive definitely had issues where the fix was to nest it under some conditional checking if the appropriate zone was active/exists
if self.ability.name == "Steel Joker" then
self.ability.steel_tally = 0
for k, v in pairs(G.playing_cards) do
if v.config.center == G.P_CENTERS.m_steel then self.ability.steel_tally = self.ability.steel_tally+1 end
end
end
it's used to count stone cards
the only thing is the 'k'
and Idk where it's used
loc_vars = function(self, info_queue, card)
local glass_count = 0
+ if G.playing_cards then
for _, v in pairs(G.playing_cards) do
if v.config.center == G.P_CENTERS.m_glass then glass_count = glass_count + 1
end
end
+ end
- local current_multiplier = glass_count * card.ability.extra.Xmult_mod
+ local current_multiplier = card.ability.extra.Xmult + glass_count * card.ability.extra.Xmult_mod
return { vars = { current_multiplier } }
end,
calculate = function(self, card, context)
if context.joker_main then
local glass_count = 0
for _, v in pairs(G.playing_cards) do
if v.config.center == G.P_CENTERS.m_glass then
glass_count = glass_count + 1
end
end
- local total_multiplier = glass_count * card.ability.extra.Xmult_mod
+ local total_multiplier = card.ability.extra.Xmult + glass_count * card.ability.extra.Xmult_mod
if total_multiplier > 0 then
return {
Xmult_mod = total_multiplier,
message = localize { type = 'variable', key = 'a_xmult', vars = { total_multiplier } },
card = card
}
end
end
end
}
you have a syntax error in a localization file
i should maybe change those to not use loadstring, the buffer names being the string length is ridiculous
it worked! why tho?
u just added a condicional
G.playing_cards only exists when you're in a run
ohhhhhhhh
so this just acts as a fallback for when you're not
I'm trying to add an upgrade feature but idk what I'm doing
can someone point me to the right direction please?

left
Adjust your "config" for this, but if you want the Joker to upgrade every time there's a 4-card hand played...
if context.before and #context.scoring_hand == 4 then
card.ability.extra.Xmult = card.ability.extra.Xmult + card.ability.extra.Xmult_gain
return { message = localize('k_upgrade_ex') }
end
if context.joker_main then
return {
card = card,
x_mult = card.ability.extra.Xmult
}
end
Also, loc_vars... that should have function(self, info_queue, card), really, not center.
the game crashes when I score
center is a cryptid moment

certified
oment
What sort of crash?
idk what could be causing it
I never heard of that mode lmao
reverting it back to center instead of card fixed it ._.
...huh, bizarre, but if it works, it works... just not the usual method on latest SMODS and what not.
well you'd need to change both the argument (self, info_queue, card) and its usage card.ability
I think you need to update galdur
shit good call thanks
when i won again on white stake it didn't update either
yeah sounds like outdated galdur
for /d %s in (*) do (cd %s && git pull)
if anyone wants to like
update all subfolders
am i stupid or can i not find the galdur repo
danke
np
oh hey it works now
thank you kind internet stranger
oh wait damn you're the actual john smods?
good shit, thanks for the smod
saint aure doing my work in troubleshooting galdur bugs
it's what they call me
no way
eremel is #2 contrib in smods by lines changed as well :p
-# you'll get there by commits eventually xd
does anybody know how to make the text box durations more streamlined? not sure why the first one has such a huge empty space after it
currently going crazy trying to make a custom card deck for someone but getting stuck, so trying my luck here 🙏
i have changed the suits to custom art with their own colours but i cant for the life of me find where in the .lua s the colour of the name of the suit that shows up when hovering over the playing card in hand is defined (in hex or from the global colour variables)
Anyone knows where i can find those? (i´ve found the one for the joker text already in the high contrast ones in global.lua but no dice for this one)
(pretty sure it´s not connected to the global colour variables, because i´ve already tried changing them but only got flashbanged by pastel colours in the main menu but the names were still the same ^^)
globals.lua
note you need to change the SO_1 (low contrast) and SO_2 (high contrast) and not the SUITS
also need to start a new run for the change to apply iirc
Aren't the SUITS one is just picking from SO_1 and SO_2
so you´re telling me i´ve looked around and changed around everything and this is probably the problem? i´ll try it out right now
HOLY SHIT I DIDN'T SEE YOUR NAME LMAO
thanks allot loll
yeah I'm pretty sure it is, I made this so the changes can apply whenever I want
right
thank you for saving my sanity (at least what is left ^^) mr. THE john smods o7
it really was just because it has to update with a new run
is there a way to break the text without having to rewrite all the {c}, {s:} etc?
you mean a newline right?
yh
no, these are per line
nobody knows?
seems like other events are happening at the same time (the cashout screen)
what trigger are you using?
a different question related to my own code - can I not return a message from context.remove_playing_cards?
'after'
either change the trigger or take a look at the blockable property (this might break when pausing the game using esc? idk you'll have to try it out)
ohhh i didnt think to use the blockable/blocking stuff
like is it intended this joker does not give any messages?
SMODS.Joker{
key = "joker2",
calculate = function(self, card, context)
if context.remove_playing_cards then
-- when a playing card gets removed
return {
message = "hi",
focus = card,
juice_card = card,
card = card,
-- yes, I overdid it with the ... = card
}
end
end
}
because for some reason this was missed in Card:use_consumeable
debugplus delete might just not be updated to use calculate_context
oh lol it works when using joker's delete haha
wait is it specifically hanged man? 🤔
oh also immolate
but familiar grim and incantation work for some reason
doesn't seem like they should
looks like SMODS takes ownership of familiar, grim and incantation and properly creates the right contexts
much better, although i would prefer if the joker didnt self destruct at the same time as the third dialogue box appears but for some reason there seems to be nothing i can do to stop that from happening
hmm, the messages still seem to be fighting some other events?
yeah, its strange
what approach did you use / what's your code looking like right now?
oh i just stacked a bunch of G.E_MANAGER:add_event on top of eachother which is probably not ideal
G.E_MANAGER:add_event(Event({
trigger = 'after',
delay = 0.0,
blocking = true,
func = function()
local text_key = 'erlking_end_1'
local align = nil
local loc_vars = {quip = true}
local n = 5
card:jcbb_add_speech_bubble(text_key,align,loc_vars)
card:jcbb_say_stuff(n,false)
play_sound('jcbb_letmeseeyoureyes')
return true,
G.E_MANAGER:add_event(Event({
trigger = 'after',
delay = 5,
blocking = true,
blockable = true,
func = function()
card:jcbb_remove_speech_bubble()
local text_key = 'erlking_end_2'
local align = nil
local loc_vars = {quip = true}
local n = 5
card:jcbb_add_speech_bubble(text_key,align,loc_vars)
card:jcbb_say_stuff(n,false)
return true,```
this is, like, only 20% of what the code is looking like
is there a way to do consecutive events without having to keep stacking G.E_MANAGER:add_event over and over?
wait so what did you change in your code since your first message?
i set blocking from false to true
Dumbass question here, but when the joker has triggered, how should I make it "shake"?
stack them outside
Ah okay. Did you try setting blockable to false?
oh false blockable might also work, but might also interfere with base game
i believe "juice_up" is the word for it
or uh, the code for making a joker wiggle when it triggers
how often
outside?
yeah in a row instead of containing one another
yeah i did
yeah I have no idea what will actually happen if you do this, but judging by the name and description I feel like it'll work
you could also
but make sure to test it
create an event that persists while the message exists
and when it dies it fires the destruction
but i see, i will try that
Doesn't he shake automatically if you return something on your joker? 🤔
hey, i'm new to balatro modding and need some help
Is there any way to add a different card back to different suits? i have these uno card numbers (sprites by @scarlet spire) and i want to add colored card backs to them instead of the default white one?
You can make the blank texture black and add a colorful part to each rank
how to make a joker add extra discards? Copying drunkard's code doesn't work
I'm the one doing the sprites for this, and i'll elaborate a little: having the colored part of the uno card as part of the sprite for the rank doesn't work too well, as most enhancements get covered up partially or almost entirely by those colored sections
so the idea is custom images for the enhancements themselves, so that they can be better incorporated into the designs of the uno cards
as an example
maybe the draw method for SMODS.Centers helps idk
or otherwise patching or hooking the draw pipeline
did you mean like this?
when i do it like this the joker plays the sound and instantly deletes itself with no animation
i'm just starting out with balatro modding and i can't really find anything on what or where this refers to
i'm also checking the steamodded github wiki i can't find SMODS.Center as a page (though i swear it once was)
any help with what this'd mean or how it'd work
That's true but there are now multiple pages under it
oh they got refactored? niiceeee
by the way why is it "Suit and Rank" instead of "Rank and Suit"?
Everything else is ordered alphabetically
except these two
good question
ah
probably because they're ordered that way in the code and I didn't think to change the order
is there any real reason those 2 should be on 1 page?
anyways there is also a draw method for Ranks and Suits under "Suit and Rank"
Unfortunately no one knows how draw works
because it's never explained
-# keku and someone else have supposedly used it
what you do is you look at how Card:draw works in vanilla code
-# keku's seemed slightly jank
I mean there can be differences
tbf I should document that properly
as exemplified by self and card confusion
hooking is a more general programming concept; patching refers to lovely patches
i see
also draw might need a better system if we don't want to have anything advanced having to rely on lovely patches
agreed
it's a 300 line function so I'm scared to refactor it completely but I might have to
I already looked through it, you'll be forced 😄
I figured
aure (and other Steamodded contributors) supporting an unpaid project like Atlas holding up the heavens
It doesn't particularly do much, it basically just "draws shadow, draws main sprite or card back, draws addition sprites/shaders".
but in the right order
And it's spaghetti
It is technically simple but alas even that can be made hard via LocAltHunk coding.
And also this isn't precise; I don't actually know what each line of code does.
might need it to look something like this API-wise
draw = {
{
type = 'after_center',
func = function() end
}
}
I'll have to
uhh brainstorm that one
is self, card, and layer each different arguments to pass through?
i'm looking at Card:draw in the vanilla code right now and i only see draw(layer) and i'm not sure what exactly that'd mean when calling the method when defining the suit
Card:draw(layer) is the same as Card.draw(self, layer)
from there, self gets turned into card because the method is now on a different object
how could i do this? nothing else has worked
and we insert another self to represent the object this is actually being called on
in this case, self is the suit and card is what's self in Card.draw originally
maybe a numerical priority system is more flexible
layer is practically meaningless anyways it's always either "both" or "card" because the function code only gets executed when it's one of those two.
It does functionally nothing different other than some shadow logic not being performed on the latter.
there's also "shadow" but I think it's never called with that?
yea
would probably be like
blahblah.EventManager(…Event(…
{…
blocking = false,
func = function()
local yap = is_talking(card)
if not yap then
card:die()
end
return not yap
end
…}
…)…)
I don't remember how to write or read
yap
can you repeat that louder I can't read
so would using it look something like this?
no
no you'd have to define it as a function like draw = function(self, card, layer) ... end
i see
yeah sorry
smh my head fake fan
i dont understand, sorry
you create an event except
- it's not blocking;
- it returns false while talking and both destroys the card returns true otherwise.
i dont know how to format it, though
what part
the is_talking part
last hand is tracked by blue seal
i don't think i'm doing this right
then you access the hand level repository
you need to actually draw something
it's not automatic
i tried doing something like this
ohhh i see
(hand index = 1 as a fail safe)
maybe this helps
what does context.scoring_name get set to, and does it get reset once the scoring is done
because i need it after scoring is done
yeah idk if this is gonna work, sorry
im just gonna keep the effect as it is and just forget about it
the name of the hand
does it reset
but again you can just use the one used by blue seal
imma be real i dont think i can, cus i dont see anything giving a level in the code that triggers blue seals
cus i need specifically the level
you don't get it
blue gets you the hand name
the hand name allows you to access the level
if you don't need the name you don't need context.scoring_name
although I'm confused about what name you need
since you want to remember the last hand but also forget it immediately
the level doesnt appear right away in the hand text thing
and i would like for it to
because thats how any hand level up looks like
it does?
yep
- chips
- mult
- level
is the order
one sec lemme get a vid
is that joker supposed to, like, level up whatever is in the consumables slot?
the old level appears before the new level takes its spot
so yes, it does in fact show the old level and i need to get that level
i dont know why it aint calling it in the first place
but it is
are you using update_hand_level?
which is used i believe in burnt joker
one sec
yeah level up hand is used in burnt joker
space joker is weird because it just uses return { level_up = true }
what about the code in card:use_consumeable and if ability set == planet
(currently not by my pc, let me take a moment to go to check)
my bad
I mixed up two function names in my head
But yes that's vanilla
so if it doesn't work properly the issue is elsewhere
why isn't the level there in the first place
good question.
but it works fine with the planet card he used just afterwards
The issue is that the level text isn't present when it first levels up
Then it remains there
i dont have another mod (that isnt purely visual i.e. friends of joker) and my only patch ive made doesnt change the relevant code, just adds a new context
my point is that the proper behavior is still present within the code that is running (as seen with the second video) but for some reason something else happens with the joker
No I don't think it's the Joker unless they're messing with the UI
(im not)
this is the code surrounding the level up
the two updates are to set the hand right and then reset it right after
(because it wasnt resetting either)
i dont know what to put in the level field in #1
have you tried commenting out the first line
right now you're overriding the current level text with an empty string
yes i know
look you don't need to manually update the hand text
thats a temporary thing
it happens without that level = '' line
good
no i mean
but with the line it'll make debugging confusing
the problem does
because the line also introduces the same issue
i was gonna put the level there
don't
the fact its blank is temporary
fix the actual issue ;P
alright.
how the fuck would i fix this.
because i dont know what the fucking issue is
question...how easy is it to make mods for balatro like making a simple skin mod for playing cards
again you're either changing the level line elsewhere, doing something in the wrong timing, or it's a Steamodded issue (@frosty dock)
there's an example mod for that you can just edit
put card eval status text outside
outside what.
outside return
got a link?
doesnt change anything, but thanks for tellin me that the return wasnt needed
Replace level = '' in the highlighted line with
level = G.GAME.hands[G.GAME.last_hand_played].level .
card
That shouldn't be necessary
it worked, thank you so much
lmao
Because it should display the level already
im done with this shit, this was very frustrating
You can read what I said too
I said it shouldn't be necessary because the game should already display the level
If it isn't displaying the level the bug is elsewhere
yeah well it fucking doesnt
i literally do not care
If your mod is broken it will break other people's mods
Or if it's Steamodded then the issue should be fixed globally
waaaa
also I had told you how to access the hand level
im muting this channel for a bit cya
What do you mean by that.
Vanilla displays the level of the hand before upgrading it
My modded Balatro also does it
Yeah, and?
If theirs doesn't, they either have introduced a bug, are doing something wrong, or it's a Steamodded issue
This fix sweeps the actual bug under the rug
This is what I screenshot from card.lua
If you read the chat
They said they had the issue prior to introducing that line
I told them to remove it
and they refused because "it was already causing problems before"
So you're not talking about level number anymore? Noted.
What does that mean
???????????
Their problem was the lack of level number
Because RadDealer's level number problem is solved... isn't it?
From their description, it seems like displaying the level number before a level up was broken, so this fix only addresses this specific instance instead of everything that can level up a hand
like other Jokers
Then what do you think RadDealer meant for "it worked"?
Do you understand this comment?
some1?
idk what else to tell you man
card:juice_up()
there's no way
They said they already didn't have the level before introducing that line with the empty string
Which shouldn't be the case because selecting a hand displays its level
So they broke something elsewhere
Maybe we resume this arguement when Rad Dealer is back so we can ask further question? I have sprites to draw.
I only really care if it's a Steamodded issue
and also because you both are criticizing me without reading the logs
the first thing I explained was how to retrieve the current hand level
then I told them to get rid of the line that removed the hand level
I don't think Rad Dealer's coming back lol
iunno man if it works it works
again, if the level wasn't there for this Joker, it won't be there for other Jokers
Then maybe report the issue to smods then?
i'm leaving
if they don't want their Joker to lack a level, why should other modded Jokers lack a level
I already pinged aure; but also, I don't know if it's a smods issue or their mod issue
I was trying to get them to figure it out
but they refused
id prefer we didnt
i hit ignore on them for a reason
After all, it's just a string.
It does not work how i want... If it ads +5 mult per card played, how can I make it shake when it gives +5 mult? that's the question
calculate = function(self, card, context)
if context.individual and context.cardarea == G.play then
if context.other_card:is_suit(G.GAME.current_round.joker_random_suit.suit) then
return {
Xmult_mod = card.ability.extra.Xmult,
message = localize { type = 'variable', key = 'a_xmult', vars = { card.ability.extra.Xmult } },
card = context.other_card,
+ card:juice_up(0.3, 0.4)
}
end
end
end
```should i put here?
not in return, before it
but in calc?
okk
not working... I want so everytime a card uses the ability the jk jumps
Can you use partial screenshot (Win+shift+S if on windows but I don't know which key on Mac) and then Ctrl+V onto this channel, please?
Sorry, just in case.
npp
The position looks fine to me, but I recommend juice_up(0.5,0.5) or just juice_up()
wait why are you even asking for a screenshot instead of the code
Because double triple ` makes indentations very weird on my pc & phone screen
It may look fine but it does not work so
yh
key = 'phj_sub25',
loc_txt = {
name = 'Sub25',
text = {
"{X:mult,C:white} X#1# {} Mult",
"if the sum of ranks",
"in the played hand",
"is lower than {C:attention}25{}",
}
},
config = { extra = { Xmult = 3 } },
rarity = 1,
atlas = 'Pokerholm',
pos = { x = 3, y = 0 },
cost = 5,
discovered = true,
loc_vars = function(self, info_queue, card)
return { vars = { card.ability.extra.Xmult } }
end,
calculate = function(self, card, context)
if context.before then
local rank_sum = 0
for k,v in ipairs(context.full_hand) do
rank_sum = rank_sum + v:get_id()
end
if rank_sum < 25 then
return {
Xmult = card.ability.extra.Xmult
}
end
end
end
}```
How'd I fix my joker to do what it says, boosting hands where the sum is under 25?
anyway your solution should be to just do
return {
x_mult = card.ability.extra.Xmult,
-- message is automatically created if right return values are used
card = card,
}
How would you like your Ace count as?
Face cards also
I think stone would count as 50 in this case
Then you have to add if v:get_id()==14 then rank_sum = rank_sum + 1 for Aces
that is also valid; how do I check for a stone card anyhow?
How would I go about making it so that only one instance of a Joker can be in my Joker hand at a time
And then elseif v:get_id()<=13 and v:get_id()>=10 then rank_sum = rank_sum + 10
yeah I don't think you should be using :get_id at that point
SMODS.has_enhancement(<card>, <enhancement>) so e.g. SMODS.has_enhancement(context.other_card, "m_stone") (apparently there aren't any docs for this function??)
SMODS.has_enhancement(v, "m_stone")
nice example haha
pretty sure <card>.base.nominal works better - you don't need the face card check, but you will still need to do the Ace and stone enhancement check
Yeah I'd recommend local _rank = v:get_id() right before the if stack.
interesting, hmhm
what do you mean
no copies?
No copies yeah
No copies, or if you manage to stumble upon a second Joker in a shop or booster packs you can't obtain it
Fun Fact:
:get_id() returns a random number between -100 to -1000000 when it's a stone card.
would that be v.base.nominal in this case?
for purchasing, isn't there a can_buy method or just something equivalent built into something else?
Is it possible to call the calculate function with a custom context
you could hook or patch into that
yes
Gotcha, but what about duplication prevention
wouldn't a better approach be to stop the duplicates showing up?
for copies it sounds a bit trickier. The dumb solution is to hook/patch create_card and not create anything if it tries to create the card
The smarter solution needs to know how copies are made
which I don't
Using calculate(self, card, custom context)
Then have in the function have if context.custom context then
End
No idea what way is more viable, there are quite a handful of ways to approach this
stopping duplicates from showing up is a lot easier
rather than preventing the player from buying a second copy if it does show up
Mmmm gotcha, where can I look into for this?
Duplicates already shouldn't show up except with Showman
But other mods could have similar effects so Idk
yeah just as confirmation, you know how showman works right jitjet?
similar to Showman
is there a way to reload the mods that's faster than quitting and reloading, or disabling and enabling the mod?
Oh yeah it's that one with a slightly confusing description
yeah that should be possible, my guess is you call SMODS.calculate_context( with your custom context
Jokers, etc etc may appear multiple times but not sure where it appears multiple times, shops, my Joker hand, booster packs, I dunno
yes
I think quitting and reloading is faster. Maybe there's a keybind to do that but I forget
holding M I think, unless it changed
M did it yes
I remember there were discussions about trying to do that faster but IIRC there wasn't another way to avoid side effects
reloading mods? yeah hard to keep track of
Yeah showman allows duplicates to show up in the shop, in booster packs, from random spawns (e.g. riff-raff or emperor etc)
I wish it's a different keybind because if I quit with VSC in the background I just get mmmmmmmmmmmmmmmmm
It worked like wonder...
i just do not know why
Trying to make a joker but it isnt exactly working, the joker doesnt even have a texture, name, or text, can someone point out whats breaking in my code because i cant figure it out
key = 'bubbleuniverse',
loc_text = {
name = 'Bubble Universe',
text = {
'{C:green}#1# in #7#{} chance for',
'one random {C:attention}Joker{} to become',
'{C:dark_edition}Negative{} when selecting {C:attention}Blind{}.'
}
},
blueprint_compat = true,
eternal_compat = true,
atlas = 'Jokers',
pos = {x = 1, y = 0},
rarity = 4,
cost = 20,
config = {extra = {odds = 2}},
loc_vars = function(self, info_queue, card)
return {vars = {(G.GAME.probabilities.normal or 1), card.ability.extra.odds}}
end,
calculate = function(self, card, context)
if context.cardarea == G.play and context.poker_hands['High Card'] then
if pseudorandom('seed') < G.GAME.probabilities.normal / card.ability.extra.odds then
local chosencard = pseudorandom_element(G.Jokers.cards, pseudoseed('seed')),
chosencard:set_edition('e_negative', true)
end
end
end
}```
I've asked Aure to change it to instant restart on ctrl + p or something I think? Since holding M just kinda sucks if you have any text input in the background (also it's slow)
Magic ✨
(the real reason is card = card lets steamodded know which card to jiggle - the playing card will automatically jiggle because of the individual context)
Anyway, what do you actually want your approach to be?
Stop duplicates of your joker from showing up (this happens by default), and also prevent duplicates of your joker when the player has showman?
it's loc_txt
Something like that yeah, just any means necessary of stopping cloning and not having more than one instance of that specific Joker
ooh, also stuff like invisible joker then 🤔
i figured but whats wrong with it, because it looks fine to me
Yeah Invisible Joker, Showman, some Spectral cards
no it's loc_txt instead of loc_text
And that's not accounting for any mods that add Jokers or other card types that clones Jokers
OMG im so blind
ive looked that part over so many times 😭 darned monkey brain
you're not the first haha, no worries
Hmmm, that might be a bit difficult to do "cleanly"
it's not that hard to delete 1 of the 2 if a second ever shows up, but it also kinda sucks for the player if it's not communicated clearly or when invisible joker randomly picks that one
To be honest, the reason why I want to do this is because my Joker has a soul sprite that can change, but someone pointed out that if there are multiples of that Joker, when a soul sprite changes for one Joker, others will change as well
So instead of delving deep into the very foundations of soul sprite rendering I thought it'd be easier to do duplication prevention
bunco
handles it
but I'm gonna guess it's just an extra variable
I can see why. You might want to think about what the best approach is. This seems difficult to pull off as well haha. Unfortunately I gotta go 😅
If you do want to do it this way, my suggestions would be to:
- use
in_poolto prevent any duplicates from showing up when using showman - use
add_to_deckto delete a secondary copy if it ever shows up - use some lovely patches to make sure invisible joker/ankh/etc cannot pick your joker nor can be used when it is your only joker
docs for those first 2 methods
I think making each one track the sprite separately is easier
probably
You mean like create multiple instances of Jokers for each change?
calculate = function(self, card, context)
if context.before then
local rank_sum = 0
for k,v in ipairs(context.full_hand) do
local _rank = v.base.nominal
if _rank > 13 then _rank = 1
elseif _rank >= 10 and _rank <= 13 then _rank = 10 end
if SMODS.has_enhancement(v, "m_stone") then _rank = 0 end
rank_sum = rank_sum + _rank
end
if rank_sum < 25 then
return {
Xmult = card.ability.extra.Xmult
}
end
end
end```
1) Not applying any xmult yet, am I in the right context?
2) Is there a way to print variables during this calculate code block?
3) How do you folks make this code block in discord have syntax highlighting?
specifically the tapes IIRC
- idk about the context but I'm not sure if it's the right
return sendDebugMessageorprint- ```lua
Man so I have to create 8 Jokers, 1 original, 7 with different soul sprites?
- … it might be
xmultorx_multorx_mult_modorxmult_mod
no
also digimon
ahhh Xmult_mod according to the examplejokersmod, I see
is there a way to encapsulate all the G.E_MANAGER:add_event instances into one block?
so that they play one after the other without letting outside events play, maybe? i dunno
Maybe
Although for talking Jokers I thought allowing other events to play was better
to not stall the game
but I had many, medium to long messages
How can I add a dependency to a SMOD mod?
i think normally that would be the case, yes, but the thing is that the events end with the joker's destruction
also Idk how's your implementation but to test if the speech bubble exists I'd do card.children.speech_bubble
for my implementation, based on Bunco's, based on vanilla, that should theoretically work
bunco uses speech bubbles?
Ahh, the right context for me seems to be context.final_scoring_step. I hadn't noticed it, but in context.before, the xmult WAS applying but it was being reset in the scoring phase
it did at some point
huh, interesting
thankss, where can I see a list of joker IDs ?
anyways mine
vanilla code
I think at the beginning of card thunk defines all loc_vars, or maybe wherever the data to build all prototypes is written
the latter should be complete
i searched through the archives and didn't find a file with the jokers bwahh
wooop found it
thank youu
can i add the rule of all chips requirement doubled for a challenge?
peak
Man ppl really thought of everything
Well time to steal ideas
It's no longer your ideas it's our ideas
I'm not even safe from accidentally implementing an existing vanilla Joker
me recreating Stone Joker

just recreate lusty joker 10 times

what's the key for a blue seal / seals in general, i need it for the infoqueue table thing
or window like these
iirc it's just blue
I recall aure mentioning that sometimes the correct way to write now should be providing the Center directly
according to this I'm right, unless I misunderstood them back when #1209564621644505158 message
and is blue a string?
I mean it's a ?
what else would it be?
I'm just sharing the "correct" way to add Jokers, Tags, and Seals according to aure
idk
which should be providing the "Center"(?)
seals do not have centers
nope
the code, to be safe
no yu'd need the object
I'd have to check what set seals are under
sorry I haven't done info queue stuff in a while
s'all good
does this bit from get_end_of_round_effect help?
no
as a type or like the table
for the table
The prototypes are in P_SEALS
I dunno if SMODS reuses it
actually those are the prototypes, not sure if there's another one
no I think it should be that one
like P_CENTERS
G.P_SEALS.Blue
not P_CENTERS, P_SEALS
not appearing now
hello again chat, why am i getting this error for global variable, it looks like its defined as local?
chosencard:set_edition('e_negative', true)```
mhm, ill try that
i changed it from the copy paste bc i thought it was an error
I wonder if the comma is important
seeing that most things are in all upper or all lower case
does it work without it
if that doesn't work try {key = "blue_seal", set = "Other"}
it worked
thanks to @topaz sun , I merged a localization file PR https://github.com/kcgidw/kcvanilla/pull/9 so now I have english and portuguese
I've pulled it in, what steps do i need to take to integrate it? Do I just need to delete my Jokers' hardcoded loc_txt's or smth? That didnt seem to work
Seals are inconsistent because thunk
seals my beloved
ah yes, it does, naturally
stops the crash at least
Wait is that pre or after better calc
I think if there's a Brazilian Portuguese localization file, and you change the language to Brazilian Portuguese, it should work
I think it interprets the line with the comma as a double variable assignment, so it tries to execute the second part before the variable is assigned
I get this when I try to play my custom challenge
yeah that's the thing - i tried switching and it just showed english still. so i thought, oh, maybe i delete the hardcoded loc_txt in my joker code... but that just makes it blank
local a, b = 1, 2
i see, thanks
oh you programmed the hosses texture pack
anyways see if the dump in the lovely folder helps identify the troubling line
thanksss I'll try to port it bwahh
And Legendere: #1251615229721186457 message
did you try to switch languages after deleting it?
I don't know how assigning a default loc_txt works; I only work through localization files
so switching languages works for me
hmmm, how can I call a custom joker into the challenge? naming the key I gave the joker doesn't seem to be working
So the Yu-Gi-Oh cards I'm making the pot of greed destroys itself when u use it
You think it should go back into the deck
did you use your mod's prefix
destroy the player for playing an illegal card
Lol

how is it formatted? thanks
Because it counts as discarding the card when I activate it so the game gives u 3 instead of just 2

for most things it's like… it's easier to do jokers: j_PREFIX_KEY
consumeables are c_PREFIX_KEY
etc.
probably the second one
right... that's why im trying to remove loc_txt, so im only working with loc files going forward. The loc file exists but if I remove loc_txt there's simply no text, not even for english
definitely missing smth here
have you added an English localization file already?
Hopefully nobody done this idea yet has anyone made a joker card that is a monkey that synergizes with the banana cards

yeah, it was included in the PR
might just wait for the guy to come online and see if he is able to see any translations
So is there any way to increase the limit of how many times a joker card can appear in ur pack?
Instead of just once
Some cards I'd like there to be more than just 1 of
hello chat room ! fully new to modding/lua stuff, please assume i don't know things unless stated otherwise. started looking into implementing the custom jokers i've concepted/sprited, and i've got some extremely basic stuff down (getting it to display the proper text and sprite, formatting the text correctly etc) but now i want to learn how to properly write abilities. i've bashed my head into a wall trying to find the proper things to reference, let alone properly write something functional for them, and atp i've conceded that if this little project is going to go anywhere i need Help help lmao.
so! the joker i've started with gains xmult every time a playing card or joker is destroyed, and every time a debuffed card gets "scored". what would be the first steps to getting that in place?
I don't know much about Lua either
¯_(ツ)_/¯
Can't learn if u never try
Lua is pretty simple to work with just need to get a general understanding of how it works with the game
Start with a basic card mod work ur way up
What I've been doing started with a basic card mod when I had more than 3 I tried making a booster now making custom cards
Everyone here is more happy to help if ur running into issues my dumbass was using the wrong prefix for a card and a single character typo was crashing my game

that's
what i was asking about LMAO
i've already tried, but i've only gotten so far by myself
Pretty sure @frosty dock might be able to help u with ur question tho
I'm still learning myself but sounds like something to ask them
Everyone been helping me might just be offline atm
if context.remove_playing_cards then for destroyed playing cards (remember to for i=1, #context.removed do [upgrade] end);
as for destroyed joker I'm afraid there isn't one simple solution (unless you temper with Card:start_dissolve function code).
i see i see, i'm def willing to ax the destroyed joker clause. glad to know i was on the right track
you can search for the joker names in the code to see how and where their abilities are implemented and patch those spots
e.g. if you want to make "destroy joker to the right" effect check dagger
search for the english name e.g. Ritual Dagger
i've tried that, to very poor results. a lot of it is utterly incomprehensible to me even if i do manage to find the joker i'm looking for
like, i need to stress that i am Brand Damn New to lua stuff and coding generally lol
which leads me into my next question. i had the context already figured out, but i am absolutely struggling to figure out how touse card.ability.extra.Xmult and Xmult_mod correctly - If Those Are Even What I Should Use
lua is a confusing language, even more so than JS
i'm tryin my best
i'm not letting this intimidate me even if i can't really find results myself a lot of the time
Can you give an example of an effect you want to do
let's start with this one
so! the joker i've started with gains xmult every time a playing card or joker is destroyed, and every time a debuffed card gets "scored". what would be the first steps to getting that in place?
ah, I see
It takes time especially if ur just starting out literally took me 2 weeks just to make this
Don't expect it to be an overnight success give it time and it'll come together
And it's no where near as cool some shit seen ppl make
It just works when ppl select portuguese as their language, no need to delete the jokers' loc_txt. Although it's redundant code if there's a en-us loc file
Wait, it didn't work? I'd woked here when I tested without the en-us loc file. Will do some testing tomorrow
Rn it's 3am and imma get some sleep
I will try to explain the thought process as well:
First of all, we need to make the effect to make it gain xmult
-- Two dashes marks a comment.
-- This means anything in the same line after the
-- two dashes are not run as code.
-- This makes a piece of code reusable.
-- Whenever you call the function by writing
-- "my_joker_gains_mult()", the code within
-- gets run again.
function my_joker_gains_mult()
-- We need to check every joker,
-- if it is my joker, update it.
for i,joker in ipairs(G.jokers.cards) do
-- Repeat for every joker:
-- "i" is the ordinal of the joker
-- (1 for 1st, 2 for 2nd, etc.)
-- "joker" is the actual joker itself.
if joker.ability.name == "My Joker's Name" then
-- "extra" is extra information about your joker.
-- You can use this to store the Xmult increase of
-- the joker, making it easier to change later.
joker.ability.x_mult = joker.ability.x_mult + joker.ability.extra
-- Alternatively, you can "hard-code" it: (using 0.2 as an example)
-- joker.ability.x_mult = joker.ability.x_mult + 0.2
end
end
end
Debuffed card:
eval_cardis apparently used to calculate a card scoreG.FUNCS.evaluate_play(state_events.lua#571) uses it and seems to calculate a hand
scrolling through it... Oh!state_events.lua#655 runs for every debuffed card.- Insert
my_joker_gains_mult()right there. (after theif)
sure, no rush! anything comes up feel free to make a comment in the kcvanilla thread
Will do 🫡
thank you, this is actually really helpful. i knew about comments, i'd managed to intuit storing the Xmult increase under extra, so that's nice. as someone who needs to learn both the thought processes and the actual ways to go about writing this is exactly what i needed
I think i've got this remove_from_deck function wrong
What is the SMODS' version of
context.consumeable.ability.name == 'The World'?
is there a way for a mod to check if you have a different mod installed and if you dont disable content that only works with that mod rather than listing that mod as a dependency?
Dumb idea but has anyone tried making a joker card that runs balatro within a joker card lol
i.e i make a consumable that can apply cryptid glitched edition this obviously wont work if cryptid isn't installed can i somehow disable this consumable from existing if cryptid isn't installed rather than requiring cryptid as a dependency
Alright i dont understand the remove_from_deck thing, i've got this function set up wrong
finally got it working!
yes your remove from deck is not set up right. you should defining it as so:
remove_from_deck = function(self, card, from_debuff)
You should also end off calculate before that definition
Forgot to ping
i knew it was something like that
i was looking at the other functions and thinking about it
Question you think the anime style card look is better or the official TCG look would work better in a Yu-Gi-Oh mod
Thinking of swapping to the anime style
But not sure
So the card is supposed to make rerolls start at $0 for 3 rounds before it disappears
The problem i was having is that the rerolls would stay at 0 cost after it would disappear
and i'm trying to make them go back up to normal cost after it's gone but i'm having trouble.
now every time i reroll it looks like it adds 10 to the cost
Is next() a lua built-in or a Balatro global?
I'd take a look at the source code to see how Chaos the Clown does it, seems to be a much simpler solution than what you're trying to do
Chaos the clown only gives you 1 free temp reroll
after the free roll it goes back up
Yes, and that functionality can be adapted to your card
I'm not giving free rolls tho, it's closer to the d6 tag
Rerolls starting at $0 sounds like a free reroll
It's technically 1 free reroll, but then the cost climbs from 0, rather than going back up to 5
so 0, 1, 2, 3, 4 etc
I just spent 2 hours debugging, and still couldn't figure out why somehow my patched function apparently returned an infinitely deep UI definition (there are probably circular references)
I even wrote a function to diff two tables
I understand now. What exactly is card.ability.extra.rolls and why is it being applied to the reroll cost?
rolls = 5, i'm just minusing from the cost
error(tdiff(nret.nodes[1].nodes[1].nodes[1].nodes[1].nodes[1],c.ret[1].nodes[1].nodes[1].nodes[1].nodes[1].nodes[1],"",7))
When are you removing the card from your hand?
after 3 rounds it removes itself
But when? As in what context?
oh, ending shop
That might be why. I think reroll costs reset when the shop ends, and when you add that extra +5 from your card on top of it, you're getting the 10
I'm not 100% on that but it's a decent hunch
Nah that looks to happen everytime i roll
i think also after the shop ends, but also when i roll
Well yeah of course it'd change when you roll, but that sounds like it's working fine. It's after it's being removed that's the problem, yes?
Without the remove_from_deck function, the rerolls work as intended, but the cost doesn't reset to normal after the card is gone
may have solved it
hmm, nevermind
When hand_type is lv.1, would G.GAME.hands[hand_type].level be 0 or 1?
I wish I could help ya work this out more but it's late and I'm stumped too. I hope you figure it out
i dont think the adding 5 works, because it just overwrites any vouchers and sets the cost to 5 forever lol
Thanks for the help anyway
have a good sleep
It would be 1
Thank you
OH MY
There was a stub that returned nil for an unimplemented functionality since I assumed it never existed
that apparently caused all sub function calls used as table values to be deleted
for example
function original_function()
return {get_one(),get_two()}
end
function patched_function()
return {nil,nil}
end
This feels kinda cursed even though it works without causing any issue so far.
if it works it works 
Is it just me or does print not write to the console
love's console is always empty except for the steam API debug info
Heard that
That's programming baby
Use sendDebugMessage(“message”,”name”) instead
If it works it works
I should probably learn to organize my code now
Instead all of it in same main.lua file

is that sendDebugMessage or love.sendDebugMessageo r love.something.sendDebugmessage
The first
why would u organise code when u can have it all in one place :]
True
I feel that
But I'm planning on expanding it into more than just Yu-Gi-Oh cards now
So probably be better to have everything organized
also ive searched in this chat for a bunch of answers but none of them have worked for me.
trying to scale a joker like square joker / photograph / etc, and the only progress ive made is un shrinking the sprite??? if anyone knows how to do this oTL
Wish I had the answer cuz I need it too lol
🤝
ive tried like 3 peoples old 'fixes' and nothing changes (and it also doesnt even crash) lmaoo
Instead of making mine more square tho mine needs to be more skinny
What is its atlas' px and py?
atlas is 71w 62h, and in the smods joker ive set the display and pixel size to those numbers
originally tried having it w the other jokers and leaving it at the top left but made no progress so its a seperate sprite now
Try setting them to 71x95, but keep the sprites and expand transparent margins
What is the size of sprite itself
with the margins, 71 wide and 62 tall
and without?
Set the margins as:
left:1px, right:1px, up:1px, down:34px
where would i set the margins :0
just make the transparent margins those size
if square joker can keep being square, so should yours be able to.
this works but its not centred, and the hitbox is normal size
the art is cool
made by a friend of mine, im putting our dnd characters in the game as jokers :]
You mean the lower supposed-to-be- transparent margin is included as part of the hitbox?
ya i can grab it
i know theres a bunch of hard coding for those in the card.lua file in base game but unsure how i'd replicate it
i'll live w it as is for now
Centre issue should be solvable by adjusting the sizes of up-down margins, such as make them 17-18 instead of 1-34.
well the thing is thats hard coded in the card.lua, as opposed to connected to the joker
like
I assume you've also tried giving your joker T={h=62,w=71}?
just tried it, didnt change anything oTL
So I'm going to change the sprites of the Yu-Gi-Oh cards to anime theme so I can take more creativity with changing card text
What do u think
Even the hitbox didn't change?
nope, no change lmao
OK...
pot of greed!!! does it draw 2 additional cards
Of course
well 3 because technically it counts as a discard so the game draws 3 lmao
You will need to do math to get the multiplier, but use set_sprites for that
set_sprites = function(self, card, _front)
card.T.h = ... -- height
card.T.w = ... -- width
end
Probably gonna add card destruction to
I mean at this point I think just making cards op is fine
Unless u actually want to make a balance mod that works with vanilla game
I dont want it to be op no
Everyone just likes breaking the score at this point lol
I'd say wait until you have majority of cards u want in ur mod
Then just start assigning rarity then
What I'm doing
All them have 100% chance spawn rates just for testing
cant you spawn them with debugplus
this got it working!!!
needed to make sure the atlas was the right size and that the multiplier was scaled for 2x but this works!! thank you :]
Ok I think this is better
Debug causes problems.with spawning boosters
For some reason
How so?
Yeah how so?
Doesn't let me buy them
When I get off work I can send u a video
When I spawn my custom pack into the slot won't let me select it
you can run the resetshop command to at the very least rereoll the boosters
And if I leave the game come back sometimes it's there to buy and works sometimes it's not
I just have the booster set ton100% spawn rate lol
So no big deal for me rn
It works for custom packs
debugging LuaJIT bytecode with imhex
Why?
I'm making a mod to patch function bytecodes instead of replacing the function entirely if you want to patch in the middle
Ah
but after reassembling everything broke
function calls were randomly replaced with nil
I'm not even incorrectly parsing the instructions, I'm just failing to parse the constants(with local table = {1,2,3,foo()}, table's value is cached separately rather than built at runtime(except for the foo call))
https://discord.com/channels/1116389027176787968/1266751398142414928
this post has the links you'll need
floating point imprecision strikes again (I think)
YouTube Revanced for Android does byte code for modding YouTube apks
FIXED
Oh my god
I kept looking at my code making 64-bit ints and my brain kept thinking "lua's doubles have only 52 bits of mantissa, they can't store 64 bits" but I thought "that's such a rookie mistake I could not have possibly made that when I first wrote code"
When I first wrote the code I was probably looking at the type annotations being foo: integer and thinking "Oh, Lua must has int support like Python"
import type.leb128;
using uleb = type::uLEB128;
struct ktabk{
uleb tag;
if(tag == 3){
uleb i32;
}else if(tag == 4){
uleb lo32;
uleb hi32;
}else if(tag >= 5){
char string[tag-5];
}
};
struct khash{
ktabk key;
ktabk value;
};
struct ktab{
uleb arr_sz;
uleb tab_sz;
ktabk arr[arr_sz];
khash tab[tab_sz];
};
struct kgc{
uleb tag;
if(tag == 1){
ktab table;
}else if(tag == 2 || tag == 3){
uleb lo32;
uleb hi32;
}else if(tag == 4){//complex type, but lua has no built-in complex type
char ERROR[-1];
}else if(tag >= 5){
char string[tag-5];
}
};
If anyone wants to debug the bytecode with ImHex, here are the patterns for kgc
YES
FINALLY
I managed to replace the final create_UIBox_generic_options call with a call to whatever function I want in create_UIBox_options
WITHOUT replacing the whole function
🤔
THERE
Here's the patch code:
function aml.globals.__inject_mod_button(args)
local contents = args.contents
contents[#contents+1] = ui.button("Mods",ufa.register_ui_callback(show_mod_list),ui.config():min_width(5))
return aml.globals.create_UIBox_generic_options(args)
end
aml.patch(aml.globals,"create_UIBox_options",aml.reassemble,function(d)
local func = bp.parse_chunk(d.chunk)
func.protos[2].constants[3].value = "__inject_mod_button"
d.chunk = bp.write_chunk(func)
end)
func.protos[2].constants[3].value is the "magic location" storing the name of create_UIBox_generic_options. I could probably replace it with a table search for better future-proofing
Yes, this is for my own mod loader
Ah
smod uses source injection
which changes the lua code
I just directly patch out the bytecode
which will stay working even when line numbers move around
For even better compatibility I can record the old function name so if another mod patched this function as well I can call the patched version after injecting the mods button
(however this change is incompatible with table-searching for create_UIBox_generic_options for obvious reasons)
Could I add in something to my mod to make it so the game disables and re-enables the High Contrast Cards option in the menu upon launch? The color changes I've added to tooltips don't appear unless I manually disable and re-enable the option when i open the game every time. not the most important thing since i finally got the config working, but a slight annoyance
me when I nodes[1].nodes[1].nodes[1].nodes[1].nodes[1].nodes[1].nodes
😲
Hey i have a question. Can anyone give me a little rundown of how mods for balatro are made? I have a little knowlegde of Lua and i really want to get into making mods for the game.
smods doesn't care about line numbers moving around either
i don't remember my longest nodes chain, prolly this
t.nodes[1].nodes[1].nodes[1].nodes[1].nodes[3].nodes[2].nodes[1].config = { object = G.shop_booster }
There's a good few resources here: https://github.com/Steamodded/smods/wiki
... or new lines being added that match old patterns.
may be a stupid question but is it possible to :draw() something, adding transparency
But really, I think the great potential with bytecode patching is you can patch functions you don't know about
Alright thank you so much.
Honestly I think smod probably better for beginners than byte coding
bytecode patching is for advanced things
