#💻・modding-dev
1 messages · Page 636 of 1
I see where issue happened
Go to mods folder
Find folder called lovely
Inside it dump/tag.lua
Send it here
where do i find that??
%appdata%/Balatro/Mods/lovey/dump/tag.lua
I see
It's because of mod Arrow
@weak brook
FIx your patch please (replace 'at' to 'after')
?
if you want an instant solution, disable arrow
isnt the manager down or smth?
Okay, so, lets do advanced stuff
Go to %appdata%/Balatro/Mods
Find folder Arrow or smth like this
still nothing abt arrow
sure it aint called sumthn else?
this all of em
no "arrow"
?
you dont know how to archive things?
never even heard of doing that
goddamnit
rigt click on the folder then compress to
OHHH
DID
DI YOU MEAN COMPRESS??
I KNOW HOW TO COMPRESS
whyd u call it archive? ðŸ˜
bc zips are archives
never heard it called archiving
everyone ive heard talk about it has js said compression
crazy
and also the extended context menu
barely use THAT either
oh damn fr
mad shit
reminds me how vanilla's Card:load() has a single capitalization typo that sets a card's height to nil
How to check if final score is a prime number?
good luck
newest smods version has that fixed tho
-# i wonder who did that
maybe give this a read
tho that's for generating primes
not checking
sooo are you gonna add a cap to that orrr
pointing at bio
ah.
don't point out our typos please
haha! i will hopefully keep that in mind!
ty
checking is number a prive is VERY expensive operation for relatively big numbers
id imagine checking for primes reaching the e numbers would be really slow
imagine trying to use that prime checker joker with numberslop mods like cryptid
3.5#6e2 or whatever numberslop notation looks like
thats awesome
then ##
infinite prime
I think that even for e100 check will take insane amount of time
Just prime-numer check the part after the e
maybe only check the rightmost exponent
Alright so I kinda get how all of this works but the code sounds very slow when comparing it to extremely high numbers
Because it is
alright what do i do to check what the final score with be?
How do I localize rarity names?
actually no how do i check the current mult display?
is it possible to create a joker that upgrades the rank of cards into another rank (such as converting all 10s from deck into jacks) upon being sold after an amount of rounds (just like invis), dont really have a base to go off except invis code which for now is a bit too complex for me to understand
I'm having a problem, probably a syntax one for using Malverk: I'm trying to use Malverk to replace the default card sprites in the game using this:
AltTexture({ -- Replace Cards
key = 'new_cards',
set = 'Default',
path = 'spritesheet.png',
keys = {
'c_base' = true
},
})
-- But I keep getting this error:
Oops! The game crashed:
[SMODS _ "src/loader.lua"]:571: [SMODS TestMod "main.lua"]:92: '}' expected (to close '{' at line 91) near '='
I can provide the actual main.lua if needed (I changed the mod name and key for temp reasons.)
yeah, one sec
Everything you're trying to do is possible by looking at the vanilla code, take a look through (what is now my new favorite resource)
https://github.com/nh6574/VanillaRemade
Oh
wait, i see a small error in the temp thing i did. ignore the new_cards part, that wasn't supposed to be there
Just trying things lol
An inconsistency compared to your other lines of code
I'll try that, the new_cards part should be retro_cards, I fixed that real quick. I'll test these fixes, thanks
extra commas at the end of tables don't cause errors
you can’t key that table like that I think
for string keys you use [] those
along with the quotes
but you can also just remove the quotes
It doesn't crash now, but it doesn't use the sprite I placed in the folder (2x) sprite called "spritesheet.png" which is a new blank card sprite
To correct myself, I placed a new sprite called (spritesheet.png) in the assets folder of my malverk pack. The image looks like this:
I’d have to check on my pc, hold on let me boot it up
thank you! ill look through it again tomorrow, mustve missed some crucial bits of it if i thought my idea cant be achieved
Currently, it just does this:
baltro
My paranoia has subsided
It's gonna get wacky, but it's very doable
You're probably gonna look at cards that request a certain rank, invisible joker and ouija
got it
Happy Holidays everyone! In case someone missed it, the Youtuber 'Fir' did a really fun video where he enabled 40+ community mods in his Balatro and recorderd the outcome! It was really great to see the mods of so many folks I know from here on his video!
https://www.youtube.com/watch?v=SfejblgDZGo
@long sun , @tall wharf and many many others!
Today I installed way too many "Vanilla" Balatro Mods. Basically, if the mod self identified as a vanilla or vanilla+ style mod, I installed into my game. On their own a lot of these mods are pretty tame, but when combined together the game becomes an absolutely beatiful and fun mess. Enjoy the video!
Thanks to all the mod makers who created so...
I felt so proud of my peers here to see their cool stuff shown off!
i remember seeing niko send this in hotpot discord
It exposed me to the ModManager which I totally missed. Turns out you neeed to do work to publish your mod there
I couldn't figure it out, but I think I have a workaround that might work. Just make the deck skin have the white part so I can use my custom sprites. I guess it worked out in a way
video thumbnail so cool tho, I like it
That helped massively! But now I'm trying to figure out how to only debuff clubs.
Removed the context.other_card so I know I'm on the right track, was trying to put :is_suit and still got a nil value
context.debuff_card:is_suit("Clubs")?
Yeah
do you definitely have the file in both 1x and 2x folders? it seems to work fine on my end
you should check context.debuff_card on its own first, then the cardarea check, and then the suit check
also it should be context.cardarea
No, that doesnt exist in context.debuff_card
on its own instead of context.debuff_card.area
wait, this isn't right
i was just doing one step at a time because doing everything at once won't teach me as much, why is context.debuff_card nil in general???
its nil because it doesnt always exist when calculate is called
checking context.debuff_card first makes sure that it isnt nil
oh, that's wacky
and doing the cardarea check second makes sure that the card has a suit and that is_suit will therefore work on it
it's not crashing but nothing is getting debuffed, the first line works, it's the other two i can't seem to figure out.
it should be context.debuff_card.area, not context.cardarea
besides that it should work, so idk
WOW, i'm shocked that just splitting the checks worked!!!
now i have no tools for problem solving :)
thank you so much!
you can put all the checks into a single if statement btw, its just the order that matters
Hi! Making my first mod, quick question: Is it possible to check the size of a played hand before the cards are scored?
I keep trying to compare the played hand size to a set value (3, like half joker) but it appears the value is null, and my game keeps crashing.
#context.full_hand
you can check for values like if it was square joker it would be
#context.full_hand == 4
i'm not sure how helpful that is
you could also do (as an example)
card.ability.extra.chips = card.ability.extra.chips*#context.full_hand
though i haven't tested
okay so this works, but i need it to count debuffed clubs instead of not debuffed clubs
if playing_card.debuff
like this?
where does the game undo the effects of a defeated boss blind? i see Blind:defeat() but it doesnt seem to do much
No, if playing_card:is_suit('Clubs', true) and playing_card.debuff
No, it is Blind:defeat
well this doesnt seem to do much besides fucking up the UI a bit so im not sure if thats what actually undoes boss blind effects after beating them
Are you trying to make it so boss blind effects are permanent?
yeah i tried to do that a while ago but it flopped and rn im revisiting the idea
Yes, you would have to use the code I sent you.
well if you recall, according to you it doesnt work on vanilla blinds, and i have asked several people many times for solutions and got none
You would have to do the effects of the vanilla blinds manually.
same result, no tally if debuffed
Code?
vanillaremade clutching for the 13th billion time
rq where does balatro store its joker code
card.lua
if SMODS.has_enhancement(card, 'm_wild')
ooh as a side note
like this right
something can be all suits without being wild
so do you want to check for wild specifically
or all suits
so like this right
you dont need card = card
context.repetition instead i think
(still keep the cardarea check)
and its repetitions = 1
Thank you for the answer, I appreciate it.
That was the solution I was trying, but it kept giving an error message to the effect of "Cannot compare null to table"
I'll figure it out tho. At least now I know I'm on the right track lol
instead of retrigger = true
No, it would be if SMODS.has_enhancement(context.other_card, 'm_wild')
why is my localize call not loading the description?
this is a lovely patch in generate_card_ui
this is the localization entry
if context.repetition and context.cardarea == G.play then
if SMODS.has_enhancement(context.other_card, 'm_wild') then
return {repetitions = 1}
end
end
that should do it
got it
lemme check
YEP
thanks much
i'll try and be more active here
i want to
learn.
Oh this helped me realize I was using a single = and not == haha
Thats probably why it was giving me an error
lol
I forget often, so my favorite way to remember is
Are you checking? ==
Are you declaring? =
It's obtuse but having an easy question to help you stop and think goes a long way.
Its so silly cause I've been in computer science for so long, you'd think I'd remember those kinds of things, but I still forget commas and ending functions lolll
bump
It's always the little things :)
no way, its them
cant have shit in balatro
i tried to modify the hands played thing to add a clause which refunds the hand immedietely if a eval returns true
it never worked
my mom then blamed the resulting panic attack over the video game
oh my fucking god
What's your mod? Is it on ModManager?
lbp in the big 2025
Littleb.... ligglbigp... littglebuh... bigplsnet... buhhhh
🥺
This could change the game
Wait ninjabanana??
heya
I've been here a while
mainly over in cryptid discord (and clams more recently)
wait was i just in chat
There was so much more content between 0.5.2 and 0.6.0 of AIJ... if only he updated it T_T
how would one count debuffed cards like cloud 9?
i'm trying to count every debuffed club in a deck
i have this but playing_card.debuff doesn't work
someone REALLY should make a guide on custom context
Like what would be in that guide?
its not obtuse to create a context, its obtuse on handling the return tables and i just have no damn clue on that
hmmm that is interesting. I've never added a context. I would have to look at something like context.destroying_card or repetition to see how they handle their inputs to see whats going on. Also how you even register a context window and when that should happen
context window is a myth
theres a function you can call
that is passed as context
context wunkdow is NOT a myth...
how are context return tables even returned as
big table
a horrible tuple?
is it in cardarea area...
SMODS.calculate_context type shit
don't call them tuples!
what do i call them...
Tables..
i hate tables
Suggested by http://www.freelists.org/post/luajit/Luajit-30-plan,2 Unclear how to find the upstream tracker to push this issue to. Will send pull request to upstream with DNA/RNA sequence, once thi...
who is Mike Pall
who is mike pall
oh my god nevermind ðŸ˜
more is better i think
hey is there any way to force localize() to use a specific language
No, because localize just looks at the localization.
so you'd have to load the localization file you want yourself
that's done in game.lua in the Game:set_language function
dam
bump
speaking of localize
im pretty sure other indexes into the misc.dictionary section
mfw
function togabalatro.talismanjoke()
local rtxt = G.localization.misc.ui_strings.toga_talismanjoke
local other_joker = context.card
if other_joker and other_joker.ability then
local result = {}
if other_joker.ability.extra and type(other_joker.ability.extra) == "table" then
if other_joker.ability.extra.chips and other_joker.ability.extra.chips ~= 0 then
result.x_chips = other_joker.ability.extra.chips
end
end
if next(result) then
return result
end
end
end,```
how do i make this trigger when other jokers trigger
ive tried type = "para_items" as well and no luck
context.post_trigger
type can only be a few hardcoded ones
its an optional feature that you should look into
dont know too much about it unfortunately
i tried but like how do i get the card which triggered
descriptions\
context.other_card
oh
type = "descriptions", set = "para_items", key = "para_itemeternal"
tysm ive been trying for so long
and its that easy
small problem
same crash
type = "description"?
try putting localize{ type = "description"... into the debugplus console
maybe it wont look for the loc_vars if i remove #1# and #2#
different separate crash which comes from running it without nodes i think (because i cant give it any nodes to render on from the console)
thanks balatro very cool
big
ending the round as soom as entering a blind, how do i make it so the cards do not draw?
Code?
cant send the entire joker code for reasons
Try putting the end_round in an event.
i'm trying to make it so on discard debuffed clubs trigger adding mult, but nothing is happening
(i decided to pivot to what is better game design and what should be much simpler to code)
Debuffed cards aren't calculated.
You could do it on context.pre_discard but it wouldn't directly trigger on the cards being discarded.
that's fine :)
it's the joker itself i truly care about
well there is progess, now it crashes saying other_card is nil lol
is it because the card is debuffed?
No, it's because you have to iterate over context.full_hand
its not on mod manager
its called degeneracy
if context.full_hand and context.pre_discard and context.other_card.debuff and context.other_card:is_suit("Clubs") then
still nil though i'm not familiar with how to use context.full_hand
if context.pre_discard then
for k, v in pairs(context.full_hand) do
if v:is_suit('Clubs', true) and v.debuff then
card.ability.mult = card.ability.mult+1
end
end
end
that wouldn't work
Card:is_suit(suit) already returns false if the card is debuffed
use Card:is_suit(suit, true) instead, it bypasses debuff state
i was doing this fo r so long i'm still in shock it now works, what would i do without the leader in our community 
😔
-# and yes, the card area DOES exist because trying to remove a card returns a nil error
question: how do i make a seal retrigger OTHER scored cards
i dont understand
please explain
What do you not understand about it?
honestly
like all of it ðŸ˜
i need to know what parts are not needed in what i'm trying to do
You would have to change most things in the if v:get_seal() == 'modprefix_key' to make it retrigger the cards you want instead of adjacent cards.
is this correct
SMODS.Seal {
key = 'club',
atlas = 'wacleseals',
pos = { x = 3, y = 0 },
config = { extra = { repetitions = 1 } },
badge_colour = HEX("5186a8"),
calculate = function(self, card, context)
if context.repetition then
local retriggers, my_pos, full_hand = {}, 0, CardArea.is(context.cardarea, CardArea) and context.cardarea.cards or context.full_hand or G.play.cards
for k, v in pairs(full_hand) do
if v == context.other_card then
my_pos = k
end
if not v:is_suit('Clubs') then
retriggers[k] = {repetitions = 1, card = v}
end
end
if retriggers[my_pos] then
return {repetitions = retriggers[my_pos].repetitions, message_card = retriggers[my_pos].card}
end
end
end
}
No, you have to use the mod calculate.
what
SMODS.current_mod.calculate = function(self, context)
end
so smth like this
SMODS.current_mod.calculate = function(self, context)
if context.repetition then
local retriggers, my_pos, full_hand = {}, 0, CardArea.is(context.cardarea, CardArea) and context.cardarea.cards or context.full_hand or G.play.cards
for k, v in pairs(full_hand) do
if v == context.other_card then
my_pos = k
end
if not v:is_suit('Clubs') then
retriggers[k] = {repetitions = 1, card = v}
end
end
if retriggers[my_pos] then
return {repetitions = retriggers[my_pos].repetitions, message_card = retriggers[my_pos].card}
end
end
end
or do i have to put SMODS.calculate under calculate =
No, you need to check if the card has the seal.
i want it to retrigger if it isn't clubs tho
You mean you want cards with that seal to retrigger all other cards that aren't Clubs?
yep
all other scored cards
SMODS.current_mod.calculate = function(self, context)
if context.repetition and context.cardarea == G.play then
local retriggers, my_pos, full_hand = {}, 0, context.scoring_hand or context.full_hand or G.play.cards
for k, v in pairs(full_hand) do
if v == context.other_card then
my_pos = k
end
if v:get_seal() == 'modprefix_key' then
for kk, vv in pairs(full_hand) do
if not vv:is_suit('Clubs') and vv ~= v then
retriggers[kk] = {repetitions = (retriggers[kk] and retriggers[kk][1] or 0) + 1, card = v}
end
end
end
end
if retriggers[my_pos] then
return {repetitions = retriggers[my_pos].repetitions, message_card = retriggers[my_pos].card}
end
end
end
ah thanks
huh
No, it needs to be outside of the seal.
why does this reflip the card when scored right after being unflipped
if card.facing == 'front' and not context.main_scoring and context.cardarea ~= G.play then card:flip() end
if card.facing == 'back' and context.main_scoring and context.cardarea == G.play then card:flip() end
help
h
The question is simply how do I clear these boxes as if there was no hand selected?
Folks, I have a function that doubles ante scaling by X2 for the rest of the run when you beat the Blind by scoring X2 the requirement. I want to tone down this effect to make it work on the next Blind only. How should I adjust the code to do that?
(ignore prints)
hm
have a variable that decreases or whatever at the end of the next blind
then you check if that variable has done its thing
Look for update_hand_text
:3
empty strin
g
just empty string
you were like 99% of the way there lmao
ah
oh and mult and chips should be 0 instead of empty string
amazing
...where in localization file do I define the text and labels for a custom ConsumableType?
I'd like to make it so the score displayed is only changed on the next blind
Otherwise itd be a bit confusing
...so, in my case,
SMODS.ConsumableType{
key = "togaitem",
...
}
would be this?
I suppose it in misc.labels
No idea, sorry.
this is really funny to me for no reason lmao
people.you
clueless
any way to check if a card was face down before getting played?
tried to use card.facing == "down", but doesn't work bc the card gets flipped before getting scored
hey guys does anyone want another code dump from me being the clueless soul i am and asking for help on the simplest of stuff 🥺
sure why not
Send it.
so my awesome code (on the right) hits me with the crash (on the left) whenever i try exiting and entering the uibox (its a menu)
i assume its something to do with how i remove the cards from the cardarea on leave but i need help please 🥺
When cardarea is removed it deletes cards for some reason
aka you're trying to do smth with removed card area
oh
G.FUNCS.market_clean = function()
for i = 1, 5 do
G.fish_cards.cards[1]:remove()
if i == 5 then
G.FUNCS.exit_overlay_menu()
end
end
end
wtf is this
so i need to re add the card area?
the cards just sorta stay floating if i dont remove them manually
I do believe you doing something wrong if you need to do this on market closing
thats why im asking for help 🥺
Decent amount of things here to cover
Starting from G.FUNCS.market be too generic name
Prefix all your stuff
Then, you shouldnt store your stuff in just G
If you have own global object, use it instead
Because, game saves all card areas added to G object, which you probably don't want right
When you have area added to UI via G.UIT.O, when UI is removed, it removes area and cards in it automatically, so you dont need cleanup them
But reference in G will contain removed card area
And attempt to add cards in it will crash because, well, it's removed
trying to fix bugs that have popped up with my mod since ive been away for a while, anyone know why trading card might not be active despite discards used being 0?
ohhh
holy, wtf
G slop
that's not how localization should work ideally but okay, sure
the menu looks like this if that helps at all
not the prettiest ui I seen I'll be fair
if i nest it in a variable deeper than G (say G.X) will that have the same issue?
yeah
i agree it looks horrendous but it gets its job done
Common practice for mods is have own global object where you store and manage your stuff
I guess you dont have one, that's okay
In theory you can make G.FishMarket = {} and store stuff here
So
how does one create a mod for this game.
I am a bit intrigued
Is there a way to re-enable a boss blind after it has been disabled?
Yeah gimme a sec
First joker at the top re-enables the boss blind
you can take a look at the smods wiki (you should find the smods github repo simply by searching for it lmao)
tho i learnt from a video guide
seems simple enough, thank you
Np
No, it wouldn't work for blinds that use set_blind
Haven't encountered that problem so I didn't know 
Alright
this is so frustrating because its only happening sometimes
like its random if trading card just doesnt work or not
Seems like a lot (0_0)Ï…
so far
and looking at the code, G.GAME.current_round.discards_used is the only value trading card really checks so i have no idea what i couldve done to break it
What do you want to do exactly? New jokers?
uhh
Yes! I'd like to try and do that first.
try using your own variables then update that with discards
Okay when I first got into modding I honestly checked how other mods did stuff and then made mine based off that. But the most important files are a "manifest" file that basically is the thing that loads in the mod essentially with some other info. And a "main" file that is usually for loading in other files and some function definitions
ohhh alright
would getting the cryptid mod be a good start?
Vanilla remade is a good one to check
Alr
Just all vanilla stuff made with smods stuff
tf is a manifest file bro
Hard no; its code is not like most other mods, instead structured to meet its own unique needs; its code is not good to learn from as a beginner
That's what I did but cryptid is not a good one if imma be honest
Ohhh
really you only need a main.lua, asset folder with images (1x and 2x sub folders), json with mod info
Y'know the file that says what the displayname of the mod is and color
aint that the .json
Ig
I just call it the manifest cause that's what I name it normally
That's usually what those kinds of files are called
Yeah exactly
Real
So how far have you gotten now?
Im attempting to download everything from the smods guide
Alr
balatro modding is like an in quint easing
it starts easy then you get 500 crashes of doom
ok i think i got the lovely injector working!!
The game crashes cause you forgot to add one end
nope
forget a double =
from being so tired
maybe you miss a bracket
type o instead of i
Legit my game crashed and I couldn't figure out why for 2 hours until I saw that I forgot 2 parentheses in the loading
💔
Also play stocking stuffer now
dont you check the crashlog
I read it
I got a question about the downloading steps
Do i put the extracted steammodded folder in Balatro's mods folder?
in appdate/roaming
Ok i think did it!
help?....
is there a way i could like. hook into lucky cards? (i'm trying to make a joker which replaces the +mult on lucky cards with xmult and it's currently working but it requires taking ownership of the entire enhancement which feels inelegant)
I need to localize my files, what is the format for localizing Jokers?
is it {prefix}_j_{name of joker}
No, it's j_modprefix_key
thx
What are the standard highlighting colors?
I know Mult is often highlighted with red. Chips are sometimes highlighted with blue as well
Is there any "standard" for Jokers?
usually the name of any given item is highlighted with {C:attention}, with the exception of consumables which are highlighted with the colour of their consumable type
is there a context for determining if you are currently in a blind?
G.GAME.blind and G.GAME.blind.in_blind
ty
coming back from last night with the idea i had about a joker similar to invis but that converts cards of a rank to another once sold, i implemented it but:
- it doesnt track progress
- im not sure if the actual transformation is correct
would appreciate if someone tells me where it went wrong and if i did it correctly
loc_vars = function(self, info_queue, card)
return { vars = { card.ability.extra.total_rounds, card.ability.extra.invis_rounds } }
end,
calculate = function(self, card, context)
if context.end_of_round and not context.blueprint and not context.repetition then
card.ability.extra.invis_rounds = card.ability.extra.invis_rounds + 1
end
if context.selling_self and card.ability.extra.invis_rounds >= card.ability.extra.total_rounds then
for i = 1, #G.playing_cards do
local deck_card = G.playing_cards[i]
if deck_card:get_id() == 11 then
G.E_MANAGER:add_event(Event({
func = function()
local _card = G.deck[i]
assert(SMODS.change_base(deck_card, nil, "Queen"))
return true
end
}))
break
end
end
end
return {
message = (card.ability.extra.invis_rounds < card.ability.extra.total_rounds) and
(card.ability.extra.invis_rounds .. '/' .. card.ability.extra.total_rounds) or
localize('k_active_ex'),
colour = G.C.FILTER
}
end
}```
Is anyone here able to help with an accurate conversion of a node's coordinates to screenspace coordinates?
The base coordinate system conversion as the cursor implies is simply self.T.x/y * G.TILESIZE * G.TILESCALE, but it seems like for nodes they have some offset using self.shadow_parrallax and some other built-in offsets that I can't quite wrap my head around. The result is that the offset betweeen the base coord conversion and the real node's screen position looks something like this, but I haven't been able to wrap my head around what math gets me a consistent result to the real screenspace
is there anything wrong with this extension? the consumable type definitely exists and it does work when i have the set as the exact same thing in an SMODS.Consumable yet when i try to use the extension it crashes on launch
log
actually nevermind i might be stupid
how can i check if the player has a specific achievement?
wait do achievements even stay earned even after you disable and enable the mod?
yeah, i believe achievements are saved to player data which persists even after a mod is disabled
I'm trying to pull the enhancement key for the card that a seal is on, but I keeps saying center is a nil value. This is the line of code that mentions center:
local enh_key = card.config.center.key
Am I going about it wrong?
bump for anyone that has more of a clue on whats happening for it to go wrong
and other issues
Still encountering an issue with something I tried to do. I tried to replace the white part of the card with a new default one and it shows up invisible now. I have an image called "retro_cards.png" in the (2x) folder and I have the code for the alt texture, but it doesn't work. Any ideas on what I did wrong? I've been stuck for a while trying to figure it out. (I'm using Malverk)
AltTexture({ -- Replace Cards
key = 'retro_cards',
set = 'Enhanced',
path = 'retro_cards.png',
keys = {
'c_base'
}
})
is there a context to detect if you've won a run?
Remind me again
What was your goal
with that
eremel helped me
ah
were you still having problems with that one blind?
how do i get the currently selected joker
G.jokers.highlighted[1]
card.config.center.rarity
iirc
where card is a joker
if you're checking if a card is a certain rarity
you can just do
card:is_rarity(rarity)
i'm checking if a rarity is higher then another one
SMODS currently does not implement an ordering system for rarities
but for common - uncommon - rare - legendary they have number aliases
yes
what is the status of your current blind code
i have to leave temporarily but ping me so i can get back to it easier
Oooo what is this mod?
Not modded rarities though
Though someone is working on a numerical ordering system for modded rarities
yea
just know that i know this will mostly work for my usecase
If it works then it works
bump
its 1am here 😠ill let u know tomorrow
Fair
i think you can try use G.GAME.won
question: how do i get the length of played hand as a number
#context.full_hand
thanks
why does this keep returning +0 mult when i play a hand with less than 5 cards
calculate = function(self, card, context)
if context.before and context.cardarea == G.play then
mult = mult + ( (5 - #context.full_hand) * 11 )
return {
message = localize { type = 'variable', key = 'a_mult', vars = { card.ability.seal.extra.mult } },
colour = HEX("575757")
}
end
end
mult = 0, manacle_mult = 11 btw
Put the mult in the return.
so like this
Remove the mult +
ah
i want to make it scale by adding that tho
-# nvm it just rebalanced the effect lmao thanks
this was awhile ago, hasnt been much updated but its geomelatro, i think it mostly works?
Interesting
This has the unlock conditions remade
https://github.com/nh6574/VanillaRemade/blob/main/src/jokers.lua
You can find a joker or object that requires a run win to unlock
blueprint
way way late response just realized but yeah just find blueprint
where is every playing card sstored
G.playing_cards
is there a context for when a card spawns in
context.setting_ability?
theres the card's set
which is like 'Tarot' 'Joker' etc
i think its 'Base'
will try
tho i havent really needed to get the set of a playing card before
i just know its sometimes referred to as 'Base' in the code
is there any setting in set_edition, set_ability, or set_seal that allows you to mute the sound they would otherwise make?
doesn't appear to be the case?
hec
i saw another mod doing a forever running event for setting prices
i guess i couldl do that
but how do i stop it when the joker ceases to exist/gets debuffed
...wouldn't a return true kill said event?
i know that
but how do i check the joker ceasing to exist/debuffed
(and how to start it back up when the joker stops being debuffed)
@faint yacht
Stopping the event is return true if not next(SMODS.find_card('j_mod_key'))... but bringing it back up?
Or just do nothing if not next(SMODS.find_card('j_toga_notsosmileyface')) in the event.
wait find_card excludes debuffed cardss?
SMODS.find_card(key, count_debuffed)
makes sense
time for pain
how do i make a joker not appear nor count in the collection?
?
question: how do i use a loc file for seals
doesn't seem to work for me :/
damn
is it possible to swap between two atlas pos indexes based on a joker's local variable?
Yes, every time you change it you would do card.children.center:set_sprite_pos({x = x, y = y})
thanks
its so easy to lose track of balatro ui im crying
ive had to look for the node i was already editing again like 5 times
Is the parts table of poker hand parts global name "parts"? I need to define a poker hand part in terms of another, like parts._all_pairs is define as the merged parts._2
i fucking swear to GOD this was working earlier why is it not now all of a sudden
SMODS.Seal {
key = 'club',
atlas = 'wacleseals',
pos = { x = 3, y = 0 },
badge_colour = HEX("b9cb92"),
}
SMODS.current_mod.calculate = function(self, context)
if context.repetition and context.cardarea == G.play then
local retriggers, my_pos, full_hand = {}, 0, context.scoring_hand or context.full_hand or G.play.cards
for k, v in pairs(full_hand) do
if v == context.other_card then
my_pos = k
end
if v:get_seal() == 'tcg_club' then
for kk, vv in pairs(full_hand) do
if not vv:is_suit('Clubs') and vv ~= v then
retriggers[kk] = {repetitions = (retriggers[kk] and retriggers[kk][1] or 0) + 1, card = v}
end
end
end
end
if retriggers[my_pos] then
return {repetitions = retriggers[my_pos].repetitions, message_card = retriggers[my_pos].card}
end
end
end
-# for some reason when i delete the rest of my code it works ðŸ˜
Try and separate local full_hand? the Boolean expression might give you problems.
-# i've made several copies of this code with modifications and when i add a copy only that copy works
-# ...do i just send my entire fucking file or smth ðŸ˜
what
local retriggers, my_pos, full_hand = {}, 0, context.scoring_hand or context.full_hand or G.play.cards
instead do this
local retriggers, my_pos = {}, 0
local full_hand = context.scoring_hand or context.full_hand or G.play.cards
ah noted
...still broken
local retriggers, my_pos = {}, 1
since it seems you use my_pose as the index of a table
Does the crash have something to do with indexing a nil value or any other non-table value?
Does it even crash?
does not crash, it just straight up does not retrigger
Try and put some print() to see what code is executed
still nabbing at this
ive made a hook that "works" where cards arent being drawn
but the game state itself is just frozen
i dont wanna learn the ins and outs of how balatro operates the states so if anyone actually knows...
(specifically if the round doesnt end, but when i force the round to end the cards still draw)
-# decided to revert it back since the minor changes didnt rlly work
No, those are the exact same.
No, my_pos should always be set.
oh my fucking god ðŸ˜
it's because you can't have more than one calculate
the lowest one overrides the ones on top ðŸ˜
welp tiem to learn how to add more code files
You just put the code in the other ones in the first one.
ah
oh my fucking god i'm stoopid
oker it works now :D
how does Card.calculate_joker work?
I want to localise the name of G.GAME.last_tarot_planet, and so I need to find a way to make it reference two different sets, or check between them
set = G.P_CENTERS[G.GAME.last_tarot_planet].set
Sweet
i'm trying to identify the blind's name, how would i do so?
localize({type = 'name_text', key = 'bl_modprefix_key', set = 'Blind'})
i'm actually trying to create a joker that shares the same name as the current blind, should have been more specific
i know how to create a joker, just need the name
also i have made the jokers with the names
key = 'j_modprefix_'..localize({type = 'name_text', key = G.GAME.blind.config.blind.key, set = 'Blind'}):lower()
What is the goal?
What is a sliced consumable?
No, you should be using SMODS.shallow_copy to make a fake card.
You might need to ask Ruby about that, Also this is a crossmod for Entropy so…
what do i need to change about my key?
this joker is hell and this is my punishment
hey atleast it didnt draw the cards
ðŸ˜
im just patching some stuff and seeing what sticks
if i actually pull this off by throwing random bullshit i should get an award
what are ya trying to do
make it so cards dont draw when starting a blind
uhhh
local oldcardremove = Card.remove
function Card:remove()
if self.ability.consumeable and G.I.CARD[2] then
local card = G.I.CARD[2]
G.modprefix_savedconsumeables = G.modprefix_savedconsumeables or {}
local old_ability = copy_table(card.ability)
local old_center = card.config.center
local old_center_key = card.config.center_key
card:set_ability(key, nil, 'quantum')
card:update(0.016)
table.insert(G.modprefix_savedconsumeables, SMODS.shallow_copy(card))
local fakecard = G.modprefix_savedconsumeables[#G.modprefix_savedconsumeables]
fakecard.ability = copy_table(fakecard.ability)
for k, v in pairs({'T', 'VT', 'CT'}) do
fakecard[v] = copy_table(fakecard[v])
end
fakecard.config = SMODS.shallow_copy(fakecard.config)
card.ability = old_ability
card.config.center = old_center
card.config.center_key = old_center_key
end
return oldcardremove(self)
end
if context.post_trigger then
local other_joker
for k, v in pairs(G.jokers.cards) do
if v == card then
other_joker = G.jokers.cards[k+1]
end
end
if other_joker == context.other_card then
for k, v in pairs(G.modprefix_savedconsumeables) do
Cryptid.forcetrigger(v)
end
end
end
gl i guess
oh cool now the hand doesnt update to draw cards
i might actually use this for the future
So I want to implement a functionality that once a hand is played, at the end of the scoring it nullifies the score and returns the played cards to the hand. But whether I try it with a boolean that changes on context or contexts themselves, it doesn't work properly (I've listed what I've tried so far). Can anyone help out?
config = {
used = false,
last_score = 0,
hand_incoming = false,
hand_cards = {}
},
-- "Once per round at end of scoring process revert the played hand by selecting and dragging this joker"
-- no need to synchronize between several copies of the joker since you can't be dragging two jokers at once
calculate = function(self, card, context)
if context.blind then
card.ability.used = false
card.ability.hand_incoming = false
end
-- intended if hand_incoming boolean used
-- context. : press_play, after
-- (for some reason context.before triggers it even not during hand playing, but everywhere else too, go figure)
if context.press_play then
card.ability.last_score = G.GAME.chips
card.ability.hand_incoming = true
end
-- context. : individual (triggers mr bones - beats blind after score nullified),
-- main_eval (triggers it basically at all times),
-- joker_main, post_joker, edition (don't work at all)
if card.states.drag.is and not card.ability.used and context.edition then -- the third boolean I swap
card.ability.used = true
G.GAME.chips = card.ability.last_score
G.GAME.current_round.hands_left = G.GAME.current_round.hands_left + 1
-- TODO return played non-destroyed cards into hand
end
-- intended if hand_incoming boolean used
-- context. : hand_drawn, final_scoring_step
if context.hand_drawn or context.end_of_round then
card.ability.hand_incoming = false
end
end
Currently I've made it so proccing the joker happens by selecting and dragging it, maybe I try a different approach, but I've made it somewhat work so far
I have to mention that with the card.ability.hand_incoming parameter things work only until the first played hand, after that it triggers non-chalantly at all times
correction, key wasn't the issue
fixed it!!!!
didn't need to make everything lower case lol
honestly the solution was much simpler than i thought
now i just need to figure out how to make a fail safe if it encounters an unsupported modded blind.
how can i identify if the current blind is a modded blind?
how do i make it so that this makes exceptions for rare consumableslua if G.consumeables and #G.consumeables.cards > 0 then local itemcut = {} for i = 1, #G.consumeables.cards do if not G.consumeables.cards[i].ability.eternal and not G.consumeables.cards[i].getting_sliced and not consumable.config.center.hidden then itemcut[#itemcut+1] = G.consumeables.cards[i] end end
i guess you could just look for every vanilla blind and say anything that isnt a vanilla blind is a modded blin
d
do consumeables have rarities??
am i stupid??
soul is a rare consumable
rare consumables are hidden by default
oh uh
just uhh check for those consumeable's keys then ig?
there shouldnt be too many
mornin all, apologies for what is likely a very noob-y question, but i'm trying to make a deck modify the rate at which specific booster packs appear in the shop. is there a good pre-existing solution for this, and if not, how might i go about designing one?
edit, figured it out. weights are in G.P_CENTER indexed by the internal names of the booster packs. you can set an apply function which changes their weights when the deck comes into effect
Asking again
does find_card count jokers copying the target card
anyone know why this isn't working correctly?
local csc_hook = Card.can_sell_card
function Card:can_sell_card(context, ...)
local csc = csc_hook(self, context, ...)
if (SMODS.is_eternal(self, {from_sell = true}) and #SMODS.find_card("j_elle_diamond_pickaxe", false)>0) then
return to_big(G.GAME.dollars) >= to_big(self.sell_cost)
end
return csc
end
local sc_ref = Card.set_cost
function Card:set_cost(...)
sc_ref(self, ...)
if (SMODS.is_eternal(self, {from_sell = true}) and #SMODS.find_card("j_elle_diamond_pickaxe", false)>0) then
self.sell_cost = self.sell_cost * -2
end
end```
the set_cost hook seems to be the one that isn't working
first check is incorrect one btw, you forgot about G.GAME.bankrupt_at
ty i knew i was forgetting that but didn't remember the var name
also i found out it's just bc set_cost wasn't being run
It's called from time to time when some events occur
You can call this manually
for k, v in pairs(G.I.CARD) do
if v.set_cost then
v:set_cost()
end
end
I'm doin this when you getting jonkler or removing it
ty
thanks
guhh does anyone know how to do good art how do i art,,,
i cannot think straightlua if context.end_of_round and not card.getting_sliced and (context.individual or context.repetition) then if G.consumeables and #G.consumeables.cards > 0 then local itemcut = {} for i = 1, #G.consumeables.cards do if not G.consumeables.cards[i].ability.eternal and not G.consumeables.cards[i].getting_sliced then itemcut[#itemcut+1] = G.consumeables.cards[i].config.center_key end end if #itemcut > 0 then for _, c in ipairs(itemcut) do c.getting_sliced = true G.E_MANAGER:add_event(Event({func = function() card:juice_up(0.8, 0.8) c:start_dissolve({G.C.RED}, nil, 1.6) return true end })) return {message = "!!!"} end
for the love of god what the fuck am i doing wrong
oh
itemcut[#itemcut+1] = G.consumeables.cards[i].config.center_key > itemcut[#itemcut+1] = G.consumeables.cards[i]
so this should work
entropy/lib/hooks.lua, line 3693
What
earlier crash
cool
this was before ruby changed it to have .config.center_key
what could be wrong where?
in that file
only ruby can help, she made entropy
but i can't talk to her everyday
can you show the line numbers aswell
i don't wanna mess with entropy's library
okay
last night ive tried coding in a joker which acts similar to invis but as soon as being sold it converts all cards of 1 rank into another (in this case jacks into queens)
ive attached the code from it below as it doesnt actually keep count of the rounds played nor do i think the rank conversion works, if someones nice enough to look into my failure
loc_vars = function(self, info_queue, card)
return { vars = { card.ability.extra.total_rounds, card.ability.extra.invis_rounds } }
end,
calculate = function(self, card, context)
if context.end_of_round and not context.blueprint and not context.repetition then
card.ability.extra.invis_rounds = card.ability.extra.invis_rounds + 1
end
if context.selling_self and card.ability.extra.invis_rounds >= card.ability.extra.total_rounds then
for i = 1, #G.playing_cards do
local deck_card = G.playing_cards[i]
if deck_card:get_id() == 11 then
G.E_MANAGER:add_event(Event({
func = function()
local _card = G.deck[i]
assert(SMODS.change_base(deck_card, nil, "Queen"))
return true
end
}))
break
end
end
end
return {
message = (card.ability.extra.invis_rounds < card.ability.extra.total_rounds) and
(card.ability.extra.invis_rounds .. '/' .. card.ability.extra.total_rounds) or
localize('k_active_ex'),
colour = G.C.FILTER
}
end
}```
Does anyone know if it's possible to create a unlock requirement that involves the amount of blinds skipped?
do I have to do it manually or is it integrated on the code something that track blind skips?
Also I need some help to make this condition trigger, where the perso has to have -$20 on them to unlock a card
check_for_unlock = function(self, args)
return args.type == 'money' and G.GAME.dollars == -20
end
how do u add an info_queue for eternal
you can do
info_queue[#info_queue+1] = {key = 'eternal', set = 'Other'}
ty
youre welcome
right again. hello chat. i wanna figure out how exactly to make this so it triggers when there's a pair and not just any time before debuffing itself
`SMODS.Joker{
key = 'lbpfrida',
-- Display information for UI
loc_txt = {
name = 'Frida the Bride',
text = {
'If played hand contains a {C:attention}Pair{},',
'gain {C:mult}+15{} Mult',
'Then this Joker is {C:attention}debuffed{}',
'until end of round'
}
},
atlas = 'Jokers',
rarity = 2,
cost = 5,
unlocked = true,
discovered = true,
-- Compatibility flags
blueprint_compat = true,
eternal_compat = false,
perishable_compat = false,
-- Position on board
pos = { x = 2, y = 0 },
-- Main calculation function
calculate = function(self, card, context)
-- 1. Reset debuff at the end of the round
if context.end_of_round then
card.debuff = false -- removes the debuff after the round ends
end
-- 2. Only trigger Joker effect if:
-- a) The Joker is in the main slot
-- b) The Joker is NOT already debuffed
-- c) A 'Pair' is in the played hand
if context.joker_main
and not card.debuff
and context.poker_hands
and context.poker_hands['Pair'] then
-- 3. Apply the +15 Mult
local result = { mult = 15 }
-- 4. Debuff this Joker for the rest of the round
card.debuff = true
-- 5. Return the effect to the game engine
return result
end
end
}
`
i tried looking inc ard.lua
but
it is a
jumbled mess.
that i cant read!
so when something is debuffed, calculation stops
so i think the order is what matters here
if context.joker_main and context.poker_hands['Pair'] and not card.debuff and context.poker_hands then
i could be wrong on how to fix it lol
oh! use the link i provided and use a search in the joker.lua
should be much cleaner
card.lua is pure spaghetti code
atleast it groups cards in contexts- actuslly idk how that helps
im just too use to balatro coding bs
ok so it looks like
config = { extra = { t_mult = 8, type = 'Pair' }, },
seems like the thing that calls the pair
config..?
oh, the jolly joker config.extra
context.poker_hands['Pair'] is correct
next(context.poker_hands['Pair']) is needed
if context.joker_main and next(context.poker_hands['Pair']) and not card.debuff and context.poker_hands then
just like how you where trying to use context.poker_hands['Pair'], the difference is you're checking for the next played hand instead of the current (which doesn't change soon enough for the joker_main if it's not next)
forgive me. i am. kind of slow with this. so i put
you coded this?
sort of, yeah
whats your goal anyways?
like this joker or
pair gives +15 mult and then debuffs joker. that's it
this should work ^
so like. this.
Bingo!
so if context.joker_main is like. the starting point for every joker right
the main scoring timing of jokers
so when square joker would give you it's chips
take a gander through here, should help with learning the contexts
got it
so for this next joker; i dont think there's a good like
baseline joker to compare it to
i want it toooo
gain mult when a playing card is destroyed
but also have a chance to destroy a playing card
like gain-gain or +X mult?
the joker itself gains mult.
playing mods is always a good idea bc if you ever decide to make one, you can reference other jokers to learn how to do X.
however, to gain mult, you can reference Flash Card:
SMODS.Joker {
key = "flash",
blueprint_compat = true,
perishable_compat = false,
rarity = 2,
cost = 5,
pos = { x = 0, y = 15 },
config = { extra = { mult_gain = 2, mult = 0 } },
loc_vars = function(self, info_queue, card)
return { vars = { card.ability.extra.mult_gain, card.ability.extra.mult } }
end,
calculate = function(self, card, context)
if context.reroll_shop and not context.blueprint then
-- See note about SMODS Scaling Manipulation on the wiki
card.ability.extra.mult = card.ability.extra.mult + card.ability.extra.mult_gain
return {
message = localize { type = 'variable', key = 'a_mult', vars = { card.ability.extra.mult } },
colour = G.C.MULT,
}
end
if context.joker_main then
return {
mult = card.ability.extra.mult
}
end
end
}
Rn, not really.
you could look at glass joker
specifically if you wanted a joker to gain mult you would doconfig = { extra = { mult_gain = 2, mult = 0 } }, and then to gain some mult you'd do:
card.ability.extra.mult = card.ability.extra.mult + card.ability.extra.mult_gain
return {
message = localize { type = 'variable', key = 'a_mult', vars = { card.ability.extra.mult } },
colour = G.C.MULT,
}
the return { ... } is uneccessary if you dont want a lil' message to appear.
if context.remove_playing_cards then
if you want any playing card removed
and always the way to give mult after the hand, or more simply "when it's the jokers turn" is:
if context.joker_main then
return {
mult = MULT_TO_GIVE -- <-- change this to what mult you want to give
}
end
and you could do it in 1 line like: if context.joker_main then return { mult = 5 } end
?
the context is "remove_playing_cards".
if you dont want it to be blueprint compatible
add and (not context.blueprint) to that first if condition
also checking and card.ability.extra.mult > 0 is unnecessary
returning mult = 0, will do nothing for you on its own
no add the other if back, that is necessary, what was not necessary was the and card.ability.extra.mult > 0
just add it back and remove the and card.ability.extra.mult > 0 part
yes
good
also,
if you want a lil message indicator
replace
if context.remove_playing_cards then ........ end
with:
G.E_MANAGER:add_event(Event({
func = function()
G.E_MANAGER:add_event(Event({
func = function()
-- See note about SMODS Scaling Manipulation on the wiki
card.ability.extra.mult = card.ability.extra.mult + 3
return true
end
}))
SMODS.calculate_effect(
{
message = localize { type = 'variable', key = 'a_mult', vars = { card.ability.extra.mult + 3 } }
}, card)
return true
end
}))
return nil, true
legit just like this
oh remove the tabs
here:
G.E_MANAGER:add_event(Event({
func = function()
G.E_MANAGER:add_event(Event({
func = function()
card.ability.extra.mult = card.ability.extra.mult + 3
return true
end
}))
SMODS.calculate_effect(
{
message = localize { type = 'variable', key = 'a_mult', vars = { card.ability.extra.mult + 3 } }
}, card)
return true
end
}))
return nil, true -- This is for Joker retrigger purposes
i mean, ig it's fine, but like.
make it look good atleast
but technically yeah
easy enough
Anyways gtg, gl with your dev journey!
put your event and SMODS.calculate_effect inside the context check
Currently whenever anything happens, the game will run the code, you'll need context to prevent that, so put the part of your code under the context you want it to trigger
How do I get the localized name of a rarity?
localize('k_modprefix_key')
so where do i. put. it.
hell.
Honestly if you're just adding mult during scoring, rework all but the context.remove_playing_card
Put it under
context.joker_main
The code here isn't how we currently make jokers
if context.remove_playing_cards then
card.ability.extra.mult = card.ability.extra.mult + 3
end
if context.joker_main then
return {
mult_mod = card.ability.extra.mult,
message = [what you have there is fine but the mult has to have the 3 removed]
}
end
i typed this on mobile please dont kill me
also the 3 in + 3 should be a new card.ability entry
localisation or smtg
so like. this.
sobbing....
i meant what you have FOR the message is fine
but remove the 3
I CANT LUA IM A BALATRO FAN
you can also
message = '+'..card.ability.extra.mult, colour = G.C.RED
fucking crying bro
return {mult = card.ability.extra.mult}
you didnt change anything clawg 
yes
YOURE SO CLOSE
REMOVE THE RETURN IN REMOVE_PLAYING_CARDS
does it eork?
i mean
youre blocking the message
the joker works. kind of.
it just pops up a uh
+3 mult
and then another message that says +3 mult
even after destroying the cards the mult didnt change
yeah you don't need the message in your return table
wait uh
smods handles basic scoring messages automatically now
mult_mod here correct
yuh
(or you can do what fish is saying, sure. i don't recommend it unless you want the message to be something other than the basic "+X mult")
how would i make the message say something else. like text
-# this can be disabled if you need to have multiple variables in one message!
mult on its own does smth? i always used X_mod + message with the variable added to a string
message = "text"
localize() generates the text from localization tables, but ultimately you're just returning a string
yuh this
it does now, yes
smods added that feature a while back
to add the variable to the message use .. (also works with any variable that isnt a table(?))
awesome

