#đ»ă»modding-dev
1 messages · Page 183 of 1
...oh nevermind, the 'Hello World!' part is supposed to be localization stuff
i cant just directly put a string there it seems
i hate my life
restarting balatro be like
are there any mods that change the soul sprite dynamically which i can check?
ok so genuinely, how do i call my own localization in this case?
Guys would you say "reveals the next 5 cards in your deck at all times" is a legendary
uhhhh i remember theres a mod with an Exotic joker that always gives you max hand when you are in a round, but the more rounds pass with it in the slot, the more damaged it becomes
thats all i remember, im sorry :(
ehhh, wouldnt say so, probably Rare at most
Oops! The game crashed:
[SMODS _ "src/utils.lua"]:1547: bad argument #1 to 'ipairs' (table expected, got nil)
Additional Context:
Balatro Version: 1.0.1n-FULL
Modded Version: 1.0.0~ALPHA-1415c-STEAMODDED
LĂVE Version: 11.5.0
Lovely Version: 0.6.0
Platform: Windows
Steamodded Mods:
1: Alphabet Jokers by Runtem [ID: alphabet_jokers, Version: 1.0.0]
2: DebugPlus by WilsontheWolf [ID: DebugPlus, Version: 1.3.1, Uses Lovely]
Lovely Mods:
Stack Traceback
===============
(3) LĂVE function at file 'boot.lua:352' (best guess)
Local variables:
errhand = Lua function '(LĂVE Function)' (defined at line 553 of chunk [lovely debugplus.console "console.lua"])
handler = Lua function '(LĂVE Function)' (defined at line 553 of chunk [lovely debugplus.console "console.lua"])
(4) global C function 'ipairs'
(5) Lua field 'calculate_end_of_round_effects' at Steamodded file 'src/utils.lua:1547'
Local variables:
context = table: 0x38b4d1c0 {cardarea:unscored, end_of_round:true}
(6) Lua field 'func' at file 'functions/state_events.lua:168'
Local variables:
game_over = boolean: false
game_won = boolean: false
(for generator) = C function: builtin#6
(for state) = table: 0x38b4d198 {1:unscored, 2:table: 0x389b7878}
(for control) = number: 1
_ = number: 1
v = string: "unscored"
(7) Lua method 'handle' at file 'engine/event.lua:55'
Local variables:
self = table: 0x3890ea58 {start_timer:true, timer:TOTAL, blockable:true, trigger:after, func:function: 0x38944380 (more...)}
_results = table: 0x389a3a58 {blocking:true, pause_skip:false, time_done:true, completed:false}
(8) Lua method 'update' at file 'engine/event.lua:182'
Local variables:
self = table: 0x386d59b0 {queue_last_processed:50.749999999998, queues:table: 0x386d59d8, queue_dt:0.016666666666667 (more...)}
dt = number: 0.0133364
forced = nil
(for generator) = C function: next
(for state) = table: 0x386d59d8 {unlock:table: 0x386d5c90, other:table: 0x386d6638, tutorial:table: 0x386d6148 (more...)}
(for control) = number: nan
k = string: "base"
v = table: 0x386d5cb8 {1:table: 0x3886e558, 2:table: 0x3869b7e0, 3:table: 0x39016380, 4:table: 0x38f3efa8 (more...)}
blocked = boolean: false
i = number: 11
results = table: 0x389a3a58 {blocking:true, pause_skip:false, time_done:true, completed:false}
(9) Lua upvalue 'gameUpdateRef' at file 'game.lua:2524'
Local variables:
self = table: 0x38176750 {F_GUIDE:false, F_CRASH_REPORTS:false, F_QUIT_BUTTON:true, HUD_tags:table: 0x387e3af8 (more...)}
dt = number: 0.0133364
http_resp = nil
(10) Lua method 'update' at Steamodded file 'src/ui.lua:84'
Local variables:
self = table: 0x38176750 {F_GUIDE:false, F_CRASH_REPORTS:false, F_QUIT_BUTTON:true, HUD_tags:table: 0x387e3af8 (more...)}
dt = number: 0.0133364
(11) Lua field 'update' at file 'main.lua:996'
Local variables:
dt = number: 0.0133364
(12) Lua function '?' at file 'main.lua:935' (best guess)
(13) global C function 'xpcall'
(14) LĂVE function at file 'boot.lua:377' (best guess)
Local variables:
func = Lua function '?' (defined at line 906 of chunk main.lua)
inerror = boolean: true
deferErrhand = Lua function '(LĂVE Function)' (defined at line 348 of chunk [love "boot.lua"])
earlyinit = Lua function '(LĂVE Function)' (defined at line 355 of chunk [love "boot.lua"])
My game is crashing
Here's the new joker:
SMODS.Joker {
key = "letter_j",
loc_txt = {
name = "J",
text = {
"This joker gains {C:mult}+#1#{} Mult per unscored {C:attention}Jack{}",
"{C:inactive}(Currently {}{C:mult}+#2#{} {C:inactive}Mult){}"
}
},
config = { extra = { gainMult = 6, mult = 0 } },
rarity = 2,
atlas = "alphabet_atlas",
pos = { x = 1, y = 0 },
loc_vars = function (self, info_queue, card)
return { vars = { card.ability.extra.gainMult, card.ability.extra.mult } }
end,
calculate = function (self, card, context)
if context.cardarea == 'unscored' and context.individual then
if context.other_card:get_id() == 11 then
card.ability.extra.mult = card.ability.extra.mult + card.ability.extra.gainMult
end
end
if context.joker_main then
return {
mult_mod = card.ability.extra.mult,
message = localize { type = "variable", key = "a_mult", vars = { card.ability.extra.mult }}
}
end
end
}
uh wait, is localize supposed to look like that?
yes
every other joker i have has this kind of localization message
i see
and the game didnt crash
well my best guess is probably at the if context.cardarea == 'unscored' and context.individual then part
we need to call our police officer mr. john smods
im returning localized messages like this
Update smods
are you saying that to me
Yes
i already have the latest version
No you donât
am i doing it right, never used localization in my life (probably a bad idea not learning this earlier)
are you thinking of this card maybe?
oh, no
shiet
uhh lemme try finding it myself lol
no dont worry about it
nvm updating helped
no no, im relatively certain i can find it
do you mean to change it once or like in a loop
oh that would be great but dont make it take too much of your time
so the card is a quest and i want it to have a soul card when its completed
so basically once
i mean soul sprite
thanks
do you maybe remember its name?
im booting up the mod to find, wait a bit
....oh im fucking dumb, its a legendary
not sure why i thought it was an exotic, sorry đ
this worked for me but maybe its wrong
card.children.floating_sprite = Sprite(card.T.x, card.T.y, card.T.w, card.T.h, G.ASSET_ATLAS['prefix_atlas'], { x = 4, y = 5 })
this will come in handy hmmm
I did this which does both, and seems to be working as I intended, so far at least
G.E_MANAGER:add_event(Event({
trigger = 'after',
delay = 0.1,
func = function()
card:shatter()
return {remove = true}
end
}))
end```
alright so can anyone explain to me how 'b_unlock_all' doesn't work, but 'ml_demo_thanks_message' does? they are both inside balatro's localization file
is it simply because b_unlock_all value isnt a table?
So I just defined a sticker.
How do I apply it to a playing card?
how would i check if played card is a certain suit or rank
:is_suit() and :get_id()
ok
is there any way to check if a certain sound is playing or not ?
q: would this work the way i'm thinking it would in my head
i need it to check if the probabilities after buying an oops all 6es is bigger than the saved probabilities
i know most of this is right i'm just thinking the context.after might not work the way i was thinking it would
i need it to pull the probability after tbh
Donât call the shatter yourself pls
And remove the event
Literally just need the return
Hey, me again. I've tried looking into the Steamodded documentation, as well as the example jokers mod and the source code, but I'm still pretty stumped on how to add a new Tarot card (specifically one that does the reverse of the strength card, reducing the rank of two cards by 1). Does anyone have an example mod that just adds a couple Tarot cards that I could investigate and learn from?
My mod has some if you want, check SDM_0's Stuff (dev branch preferably), in the data/consumables file
if anyone has a quick answer for this i'd suuuuuper appreciate it :p just wanna make sure my code is accurate LOL
SMODS.Consumable with set = 'Tarot'
i haven't worked on my mod in like well over a month
No you canât combine contexts like that, theyâre entirely independent
so how could i do this in a way that makes sense? i need it to get the probabilities AFTER an oops all 6s is added to the deck
the way it works is basically it supplies a minimum that it needs to surpass based on the highest value in order to change itself
I donât remember if the buying context is before or after the add to deck call
gotcha
add an event maybe
hmm
and do the logic in there
yeah
wait should i just do context.added_to_deck?
is that even a thing
lmao
it should b e
honestly it should be
ok so how WOULD i get an event after oops all 6es is added,,, i'm sowwy my ass has forgotten this workflow
also did the context.other_card work in this context with buying_card
that's the only thing i need an answer to actually
probably better to do the check for oops outside of the event so you don't create a bunch of useless events even when your joker won't do anything
also, you need to return true at the end of your event so it doesn't loop forever
Change name to key
But also I donât think this event will work
It wonât be at the right point of the stack
any ideas on how to make this work realistically?
looks like it'd work to me? what would be wrong about it
Well first you need to check the order of the things, whether itâs calculate or add to deck first
i mean i can test it rn
does anyone know if there is a mod that dynamically changes the soul sprite of a card that i could look at (jen's jokers do something similar but not what im looking for)
vaguely, you could try something similar to what my mod does to dynamically change charge bar sprites
Did you try this?
how do i make it so those text can be colored?
thanks so much ill try it
hi, this code is triggering on the last two cards, and not only the last card:
if context.cardarea == G.play and context.other_card == context.scoring_hand[#context.scoring_hand] and context.other_card.edition == nil then
what'd i do wrong? đ
oh my god i can just do this card.children.floating_sprite:set_sprite_pos({x = 1, y = k - 1})
its from the hunter
thanks both of you so much
oh thanks for the info
it works
well, the first half works
let's see if it gets that the probabilities are higher
because yeah i forgot to write a print statement to check for that oops
cool
hmmm try adding context.individual?
idk
yep it works
bump, i cant figure this out
{C:(variable)}text here{}
i tried doing that, doesnt seem to work
that worked, thanks!!
https://discord.com/channels/1116389027176787968/1336746173008056320
Neato joker does it maybe you peek its code
Hey, is there any fast reload game (with mods/mod) ingame (like keyboard shortcut or smth) - smods?
holding M
Thank you <3
Neato jokers referenced lets go
M
nevermind, we still have ghost cards
why is my lua always crashing and attempting to call a nil value
it should tell you which line is throwing the error
nah it just says its attempting to call a nil value
further down
i'm not sure how to make this joker bp compatible without this happening
i guess i could specifically have it not copy any instances of brainstorm but iunno
Thanks that helped out a lot! I was able to get the actual function part of the tarot working, now just gonna figure out the sound and animation as the change just happens instantly. I can probably figure that out on my own but if anyone has any pointers that would certainly be welcome!
You could track a iteration count and prevent the effect if it has triggered more than the maximum amount of times
i think blueprint stops if it triggers more than the amount of jokers or smth
guess that could be one way to do it but it still seems a bit scuffed LOL
hi again, my sound effect isn't playing when my status text appears, how do i fix that?
play_sound("bfdi_david", 1, 0.5)
card_eval_status_text(context.blueprint_card or card, "extra", nil, nil, nil, {
message = "Nope!",
colour = G.C.FILTER
})```
it doesnt really help, since its just the description part of the mod which already allows you to color text đ
when is it playing
if i have five of them, they all play their sounds before the first message
have you tried adding it in an event
yeah so im researching joyous spring (yugioh mod)
joyousspring mentioned
play_sound()
return true end }))```
goated mod, gg
ty let me know if u need any help
i cant replicate how you colored the text đ
cheers, will try that :D
if you have your text in a description table and call localize it will make an ui object with the color
okay, cheers, it worked! thanks!!
i basically copied your code over, except maybe the return part, to see if it works, but they just turned out to be two empty white boxes
...oh, now that i think about it, maybe the big difference here is that mine has more than 1 line in the localization stuff
lemme try smt
misc dictionary doesn't work with color
basically the game parses the descriptions beforehand and makes text objects with the colors, scale, etc
it doesn't do that with the dictionary
or at least not that one
oh damn, i never knew about that, thanks a lot đ
gotta make sure im more careful with putting stuff in localization then, jeez
someone help me im gonna go crazy wtf is this nil value
now that i'm thinking about it, how does swashbuckler dynamically get the sell values of all cards in the joker slots?
there has to be a way to actually get if cards are added in real time other than the buying_card
yeah that could be useful
card.sell_cost?
no sorry that was for what i'm doing
no that's not what i'm saying
like right now my joker is wrapping buying_card to get when a joker is added to the library, but that's REALLY annoying for debug reasons and also would NOT work with an ankh so
like this only works with buying it
surely there's no way that swashbuckler checks every tick for the values LMAO
Putting them in an event might help
it uses the update function which runs at every frame
CRYING
man this nil value is getting me angry now
ok i'm gonna create my own event then i'm not scared of this shit
this shit's so inefficient
I actually just figured out how to set the animation and sound up :) Now I'm trying to figure out a few bugs. I can select more than two cards at once and decrease the rank of all of them, and if I try to do it on a 2 some very weird things happen đ
i don't think you need to use update? you should just be able to only do it right before it's relevant (loc_vars and calculate)
what's the log
You can use a can_use function on your consumable and check if the highlighted hand has 2 cards like this
can_use = function(self, card, area, copier)
if G.STATE == G.STATES.SELECTING_HAND or G.STATE == G.STATES.TAROT_PACK or G.STATE == G.STATES.SPECTRAL_PACK or G.STATE == G.STATES.PLANET_PACK then
return card.ability.extra.max_highlighted >= #G.hand.highlighted and #G.hand.highlighted >= 1
end
return false
end,
card.ability.extra.max_highlighted is a config value
Oh sweet, thanks!
(I should update the code since there's a SMODS booster thing iirc)
where?
mfw created my own context event because i'm not going to lag my game like that
lmao
after mod?
yes
Really stupid github question
How do you just link to "latest release" directly, or is it just "link to the main zip download"
Just figured out the last bug (that I know of), when converting Aces they'd still turn into 2s, and when converting 2s they'd stay 2s but with no values, and then crash the game. The mod (hopefully) fully works now! Thanks for all of your help!
If this is what you meant, then the card doesn't get removed from the deck, and does't get destroyed
return {remove = true}
end```
Copy the link of the green button that says "latest" on the release's page
Itâs the wrong context, should be destroying card
ah I see
I'm guessing opening the page is the best I'll get? I was thinking like a direct download link but if that's not a thing that's fine
You can also link to the direct download if you want I guess
BMM might have some mods that directly link to the latest release download, idk what the link looks like by memory
Ah I see, looking here I can always link to the correct page, but making sure it's always the latest file is down to the dev using the same zip file name it seems.
thanks! I think that worked. Now I just need to add my custom enhancement to wherever it determines whether the shatter animation or normal destroy should be played
Add shatter = true to the enhancement definition iirc
oh that's just a built-in thing?
As of the other week yeah
it's crazy how many things are just not on the documentation page
because xchip also exists despite not being on the smods.enhancements page
Writing docs is boring I have better things to do đ
is there some introduction to making a mod, im wanting to make some card editions ideas i had
No just in the main part with the other properties like key, pos etc
that was going to be the next thing I tried
You should be able to link to the zipped source code, that name doesnt change
(it'll be the source code instead of the provided zip, but that shouldn't matter for balatro mods afaik)
it did not work đ
key = "crystal",
atlas = "New_Enhance",
pos = {x = 1, y = 6},
loc_txt={
name="Crystal Card",
text = {
"{X:chips,C:white} X#1# {} Chips",
"{C:green}#2# in #3#{} chance to",
"destroy card",
}
},
shatter = true,
config = {
extra = {
x_chips = 2,
shatter_prob = 4
}
}```
ahhhh
Okay cool, I'll check with Skyline later too but I'll dig a little more and see if I can find it (basically I'm just going through and trying to set up a bunch of older mods for the Mod Manager - like there would be on the old github list and stuff - so I don't wanna be linking to unstable/changing versions if possible)
somehow, this still didn't do it...
Show code pls
key = "crystal",
atlas = "New_Enhance",
pos = {x = 1, y = 6},
loc_txt={
name="Crystal Card",
text = {
"{X:chips,C:white} X#1# {} Chips",
"{C:green}#2# in #3#{} chance to",
"destroy card",
}
},
shatters = true,
config = {
extra = {
x_chips = 2,
shatter_prob = 4
}
},```
Oh that will be fine
0.9.8 by the looks of it? in manifest.json
1.0.0 ALPHA 1415c
Yeah thatâs fine
hmmm
well this is the full enhancement if it makes any difference
key = "crystal",
atlas = "New_Enhance",
pos = {x = 1, y = 6},
loc_txt={
name="Crystal Card",
text = {
"{X:chips,C:white} X#1# {} Chips",
"{C:green}#2# in #3#{} chance to",
"destroy card",
}
},
shatters = true,
config = {
extra = {
x_chips = 2,
shatter_prob = 4
}
},
loc_vars = function(self, info_queue, center)
return {
vars = {
center.ability.extra.x_chips,
G.GAME.probabilities.normal,
center.ability.extra.shatter_prob
}
}
end,
calculate = function(self,card,context)
if context.main_scoring and context.cardarea == G.play then
return { x_chips = card.ability.extra.x_chips}
end
if context.destroy_card and context.cardarea == G.play and pseudorandom('crystal') < G.GAME.probabilities.normal/4 then
return {remove = true}
end
end
}```
And it doesnât shatter?
nope, just the normal dissolve
any help on this?
SMODS.Enhancement {
key = "burnt",
atlas = "enhancements",
pos = {x=0, y = 0},
replace_base_card = false,
no_suit = false,
no_rank = false,
always_scores = false,
config = {extra = { mult_mod = 1 }},
loc_vars = function(self, info_queue, card)
return {
vars = { card.ability.extra.mult_mod}
}
end,
calculate = function(self, card, context, ret)
if context.cardarea == G.play and context.main_scoring then
local text = G.FUNCS.get_poker_hand_info(G.play.cards)
card_eval_status_text(card, 'extra', nil, nil, nil, {message = localize('k_upgrade_ex')})
if not instant then
G.E_MANAGER:add_event(Event({trigger = 'after', delay = 0.2, func = function()
play_sound('tarot1')
if card then card:juice_up(0.8, 0.5) end
G.TAROT_INTERRUPT_PULSE = true
return true end }))
G.GAME.hands[text].mult = G.GAME.hands[text].mult + card.ability.extra.mult_mod
update_hand_text({delay = 0}, {mult = G.GAME.hands[text].mult, StatusText = true})
delay(0.6)
end
end
end
}
this is the code btw
Can you send me the state events dump file?
Whatâs it supposed to do?
Every time one card with that enhancement scores, the scoring hand gets +1 mult permanently
Oh the shatter stuff isnât changed
it works (the ui doenst update properly) but the problem is that when you use a planet card the mult will go to the mult of the level
Iâll check mine later and update smods if necessary
could I just write a lovely patch for this
G.hand.highlighted[i]:shatter()
else```
Yeah that will happen due to how heâd coded hand levels are
IT WORKS LETS GOOOO
just add an or statement to check if your enhancement is there i guess
yeah that was my plan
i looked for that to see if i can patch it, but didnt found it
Iâll fix it this evening on the smods side @minor furnace
thanks!
I'll see if I can get a patch working in the meantime, and then take it out once the update comes
if SMODS.has_enhancement(G.hand.highlighted[i], 'm_glass') or SMODS.has_enhancement(G.hand.highlighted[i], 'm_[your_mod_prefix]_crystal') then
G.hand.highlighted[i]:shatter()
else
maybe something like this should work
yeah that was what I was planning to try
nice
Literally just wait itâs just wasting your time otherwise
Itâs like a thirty second job for me to change when Iâm not on mobile
although I think it's "m_reverse_crystal" because of the prefix
And you wonât have to change any of your code
it gives me practice patching
it's a fun exercise at least
oh well the pattern didn't match I'll just move on then
Hello, can I ask - how could I get the playing card negative info blurb on the joker?
I'm currently using G.P_CENTERS.e_negative info queue with the Joker but I cannot find what the playing cards use
how do i get the current mult and chip values of the current played hand? i already have the post-scoring stuff set up, i just forgot, it's blind something i don't remember
i need to store it in a variable :p
wait isn't it just mult and hand_chips
am i stupid
Strangely enough, I cannot find the exact string that the playing card blurb is using in the translation files, only times +x hand size appears is with yellow or red colors
its an smods thing
Thank you!
how can you get the final mult and chips from a hand after all other jokers have scored actually
can't figure out the context event for some reason
đ
bc uh
if context.final_scoring_step and context.cardarea == G.play then unfortunately this doesn't work
pretty sure this one's locked to plasma iirc
hand_chips and mult are globals you can access, which are the currently scored chips and mult as shown in the boxes respectively.
why are they tables lmao
any idea why this is giving nil?
talisman 
OHHH
how can i retrieve the real value from the table from talisman
thanks actually i wouldn't have caught that lmao
You should use all values in your calculations as to_big(number) instead
Do not try reverse it
no i need to like retrieve the value
my card takes the max value of chips and mult played this run and sets all hands to use that instead of anything lower, or use whatever is higher
This not how it works
If you're interacting with big numbers, you must do all stuff in big numbers
ok well how can i compare one to another
because that's the only thing i need to do here
if to_big(hand.chips) > to_big(my_stored_chips) then end
do u have loc_vars
And to prevent mod from crashing, you must include on start of file
to_big = to_big or function(n) return n end
that would be the issue yeah, i do not
how can i represent it at
mk
oops didn't mean to press enter
how can i represent it as a string lmao
or does it do that automatically
nevermind
i see it
mhm, thats the problem, add it and it should be fine
No clue
did you define the local variables?
yeah that solved it, thanks y'all
now to make the card actually work 
may i ask, is the card you showed above not just Canio? Could you just pull the code from there?
it's Caino-based and I've been tweaking the code from that one, idk why its not proccing yet, currently working through that
silly question, how do i make a food joker that like, decays over time a-la ice cream/popcorn?
(namely, how could i like, designate it as a food joker, for the sake of other mods.)
:to_number()
what do you mean designate as a food joker? Popcorn/ice cream aren't labelled as some other rarity type or something but rather have added event managers that destroy the card when a condition is met
there are a few mods that consider "food joker" to be like, its own weird little sub-category, and i was wondering if there was like. Some agreed-upon thing to designate those as food jokers. Or if this would be something for those mod creators to handle themselves. ;P
like, we just don't wanna be caught off-guard if there is some unspoken agreement of "oh you mark a food joker as isFoodJoker = yea, in the code for other mods". totally understand if this is just me overthinking it tho, fdsgjhk
Oh if other mods have other ones they're handling that separately from how the base game does it. From what I can tell the basegame just treats them like any other joker, but with a destroy event after a certain number of rounds (though admittedly I am trying to make my own but am running into an issue with it never updating the value, so take what I say with a grain of salt)
i think there was and it changed at some point
you could snag the extra values from every joker in play every hand, then snag them again and compare to see if they go down, but that would probably grab a few jokers you dont intend
does anyone have any sort of template/reference for making art for seals
it's not in the community assets
if you know the current method lmk and i could perhaps add that in :o
waow
Ain't no way I got this code to work first try
ok so how do i make my joker trigger directly at the end of all other calculations
like i want it to be after all x mults and all other effects
bc that's the last step here
there's context.after, right?
ah I still need the "nope"
yeah but i couldn't get that one to work
cue Mattman scream
LOL
really?
interesting
mattman scream
yea
context.final_scoring_step doesn't work either
i can try again though!
this is what it says in the wiki
yeah i tried that too
didn't do anything
but also i don't want it to be after scoring, i want it to be after all effects
i want it to be the final effect that occurs, like plasma or smf like that (well, actually i want to do it after plasma)
actually i could just look at cryptid there's a card that does something similar
the answer is always cryptid sigh
Posted about an issue before , but I've narrowed down the cause and am looking for some advice. I'm writing a joker that replaces Gift card, and the code for that is here
"j_gift",
{
loc_txt = {
name = "Skips",
text = {
"Current xmult #1#",
"Gain xmult is #2#"
}
},
atlas = "Wada",
config = {extra = {xmult = 2, xmult_gain = 1}},
pos = {x = 0, y = 0},
loc_vars = function(self, info_queue, card)
return {
vars = {
card.ability.extra.xmult,
card.ability.extra.xmult_gain
}
}
end,
calculate = function(self, card, context)
if context.joker_main then
return {
xmult = card.ability.extra.xmult
}
end
if context.after then
card.ability.extra.xmult = card.ability.extra.xmult + card.ability.extra.xmult_gain
return {
message = "Charged up!",
colour = G.C.CHIPS,
card = card
}
end
end
}
)
I'm getting a crash for trying to perform arithmetic on a table extra, and I finally figured it out.
Gift card's base config has this:
j_gift= {order = 79, unlocked = true, discovered = false, blueprint_compat = false, perishable_compat = true, eternal_compat = true, rarity = 2, cost = 6, name = "Gift Card",set = "Joker", config = {extra = 1}, pos = {x=3,y=13}},
where the sole variable is stored under the name extra. I tried changing the name of the inner table on my joker to extran, so like this:
config = {extran = {xmult = 2, xmult_gain = 1}},
but that didn't solve the issue. Is there an easy way to fix this?
Cryptid has something like this iirc
too real
ok i found it
if context.cardarea == G.jokers and not context.before and not context.after and not context.debuffed_hand and hand_chips and mult then
this is what cryptid has for sync catalyst which essentially does this exact thing
context.final_scoring_step is the plasma step
if it "doesn't work" then you're likely using it incorrectly
no i got it
100% this will work
well uhh, it sure does say Nope somewhere on the screen
not context.before and not context.after is probably what eremel is referring to
in reference to 'food-like' jokers, do you know why the above code seemingly never actually procs? config is config = { extra = { money = 10, adj = 2 } }, but seemingly this calculation/update never actually happens at end of round
gotcha
i'll check it out in a sec
literally just copied this from the regular wheel of fortune, not sure why the offset is wonky
attention_text({
text = localize('k_nope_ex'),
scale = 1.3,
hold = 1.4,
major = used_tarot,
backdrop_colour = G.C.SECONDARY_SET.Tarot,
align = (G.STATE == G.STATES.TAROT_PACK or G.STATE == G.STATES.SPECTRAL_PACK) and 'tm' or 'cm',
offset = {x = 0, y = (G.STATE == G.STATES.TAROT_PACK or G.STATE == G.STATES.SPECTRAL_PACK) and -0.2 or 0},
silent = true
})
G.E_MANAGER:add_event(Event({trigger = 'after', delay = 0.06*G.SETTINGS.GAMESPEED, blockable = false, blocking = false, func = function()
play_sound('tarot2', 0.76, 0.4);return true end}))
play_sound('tarot2', 1, 0.4)
return true end }))```
I have solved this if you come across this in future (search terms: arithmetic, extra, xmult, gift card) - you can define the extra variable as 1 as normal, then proceed with keeping the rest of your variables in a table called e.g. extran. See below:
config = {extra = 1, extran = {xmult = 2, xmult_gain = 1}},
As a general question, is there a reason modded jokers usually have their variables as extra inside config?
Other stuff doesnt get stored by default iirc
I'm not sure I understand what you mean
To the save file. When exiting the game
okay, so if I were keeping variables like config = { cost = 2 }, I'd have issues loading a save?
it just wouldnt be there when you loaded
Double check whether .game_over actually exists would be my first guess
It might just not be provided instead of being false
@minor furnace fixed in 1418a
what code do you have
i might've broken it myself
hold on
lmao
no yeah it broke lmao
ok whateevr
i'll just bring it back
clearly it works bc cryptid has it and honestly idrc if it's bad practice bc if it works it works
did a git pull and saw 6 line changes to enhancement.toml so that's probably it then
it works in old calc but new calc it either wont work or will likely break with other mods
just do it properly
if context.joker_main then --score Xchips
xchips = card.ability.extra.Xchip
return {message = "TEST" }
end```
for whatever reason this isn't scoring xchips? everything else works just fine...
smods version?
oh yeah, wer're shattering cards now
You're setting xchips but not returning them?
I thought it existed, at least based on line 377 of the steammodded/examples/jokers page https://github.com/Steamodded/examples/blob/master/Mods/ExampleJokersMod/ModdedVanilla.lua
OH. i forgor the return đ
i got it to work
how hard is balatro modding? i kinda wanna look at it and get into it but im curious. it doesnt sound too difficult but idk
meh i'll fix it when cryptid fixes their codebase bc most of my players play cryptid
i'll make sure to add that they need the cryptid version of smods though in the release notes
I love actively developing for old-calc
i'm a college student i really don't give enough of a shit lmao
when it's convenient i'll fix it
Hmm, good point. Time to throw some prints in there then I guess
it is not convenient right now
is there a way to make a soundtrack mod where it can be toggleable like the cardsauce mod? I never used lua
so i tried prints but they also did nothing... I mean, are prints not just print(message) and it gets printed to console?
To the lovely console yes
... man this one is really thowing me for a loop, but I'll try again to see if I can get the print statements working
try changing it to not context.game_over
If the print is the first thing in your calculate and its not printing, sounds like your calculate isnt being called
OH
it's calculate = function(self, card, context)
you have them the wrong way round
but also change the game_over thing, nil doesn't equal false
HA damn genuinely I don't know when I ever would've caught that, huge so many thanks
Ugh good catch
lars do you remember any of the shop util stuff people wanteD?
like people wanted an easy way to icnrease the number of boosters
I've done booster count, voucher count and free reroll count, but I don't remember what else there was, if anything
Ah
I dont think I was ever part of those conversations tbh
Though it might be nice if steamodded automatically supported big shops?
iirc the shop starts going off-screen at like 8 items in the main shop
hmmm I'll take a look
wuh oh, what are we doing wrong here
hmmmm it seems I have already patched this somewhere
I'm assuming cryptid isnt in your mod list?
you have code after a return
it is not
the message, you mean?
I remember doing it sometime ago but I don't know if its in smods or something locally I have
you can never put code after a return in lua
I don't recall this being in a recent smods version
lemme see if I can track down what's handling it
wait i think i spoted PART of the issue
it's in ortalab for some reason đ€Ł
okay no, that made the error happen earlier and i feel like i was not meant to remove that return.
you can also just call card:start_dissolve() to remove it iirc
?
I mean... If someone else can do it you should be able to too?
okay, it works now, but i can't figure out how to display the "Eaten!" text. what do?
i dont know how to work lua or code though. Its confusing
I'm 99% sure you can just change your code to be
if card.ability.extra.Xchip <= 1 then
card:start_dissolve()
return { message = localize('k_eaten_ex') }
end
So what's your question
how can i do something to a joker when it is triggerd
how do you have that in vs code?
where is that
the message doesn't appear correctly (it only appears after the full dissolve), but that mostly works
extensions
try adding instant = true in the return
Would this code make it so that each card in the scoring hand be one random rank?
local rank = pseudorandom('washing', 2, 14)
for k, c in ipairs(context.scoring_hand) do
assert(SMODS.change_base(c, nil, rank))
end
what is the table telling me
so if i do other_card it will use the said triggerd card?
that works! though uh. in testing all this i accidentally stumbled upon a bug for an entirely unrelated thing
for whatever reason, this doesn't detect cards self-destructing; like food cards being eaten, or gros michel going extinct. uh. How would we do that?
local card_remove_ref = Card.remove
function Card:remove()
if self.area and (self.area == G.jokers) and not G.CONTROLLER.locks.selling_card then
SMODS.calculate_context({joker_destroyed = true , card_destroyed = self})
end
card_remove_ref(self)
end```
Instead of checking self.area check self.added_to_deck
If you do thar you'll have to add another check for if the card is actually a joker
how would we go about that? we mean, outside of just appending another "and" check--that much is obvious--we mean like, what variable ;P
if self.ability.set == "Joker" and self.added_to_deck and not G.CONTROLLER.locks.selling_card then
Food jokers get removed from the jokers card area before Card.remove is called, which is why checking for the area doesn't work
but added_to_deck gets set to nil in Card.remove, so you can use that if you hook at the start
is this a custom context?
Yes
that worked, thank you very much!
put it in remove from deck
Danton will now re-imburse you for your Gros Michel.
(sentence that makes a LOT of sense, we're sure.)
As in hook into Card.remove_from_deck?
Why would that be better
the table is just telling you whats i the context table
Most likely if number too big you'll get infinity
saves on checks
do custom enhancements automatically get added to the G.P_CENTER_POOLS["Enhanced"] enhancement pool?
performance impact's gotta be negligible tho, but i see what you mean
so if i do other_card:set_edition("polychrome") it will set the triggerd card to polychrome
e_polychrome
oh ok
context.other_card
oh ok
the answer is yes
yeah it isnt a huge deal
am i able to make it so that a steel card permanently gives more mult when it's held in hand?
how can i "upgrade" an edition
iterate through the pool of editions, find the index of the edition you currently have, increment it by 1, and index the pool of editions with that to figure out the next one (and if that doesn't exist you wrap around to 1)
alternatively do center.order
i think they should be the same
wuh oh. when hovering over spring mushroom in the collection menu when you haven't started or loaded a run yet, the game. Dislikes that.
calculate = function(self, card, context)
if context.discard and #G.consumeables.cards + G.GAME.consumeable_buffer < G.consumeables.config.card_limit then
G.GAME.consumeable_buffer = G.GAME.consumeable_buffer + 1
G.E_MANAGER:add_event(Event({
trigger = 'before',
delay = 0.0,
func = (function()
local _card = create_card('Uncannycard',G.consumeables, nil, nil, nil, nil, nil, nil)
_card:add_to_deck()
G.consumeables:emplace(_card)
G.GAME.consumeable_buffer = 0
return true
end)}))
card_eval_status_text(card, 'extra', nil, nil, nil, {message = "Feur", colour = G.C.PURPLE})
end
end
I kinda need help, I'm trying to have an effect on my custom seal similar to the purple seal, but for some reason it creates two cards instead of one, I might just be missing something but this code is mostly similar to the purple seal so I'm confused
this didnt work
you need to check that G.playing_cards exists before doing the for loop
in loc vars
uhhhh, how do i do that
if G.playing_cards then
this doesnt work either
you're on old calc jank mode
oh shit
i've tried various combinations of adding commas but nothin's worked out
the syntax is
if your_condition then
-- your code
end
THERE we go
i'm not sure. i'm so new to this whole thing and im afraid if any question is gonna be very dumb to the smartest masters of lua coding, even though i'm new.
If you're completely new, I suggest checking out some "how to code in lua" tutorials
this sounds hard to do
i'm only trying to do one thing and that's to make a soundtrack pack that can be toggleable
Regardless your 2 options are either to ask someone to do it for you, or learn how to do it yourself
are you good at that kind of stuff?
hiya, this ability triggers at the right time, but doesn't destroy the cards it triggers on â how would i do that?
if context.destroying_card and context.destroying_card.ability.name == 'Glass Card' then
card_eval_status_text(card, "extra", nil, nil, nil, { message = "Pranked!", colour = G.C.FILTER })
return nil, true
end```
I won't be doing it for you (I'm on vacation and dont have a pc or laptop with me) if that's what your question is
ok
can someone tell me why this doesn't display correctly?
the green bar which should show you what's your last used tarot doesn't show up
at all
I believe you need to return the main_end
in the vars or outside?
outside, like main_end = main_end
also probably need a local on where you create it
im having a bit of a brainfart but is it possible to get card variables in Card:draw ?
self.config.center.config seems to just get the default values and not anything that's been modified since the card was spawned
trying to create a simple deck mod, what am i missing here? what could cause this?
it seems like it's not an error in my code necessarily, but the way it interacts with the game i think?
this is literally the whole code rn
do i need to add actual functionality before i can see the deck ingame or..?
ok now it displays the red bar with none but crashes when it has to display any tarot names
what does the crash say?
yall asking advanced questions, i feel like a neanderthal among you lowkey đ
Loc txt needs to be a table, not a string
Just put some brackets around the string and all should be good
oh yeah u right about that too
(bump)
Oh yeah random question I've been wondering but haven't tried yet: Is it possible to pass a sound variable in the extra table of a card_eval_status_text call?
do you need to use card_eval_status_text?
ah that worked! cheers ^^
-# the answer is yes but you shouldn't need to use that jank ass function
wait why not what's the correct func to use
just use the return table
but what if you want to do multiple / outside of calculate?
Unfortunately yes due to the timing of things
then you can use SMODS.calculate_effect({table here}, card_to_act_on) if outside of calculate
if you need to chain messages you can add extra = {new table here} to your return table
Here's the code I've currently got that's causing this ask
It's probably bad but to be fair it worked
I mean card_eval... works it's just jank to use
like you could replace it with SMODS.calculate_effect({message = "success!", colour = G.C.GREEN}, card) there and avoid all the nils
And judging from the answer, I would be able to add a sound field?
yes
sick, thank ya
it'll also work if you just add sound to cest
Okay y'all, a final quesiton I have before I stop bothering y'all for a bit; I'm trying to make a version of abstract joker with money instead of mult; if I test it in a game it works fine, but it crashes my game if I view it in the collection not in a game. I know I have to do something where I tell it because it can't access #G.jokers outside a game it displays 0, but I'm unsure how to actually enforce it
I'll probably just end up refactoring to use calc_effect. Again, thanks
working as intended đ
Check if G.jokers exists
do i just add an if/else based on "if not G.jokers then"
That would work yes
how can i make post trigger happen well On trigger
it happens beforee they trigger
I don't think you can? That's why it's called post trigger?
post is after đ
Wait you're saying "post triggers are happening before the actual trigger"?
yeah at the beginign of scoring
How can you tell that's happening?
Sure
post triggers definitely happen after
Yeah that's my guess too
Oh
G.jokers and #G.jokers.cards * 2 or 0
OH G.jokers is the check for if the variable exist isn't it
Yeah if G.jokers exists it'll return the length of cards * 2, otherwise 0
huh yeah, thats def me thinking things work like they would in python, or close to it
edition still happens at start bu the mesage works ass expected
add the delay argument to the edition
?
immediate to false
is it set on to default?\
what do i do for the event
im having an immense skill issue trying to get a soul_pos to not be centered on the base card.
Anyone know what to change to get a Sprite() to draw at a offset? i was trying with it's X and Y but that doesn't seem to do anything
also modifying stuff with the draw_shader call i don't really get any usable results :v
now I get a slightly different error that I'm 'attempting to index field vars (a number value)'... is that some keyword issue with count or some syntax error?
put {count}
ah it is literally that easy
Lua... truly one of the programming languages of all time
oh no genuine skill issue on my part no blame to Lua... but it is truly a language of all time
i love ambiguous typing!
does zig do that
i don't think zig does that
oh it does
sick
It is extremely strange but I do value Lua since imo it's less painful to learn as a first language compared to the presumed hell of immediately jumping into C or C-ish languages.
(technically I learned Python first but that language is fake)
I do wish the Lua LSP was less... atrocious to work with.
I also found myself really enjoying Lua
i put it in an event and caused an infiinite loop so il juyst deal with it happening early
But it comes with all the problems of dynamically typed languages that's annoying
I think dynamically typed languages are only annoying if you already know how to use a typed language
Yeah, then you know what you're missing out on
"Fuck it we ball" typing is fun sometimes
I don't mind it now I'm used to it again though, it's actually remarkably useful in some situations
It is but making large projects with it becomes more difficult imo
Hobby language moment
I just wished the LSP got the memo because it genuinely think Lua is statically typed (or at least tried to force it).
lua doesn't have too much bullshit with iterators and it doesn't force an abysmal amount of OOP onto you which i love
is self.deck_preview the thing I need to check if I want these custom card areas to move down too when hovering the deck?
I'm not insane for thinking that nothing should be able to hit the else to print "after" right? like, I have a yes or no question as the other 2 if options, is the score nil? If yes, return "score is nil" if it isnt return that it isnt and do the rest of the code
and yet, somehow, "after" returned
the errors are about booting up the shop
Maybe nil * nil is nan, but idk
accualy its just applying of the shader happens early the sound happends at the right time?
lua doesn't need types. Mike Hall didn't sacrifice all he loved so you could sully luajit with types.
I don't think anyone said lua needs types tbh
i despite python imo
i learnt it and liked it for a bit but
man
that solved that
and apparently G.GAME.blind.blind isnt a value I can use as a number
itâs like inconsistent with other languages and inconsistent with itself
and then also slower than other languages
it came back saying i was comparing the value of the hand with nil
Ideally for really big projects, it can be beneficial to use both weakly and strongly typed languages tbh. My last job had us use this big C#/C++ engine, but had a whole big typescript scripting engine hooked into it
G.GAME.blind.chips is what you need I think
That said I'm kinda shit at backend programming and have hated what little of it I've done, so I'm glad to not have to work with that
this isnât about your error (kinda) but if youâre a beginner iâd recommend learning about early returns in your code to help
keep it meet
neat**
the more nested your code is the harder it gets to manage
but it does come down to preference
Can concur, early returns are neat
for me its a preference thing, the more nested it is the easier it is for me to chunk it apart
I can go "okay we are still within that function/loop/if/etc's bounds" just by seeing the tier
But if your entire function is inside a condition then you might as well early return
the messy part comes from when I just flat shove chunks of the base game code in the middle of that
okay as ACTUALLY a final question... this currently changes money not once per round but continuously... is there a way to fix that without making some weird local variable and doing tracking that way? also is context.start_of_round not the opposite of context.end_of_round?
add and context.main_eval
There's no start_of_round, there's setting_blind tho
Or first_hand_drawn if that's what you want
oh yeah first hand drawn is best
Neat đđœ
need help with making kinda unique joker code
I want to make a joker that gives you a 1 dollar at the end of a roudn for every face card that gets destroyed
Like Canio but for money instead of mult
can you hide the counter on the secondary areas?
for some reason my upgrade triggers every time the for loop checks for scored cards. any ideas why?
I was thinking the same thing
Holy shit
some of the types of area don't have them
don't see what you are talking about
oohh
at least it's not attempting TO REACH A NIL VALUE
LIKE IT'S BEEN TRYING TO FOR THE PAST HOUR
your context check isn't specific enough
what is that?
So you have three rows of cards?
Kinda
For some reason, it's triggering the effect to lose money twice at the end of the round
key = "pyrite",
atlas = "New_Enhance",
pos = {x = 2, y = 6},
loc_txt={
name="Pyrite Card",
text = {
"{C:mult}+#1#{} Mult",
"when {C:attention}played",
"{C:money}-$#2#{} if this",
"card is held in hand",
"at end of round",
}
},
config = {
extra = {
mult = 6,
p_dollars = 2
}
},
loc_vars = function(self, info_queue, center)
return {vars = {center.ability.extra.mult, center.ability.extra.p_dollars}}
end,
calculate = function(self,card,context)
if context.main_scoring and context.cardarea == G.play then
return { mult = card.ability.extra.mult}
elseif context.end_of_round and context.cardarea == G.hand then
return { p_dollars = -card.ability.extra.p_dollars}
end
end
}```
Silly question; how can we make it so that a specific calculation doesn't happen when Blueprint'd, but others will? Some scaling jokers are acting rather strangely with blueprint, and while it's funny, it's also causing some cards to become... A little counter-intuitive.. ;P
what's a context check?
is there a more specific context I can use than end_of_round?
oh for consistency I shoul probably call the extra h_dollars I suppose
how is it not specific
i really don't see the problem with my code
if context.cardarea == G.play then this triggers on every single context where you have played cards
how do i make this work then
look at the calculate guide and find the timing you want and copy the check
well its the one im using if i want to upgrade the joker when a certain card with suit played
I'm making a version of balatro called IRLtro that uses ARUco tags on cards to read them automatically with a webcam, and since I need to recreate the entire game in python for it to work it's currently playable in the command line
am I allowed to publically share the repo? it doesn't use any source code from the game, I've just taken all of the information on the wiki and recreated it
then you need context.individual and context.cardarea == G.play
ok thnks!!
do you know of any other than title and title_2 (which have a setting for not selecting cards)
not off the top of my head
ok
before it triggered between 3 and 6 times now its only 2 but still the same problem
You need to add not context.blueprint as a condition to whatever part of your code shouldn't trigger on Blueprint
im completely lost here
what does your code look like now?
where can I iterate though each joker, right after they are initiated?
is there a way to set a playing card in the deck to be a steel card in debug mode while in a run?
(or just, any enhancement?)
looking better
nvm forgot i can use tarot cards LOL
you don't need to loop inside individual contexts
looks great! I wonder if the shaded area could be disabled too
wait so i remove the for loop?
yes, for every card you play you loop over the entire hand right now
How can I check if each in-game joker contains a specific var?
What is it about this context that loses the money twice at the end of the round?
if context.main_scoring and context.cardarea == G.play then
return { mult = 6}
elseif context.end_of_round and context.cardarea == G.hand then
return { dollars = -2}
end
end```
Or I guess, I should dig through and see what Gold cards do
is there an easy way to check when the player's money decreases?
bumping this, I've been digging through the G.blind_select_opts table, but can't find the things I'm looking for
use context.playing_card_end_of_round instead of context.end_of_round
anyone know how to fix this with the images
Thousand Year Door đ
problem with that is i check if card v is a certain suit, but there is no v without the for loop
use context.other_card
ok i feel really dumb now
what's the context for checking something about each individual card in a played hand before score starts getting counted?
broad question, in lua, to put an if after another one, do you need particular syntax or not?
I got this error saying [SMODS _ "src/game_object.lua"]:363: Failed to collect file data for Atlas Garty_Jokers and I have no idea what that means
as long as you put an end to close the first one then you can put another if after. Or if the conditions are mutually exclusive, you can put an elseif after the if, and then an end after the final elseif
or do you mean the if statement is nested?
i'm trying to make a joker that scales when a specific modded tarot card is used, what i've got now doesn't seem to be working
i tried using context.consumeable.ability.name as well (which worked when i switched it to a vanilla tarot), is there some specific SMODS thing i need to use to make this work
if context.using_consumeable then
if context.consumeable.key == "tarot_grungler" then
card.ability.extra.xmult = card.ability.extra.xmult + card.ability.extra.extra
card_eval_status_text(card, "extra", nil, nil, nil, {message = localize({type = "variable", key = "a_xmult", vars = {card.ability.extra.xmult}})})
return nil, true
end
end
Nvm I forgot to change the name of an image
Does anyone know how to change the speed of the music in game?
up, if anyone knows anything about this
if anyone is wondering, you can get the tag if skipped can be found in G.GAME.round_resets.blind_tags, win chips is get_blind_amount(G.GAME.round_resets.ante)*G.UIDEF.current_blinds().nodes[i].nodes[1].nodes[1].nodes[1].nodes[1].config.ref_table.mult*G.GAME.starting_params.ante_scaling, reward is G.UIDEF.current_blinds().nodes[i].nodes[1].nodes[1].nodes[1].nodes[1].config.ref_table.dollars and the name of the blind is G.UIDEF.current_blinds().nodes[i].nodes[1].nodes[1].nodes[1].nodes[1].config.ref_table.name
maybe it's being called twice
that's the thing, I don't know why
I'm only discarding one card, and most of the code is like purple seal , and this one works well
Alright time to get a bit technical (maybe). I have two jokers that act as blueprints, but they both copy the ability of cards whose positions can change. My current solution is to store the card table in a variable, and that works fine unless the player closes the game and continues the run later, causing a crash. I believe I have been told that variables that hold tables are cleared on restart so I'm sure that's what's causing it, but I'm unsure of how I should go about fixing this. I had an idea that involved just storing the card's key instead, but I'm not sure how I should calculate the card's ability from that key. Any ideas? Here's the code of one of the two jokers to give an idea of what I'm working with
how do you get a cards cardarea from the card itself? (in this context SMODS.current_mod.set_debuff(card))
card.area
how do we add a new deck exactly?
Is there any way to stop just the music's pitch from changing? There's a custom song I want to import and it always changes the pitch and stuff
There was a line of code that I changed that fixed the issue but then the pitches for other sounds were messed up and Idk if there is another way around it
oh my god. psychic link achieved
we also went to the well of Big Penny for joker design inspo
looks awesome
please do not change yours. we think it is objectively hilarious if there are two distinct pennies that double in size that function differently based on mod. of all the concepts to run into the "50 minecraft mods that all add copper ore" conundrum
oh yeah this one increases size every time too like physically it gets bigger but its not doubling its not even multiplicative
it just gains 0.1 scale
AWESOOOME
i keep adding rare jokers that are annoying to implement and by keep i mean its happened only 1 other time so far
surely the scale is limited
i mean to get high enough for it to matter youd have to go past ante 38 cause the max scale getting it on ante 1 would put the scale at 4.9 which is honestly not too outrageous
Making a Mult equivalent of a Stone card, but it didn't score when I played it with a pair
key = "marble",
atlas = "New_Enhance",
pos = {x = 3, y = 6},
loc_txt={
name="Marble Card",
text = {
"{C:mult}+#1#{} Mult",
"no rank or suit",
}
},
config = {
extra = {
mult = 10
}
},
replace_base_card = true,
no_rank = true,
no_suit = true,
alaways_scores = true,
loc_vars = function(self, info_queue, center)
return {vars = {center.ability.extra.mult}}
end,
calculate = function(self,card,context)
if context.main_scoring and context.cardarea == G.play then
return { mult = card.ability.extra.mult}
end
end
}```
misspelled always
i've tried the following:
if context.using_consumeable then
if context.consumeable.config.center_key == "The Grungler" then
card.ability.extra.xmult = card.ability.extra.xmult + card.ability.extra.extra
card_eval_status_text(card, "extra", nil, nil, nil, {message = localize({type = "variable", key = "a_xmult", vars = {card.ability.extra.xmult}})})
return nil, true
end
end
and have added this to my consumable:
config = {
center_key = 'The Grungler'
},
still doesn't appear to be working, is there anything i'm doing wrong
Woahhhhh this is reaaaaaaaally cool
so you can disable Oops All 6's while playing glass then? đ
you can do many things
So I am still working on that joker that changes card suits
With this layout of Jokers, a played Heart card currently follows this order of events:
displays as heart
changes to spade
triggers Lusty Joker
triggers Reverse Card
triggers Wrathful Joker
I am looking to make it follow this order instead:
displays as heart
triggers Lusty Joker
triggers Reverse Card
changes to spade
triggers Wrathful Joker
any idea what needs to change about this code to cause the "changes to spade" element to not trigger until the reverse card is triggered? (and yes I know that the return currently triggers an Upgrade! message, I'm gonna fix that once the card functions correctly otherwise
vivian!
move the message and colour out of extra?
does anyone know if it's possible to give a deck a soul layer? i had a really cool effect in mind, but uh, just trying to insert pos = { x = 1, y = 0 }, soul_pos = { x = 2, y = 0 } did nothing
You'd probably have to make some injects into Back rendering code to get that to work
whoof, guess i'm not doing that unless steamodded updates to vastly streamline that particular process ;P
this changed nothing
how do i update the info center to match a dynamic tarot card? I want to make it so that a Joker gives you the tarot card matching the suit you have the most of (if you have more Spades, it'll give you the World), and I want the info_center bubble to update to the appropriate tarot card (like if it'll give you a Moon card, the info_center will show you the description of the Moon)
more on the actual functionality side, how can i make it so the player starts with a random joker of a given (custom) rarity? i mean i could also start them with the card that SPAWNS that given rarity but that is vulnerable to a Perkeo Moment
let em perkeo
for common, give em a riff raff
i mean, i need it to be for a custom rarity. the closest thing i can think of to compare it to is Wormhole Deck in Cryptid.
I think you should just do a normal create_card() process the deck's apply function
For the rarity parameter, here's what the vanilla rarities act as
Not entirely sure how a custom rarity would factor into that tho
making this mod pack is def guna push me to the edge lmao
how do we run the apply(self, back) function and put create_card() in it, exactly? (this is the first time we've made a deck so we have NO idea what the syntax is here)
(also would it be like, bad to use SMODS.add_card over create_card() for this)
how would i use localize to get the name of a tarot card by key? would it be like
localize{type = "Tarot", key = 'c_world'}?
okay, yeah, the syntax here is all wrong. how do we apply?
you forgot an end
How can I check if each in-game joker contains a specific var?
Are you checking for a returned value or just one that's being stored?
A vaule thats being stored, one each joker would have.
What exactly are you looking for?
if joker has a stored var then i want to add it to a list, the key. if it doesn't have the var it doesn't add the joker key to the list.
my problem is doing this for every joker before the game begins (a run)
tried with both it in and out of the add_card but nothing. still not sure if we even set up the apply correctly... ^^;
i think all jokers fall into a set you can for loop through, then you can be like "if i in joker_set has var == X, add to list"
ah I see it. The function declaration should be apply = function(self, back)
game boots, but on trying to start a game with the deck, this happens.
i did this for a similar effect though create joker can be replaced with something similar too G.E_MANAGER:add_event(Event({ func = function() local card = create_card('Joker', G.jokers, nil, nil, nil, nil, jester_key, 'jest') card:add_to_deck() G.jokers:emplace(card) card:start_materialize() G.GAME.joker_buffer = 0 return true end}))
Try putting the create card function in an event , that's how Magic Deck handles it
You don't need to do the loop or all the other fancy stuff, just the SMODS.create_card
What's the cheapest way to find every joker the player has that is part of a valid list of jokers? Literally just nested for loops?
I don't know off the top of my head if there's some fun lua shenanigans I can pull here
Yeah probably
well instead of a list you can do a map like local valid_jokers = { j_joker = true }, and then do, if valid_jokers[G.jokers.cards[i].config.center_key] then for example
that'd avoid using nested loops
Oh true! Lua shenanigans
add_card does emplace it
Ahhhhh ok
How can I check if each in-game joker contains a specific stored var, before the game begins (a run)?
I have the descriptions for my jokers stored in a localization file, but how do i get the file to find them? The jokers just keep showing up with blank description boxes.
do the keys of the localizations match this format:
j_(your mod prefix)_(joker key)
i
forgot the j_
oop
does it need to be like in the actual joker too?
like this key format? or the text and descriptions of the jokers?
like
localization key= j_prefix_joker
does the SMODS.Joker also now have to use j_prefix_joker, or can it still use joker
it can just be joker
ok thanks
cause if you do j_prefix_joker for the joker key the localization key has to be j_prefix_j_prefix_joker and that's a bit ridiculous
how do i have a shader be just a transparent image overlaid on top of the card? I wanna have like a "watermark" effect with one of the jokers
imma bump this one
could you do this?
The key isn't a card object tho and would error
i know that why its geting the cards.config.center_key
which gets the jokers key that equals the set key
in the players jokers
Ah I see what you're trying to do now, but that would require the copied joker to be in the card area. Ideally they retain the effect even if the copied joker isn't in the jokers area
i see there was a joker that would copy a random joker from the player collection, i'll try to find it could be of use.
how do i make a hand that is more than 5 cards playable?
You want to change G.hand.config.highlighted_limit
I've done that, and it's not letting me play it.
This is the code: (It's just the entire 52 card deck poker hand from Cryptid)
I found it
Additional Jokers for Balatro. Based on Steamodded v1.0.0 - GitNether/nethers-jokers
This is just the hand type?
this should also prove useful to me
Yeah, here's the joker if it's needed.
SMODS.Joker{
key="draw_full",
loc_txt={
name="Draw Full",
text = {
"Draw {C:attention}literally your entire deck{}",
"into your hand",
"Also, lets you select your entire #1# card deck."
}
},
rarity = 1,
atlas="spark",
pos = { x = 0, y = 0 },
config = {extra = 52},
center = {config = {extra = 52},},
cost = 2,
loc_vars = function(self, info_queue)
return { vars = { math.max(1, math.floor(self.config.extra)) } }
end,
calculate = function(self, card, context)
if not context.blueprint and not context.retrigger_joker then
if context.first_hand_drawn then
G.FUNCS.draw_from_deck_to_hand(#G.deck.cards)
return nil, true
elseif G.hand.config.card_limit < 1 then
G.hand.config.card_limit = 1
end
end
G.hand.config.highlighted_limit = G.hand.config.highlighted_limit
+ math.max(1, math.floor(self.config.extra))
end
}
Why are you changing the highlighted_limit to highlighted_limit? There's no change there
So just change calculate() to add_to_deck()?
Just the part where you change the highlighted_limit
You should also add a remove_from_deck function to reset it when you get rid of the card
The Play Hand button is still grayed out.
did you set up the function correctly?
this is what i changed it to:
calculate = function(self, card, context)
if not context.blueprint and not context.retrigger_joker then
if context.first_hand_drawn then
G.FUNCS.draw_from_deck_to_hand(#G.deck.cards)
return nil, true
elseif G.hand.config.card_limit < 1 then
G.hand.config.card_limit = 1
end
end
end,
add_to_deck = function(self,card,context)
G.hand.config.highlighted_limit = G.hand.config.highlighted_limit
+ math.max(1, math.floor(self.config.extra))
end,
remove_from_deck = function(self,card,context)
G.hand.config.highlighted_limit = G.hand.config.highlighted_limit
- math.max(1, math.floor(self.config.extra))
end
It looks like you declared the functions incorrectly
Unfortunately doesn't look helpful for my uses. I appreciate the effort tho
I thought it was kind of what you were trying to do, well not randomized but very similar
declared the functions the correct way, same thing.
add_to_deck = function(self,card,from_debuff)
print("added")
G.hand.config.highlighted_limit = G.hand.config.highlighted_limit
+ math.max(1, math.floor(self.config.extra))
end,
remove_from_deck = function(self,card,from_debuff)
print("removed")
G.hand.config.highlighted_limit = G.hand.config.highlighted_limit
- math.max(1, math.floor(self.config.extra))
end
-# quickly added some debug prints, yeah it's working
It's still storing a card object as a variable 
okay so i really want to draw a sprite over a card like the soul or legendary cards (but instead of it floating it'll be on the card like an enhancement) and i'm struggling with trying to comprehend how the draw functions work, does anyone have any idea how one might accomplish this?
I would like to avoid having it be a sticker but if I have to have it be a sticker because editions aren't flexible enough I can
Ah I forgot you need to make a lovely patch to allow playing more than five cards. You can use mine
[[patches]]
[patches.pattern]
target = "functions/button_callbacks.lua"
pattern = "if #G.hand.highlighted <= 0 or G.GAME.blind.block_play or #G.hand.highlighted > 5 then "
position = "at"
payload = "if #G.hand.highlighted <= 0 or G.GAME.blind.block_play or #G.hand.highlighted > G.hand.config.highlighted_limit then "
match_indent = true
times = 1
Gotcha, makes sense, thanks!
np
would anyone know why the Jokers.png is making the jokers appear like this instead of the individual jokers
your jokers have a soul_pos that goes to the position of those floating sprites in the atlas, correct?
Can you show the code for the cards?
its very bare bones so if i dont have what i need it would not shock me đ
im still learning LUA
so any tips is appreciated
Also it looks like the cards are misaligned on the sprite sheet due to other cards bleeding through on the bottom
Yeah for legendaries you just need to add a soul_pos that has the coordinates to the floating head on the spritesheet
ooooh okay
doe sthe sprite sheet seem to be made incorrect?
i didnt do the art im just the coder guy lol
Here's an example of a correct soul_pos addition
ooooh
Yeah. The cards are misaligned or too close together (or both)
okay thankyou
The general rule of thumb is the art should have a 2px border on each side
It should total to 4px between cards
okay so the white should be 2 and then grey 2 aswell
cuz i know the joker should be 71x95
and i think these are like 2-3 less
okie dokie
This should show it a bit better
and if that 4px gap doesn't work then try 2px. My sprites might be a little oversized
okay thankyou
this is guna be hard but its very fun!
if i have any other questisons am i good to ask you
Sure. Just ask here cuz I'm one of many lol
no issue lol
print("Adding Achievement !!!!!!")
SMODS.Achievement {
key = "true_clown",
loc_txt = {
name = "A True Clown",
text = {
"Complete a Gold Stake run without using any Jokers"
},
unlock = {
'???'
}
},
atlas = "BoingularMisc",
pos = {x = 0, y = 0},
bypass_all_unlocked = true,
hidden_text = true,
unlock_condition = function(self, args)
-- and (args.stake or self.GAME.stake or 1) == 8
if args.type == "add_card" and args.args.ability.set == "Joker" then
print("Mel: Adding Joker!")
G.GAME.current_round.mel_true_clown_status = false
end
print("Checking: "..args.type)
print(G.GAME.current_round.mel_true_clown_status)
return args.type == "win" and G.GAME.current_round.mel_true_clown_status
end
}
I have the above code for a custom achievement, but in the Achievements menu it shows "ERROR" as the description, and unlock_condition is never run, even with Achievements enabled in the menu (or else I'd be seeing a lot of debug messages when the game first starts)
is context.after for when a played hand is done scoring
how do i make a info_queue for a tag?
info_queue[#info_queue + 1] = G.P_TAGS.tag_rare for example
yeah, a Tag isn't a Center
Iâm trying to make a simple skin pack, and I just imported the few jokers Iâve made so far into the game. But for some reason only one of them is actually changed in game? Iâm wondering how thatâs possible since all jokers are on the same PNG file
Can you send me your mod files so I can experiment?
I have a minor annoyance due to insufficient information about it on the web. I have successfully merged a ton of skin mods to change the appearance of the card but I cannot find any documentation on what the key for the actual 52 card deck is. Everything else has a name such as Joker for Jokers and Planet for the planet cards.
I only changed 2 jokers (and only one of them actually updated in game) and I changed all of the planet cards but none of those updated either
You can avoid the need for documentation by checking the game functions directly. If you open up Balatro.exe as an archive with 7-zip, Winzip, etc. all of the game assets and .lua files are easily readable
I believe it's G.playing_cards that you're interested in
Which joker did you change? The Lua looks good
I changed Scholar and Madness, only Madness actually changed
To change the planet cards, you have to use the Planet key. You spefied Tarot
ooooooooooh gotcha
Planet, spectral, Tarot. Even though they are all the same image
The one you change on the bottom right appears to be too large on the right side
The code is a doozy. I can probably figure it out but I was hoping someone could answer definitively
is there a way to make one rank count as another? (e.g. make a 8 count as a 9)
there might be a way to do it like smeared joker? check the code for that and use it but with ids instead of suits
how do i add a sticker and have it be like the perishable sticker? I have it set up to where there's calculate and set functions for the sticker but i'm stuck on how and where to declare
self.ability.drained = true
as a part of the Card object.
What do you mean?
The second image is too far to the right
Which image?
The one you added. Not the one from baldis basics
Madness? Thats the only one thatâs showing up in game though, so if itâs too far to the right why is that the one thatâs working?
Interesting. It loaded differently for me. Let me check
