#💻・modding-dev
1 messages · Page 180 of 1
I still do not understand why these lines are crashing my game when I hover over the custom joker
loc_vars = function(self, info_queue, center)
return {vars = {center.ability.extra.mult}}
end
whats the crash
thanks, I hate it
the bane of my existence isn't my ability to code, it's my ability to spell
anyway, is there any documentation on how calc_dollar_bonus works?
the amount of times I've thought I fundamentally don't understand the way balatro works, only to realize I just did a capitalization error is humiliating, honestly
check the code for the golden joker
question! i want to scale a value by rounds elapsed just in general--not how many you've cleared since getting the joker, but the literal round value. in pseudocode, i want 1 + (0.05 * Rounds). so uh. How do I do that?
config = { extra = { Echips = 1 + (0.05 * idklol } },
oph my god jimbo on death row
Just got up an tried this. It works, but I'm trying to get it in the normal spot (like a Joker's sell button), not coming out of the bottom
Wonder if its tied to the cardarea
I do too
quick question, where did you guys learn about how to add UIs?
other mods lol
why are the message and the sounds not playing here?
oof, i see
the position of the use buttons is determined by card area yes
you need to hook into card:highlight i think
double check what you're passing into your Event
search bar "from:eremel" lol
fair
i'm yet to add the actual "progress a round" thing but this is tossing up an error. any idea why??
key = 'hermey',
loc_txt = {
name = 'Hermey',
text = {
"{X:dark_edition,C:white}^#1#{} Chips for",
"every round elapsed,",
"C:inactive}(Currently {X:dark_edition,C:white}^#3#{C:inactive} Chips)"
}
},
config = { extra = { scaler = 0.05 , roundsElapsed = 1 , Echips = 1 + (scaler * roundsElapsed) } },
loc_vars = function(self, info_queue, center)
return { vars = { center.ability.extra.Echips } }
end,
rarity = "stla_Stellated", --giving it special rarity
atlas = 'StellationJokers',
pos = { x = 8, y = 0 },
soul_pos = { x = 9, y = 0 },
cost = 34,
mmmmblueprint_compat = true,
calculate = function(self, card, context) --a lot of this shamelessly adapted from the waluigi joker from cryptid (tysm cryptid team <3)
--actually do the effect thing
if context.joker_main then
return {
Echip_mod = card.ability.extra.Echips
--message = "STRIKE!",
}
end
end
}```
the config can't check other config values
you need to change echips to not use scaler or roundeslapsed
yay
WIERD WAY TO SEE IT BUT IG
i have a question actually
i have seen a lot of mods adding an additional card slot below the consumable slots
if you use multiple of those mods, how would that work?
idk exactly but the ss from dimserenes pack are always funny
we should have a cardarea in every empty space
least modded balatro run
why do you even have a clock in balatro, isnt that a bit overkill 😭
for the time based joker effects, duh
oh
if its tuesday and 10pm set score to zero and set hands to zero
nah i have an idea, make a joker which works best from 3 am to 6 am
forcing people to stay up late to grind balatro
🗣️
the gambling addiction doesnt stop
"Gains +Infinity mult after 10 hours"
^^^^^ Infinity mult
actually, i just noticed something
mmmmblueprint_compat = true,
mmmm?
the curse of restarting the game
If i wanted to change the width and height of a joker like with photograph or wee, how do I do that?
fair enough
probably something to do with this
microwaved blueprint
i really wish there was a better restart command than holding M HLJKFGDS
M joker
nah same 😭
fixing that still yields the same error tho
People dont want to change it :))
Yeah hold M
SMODS.Joker {
key = 'hermey',
loc_txt = {
name = 'Hermey',
text = {
"{X:dark_edition,C:white}^#1#{} Chips for",
"every round elapsed,",
"{C:inactive}(Currently {X:dark_edition,C:white}^#3#{C:inactive} Chips)"
}
},
config = { extra = { scaler = 0.05 , roundsElapsed = 1 , Echips = 1} },
loc_vars = function(self, info_queue, center)
return { vars = { center.ability.extra.Echips } }
end,
rarity = "stla_Stellated", --giving it special rarity
atlas = 'StellationJokers',
pos = { x = 8, y = 0 },
soul_pos = { x = 9, y = 0 },
cost = 34,
blueprint_compat = true,
calculate = function(self, card, context) --a lot of this shamelessly adapted from the waluigi joker from cryptid (tysm cryptid team <3)
--actually do the effect thing
if context.joker_main then
return {
Echip_mod = card.ability.extra.Echips + (card.ability.extra.roundsElapsed * card.ability.extra.scaler)
--message = "STRIKE!",
}
end
end
}```
@grim remnant that should fix your issue, though you would have to add some other stuff to increase roundsElapsed after each round yourself
using context.end_of_round
...oh wait
the third line of text
i honestly still resorted to pressing X even when i knew the shortcut
holding M is still too annoying for me
😭
since i have vsc in the background, it will start writing "mmmmmmmmmmmmmmmmmmmmmmmm" in my code
skill tree? what is that mod?
grim
not my ss sorry no idea
oof
i do have to ask, how do i make it not "nil" while in the collection menu?
It's Grim
yeah i just remembered, no wonder why i dont see any mod with "rpg" when i searched in my mods folder lol
what's the code now?
Set a default value in the card's ability table or loc_vars function
if it's what bepis sent earlier it's missing a variable in loc vars
oh, it is?
im having a little silly but shouldn't this... work?
I want to override all music with custom track when joker is active
If i just return the big number, it plays always which is yes swag
am i brainfarting on how SMODS.find_card works??
Items/Loader.lua
-- Music override on Timeout joker
SMODS.Sound {
key = "timeout_music",
path = "timeout.wav",
select_music_track = function()
if next(SMODS.find_card("timeout")) then
return 2e512
end
return false
end,
}
Items/Jokers/Timeout.lua
SMODS.Joker {
key = 'timeout',
loc_txt = {
name = 'Timeout',
-- ...etc joker node
ah, what do i put in there then?
i didnt notice since im just fixing his error
just an assumption basing on how smods usually work, dont you need your prefix behind the joker?
like, j_(prefix)_timeout or something
you should add a second var that it's the same as what's after echips_mod =
and change the #3# for #2#
Not when you're defining your Joker. It gets added automatically
ah i see
i ned help BAD
my assumption was that was only needed for vanilla functions
doesn't seem to work
whats the problem chief
i mean, Echips is meant to be 1 + (0.05 * roundsElapsed), so i kinda wanna use that if i can help it?
i can get this Frickign Ui to go up for the love of god
do that in context.end_of_round so that it will add when the round is ending
was missing the j, you do need the full legal government name in find_card
i see
j_com_beepsterr_genmod_timeout worked :^)
can you move it at all or nah
damn, i havent read the ui stuff yet, passing this to other modders 🗣️
no i cant seem to move it at all
is that deadass a website link 😭
wait, i'm confused. do i just set the base variable values to 1 in the config = line?
no but im used to java namespacing so w/e
your joker's description will change too
whateber i do Nothing changes
You can reference galdur/Malverk for creating ui elements that have card areas
ig
for instance, +#1# Mult in loc_txt and in loc_vars, i have card.ability.extra.multGain
if i increase card.ability.extra.multGain in calculation, my joker's description should also change
btw is that how cryptid's glitched cards work? do they just randomize those values?
probably? i havent read too much cryptid's code yet
how could i change the card:set_base function from the card.lua to check if the new card is of the same suit as the card its changing it into
so uh, how do i increment roundsElapsed at the end of round? just?
return {
cardarea = G.jokers, -- G.hand, (G.deck and G.discard optionally enabled)
end_of_round = true,
game_over = game_over, -- true or false
roundsElapsed = roundsElapsed + scaler
}
end```
i just dont understand it at all
nope, you do this:
if context.end_of_round and context.cardarea == G.jokers then
roundsElapsed = roundsElapsed + 1
end
i dont think you need to return anything here
card.ability.extra.roundsElapsed
i mean, it's incremented by the value in scaler, not 1
still trying to figure this out. how do i change the hitbox of a card?
forgot that this is for roundsElapsed and not Echips ^^;
oof
you don't need roundselapsed at all in that case, just increment scaler
but also, remember to call card.ability.extra.roundsElapsed instead of just roundsElapsed like what OppositeWolf corrected
ngl, yeah
end of round crashes--oh, that'd be why
since you are getting that from here
ability isnt exactly config but you get my point ❤️
hmm.
No curly brace for if statements lol
at the end_of_round part
theres {}
oh also
you dont need "," here, since this is not a table
ohhhh
but if you want to get your joker to send a message, you would need to do
return{
message = "smt",
}
or a function which i forgot its name, try asking other people for this one, sorry 💀
localize(localization_key)
guys how do i check if two cards have the same rank please
do you know where i find what the arguments for the SPRITE and CARDAREA functions are?
If card:get_rank() == other_card:get_rank()
im a bit curious, what are you doing here?
the problem is that the cage has a hole in it
i actually thought this was your take on the loading/starting screen
😭
until i saw that 1/1
its going to be kidna like death card in incryption for balatro incrybed
Look at the sprite.lu and cardarea.lua files
is there a way to change the priority of an existing vanilla rank in a way that affects the order the card is drawn in in the hand and how much it scores
well, that's annoying
uh hello, im sorry if this is a bit of a silly question, i'm looking to make my own personal mod which would be a simple swap of how the cards look, my boyfriend is a big fan of the "moomins" franchise and i figured it'd be fairly easy to make the face cards into something moomins related for his birthday, problem is i do not know how i would go about that, i dont need an entire tutorial, i'd just really appreciate being pointed in the direction of where i could learn to do smth like this since im fairly new, thank you for your time and sorry if its a weird thing to ask.
oh shit, thats cool
message can't be a number
its cool for everyone but me
every feature is cool for everyone but programmers
makes sense, though how do i make it display a variable then? i want it to display "^Echips" :o
tostring()
the coolest ideas are often the ones most annoying to implement. there's probably a law for that.
thats because it is almost guaranteed your ideas have been considered by other programmers, but the work to implement them are so tedious they straight up gave up before even trying
i dont understand how to move it
how can i put the ^ before the tostring?
"^".. tostring(your number)
although you don't need tostring there
you can do echips = ... instead of Echip_mod and message (or might be e_chips, can't recall
it includes the default message
saviour
lies
Card:get_rank() is not a function that exists
it's Card:get_id()
get_id()
it fine
get_suit()
thanks
mostly functioning now, tho for some reason it's applying scaling twice and i can't help but feel like at some point i got my wires crossed in coding this
you calculate echips twice
i put it there to help track the variables themselves. ;P
oh wait i was going off the previous screenshot
wait so, scaling is increased twice after every round?
alright, that's definitely not the intended scale.
oh i see the problem here
no, it's supposed to be just. base value of 1, every round elapsed (tracked by roundsElapsed) add 0.05 (or scaler)
it actually expoentially increases here lol
wuh oh
ohhhh!
how od i move ui objects closer together
💔 would love to help but i understand shit about ui rn
i understand some but i can't really help without a pc to test rn
SMODS.find_card(card_key)
thats intresting
and that returns true/false?
next(SMODS.find_card(card_key)) tells you if you have any
but i want the 2 clamp things closer xd
it's a list of all the ones it finds
okay thanks
hey Eremel do you know how to move objects side to side?
Use context.before instead of context.individual
i forgot context.before was a thing 😭
How do i change the max playable cards to 6?
Just so you know that context.indivdiual is called for each card in scoring hand, that's why it happened five times
Is there an easy way to see what cards are upcoming in the deck?
almost entirely functional, but the end-of-round message doesn't appear for whatever reason. any idea why?
You could probably see how misprint does it to show it on its description
didnt work, now it does nothing
but haven't looked into it myself
wait what does misprint do
it has to be in a return (only the message)
im sorry for being annoying but i just dont understand how to patch this set_base function to check if the card before and after has the same suit
how would i do that? just, replace that line with return { message = "Buffed!" }?
yes
how do you change the order of which vanilla ranks are drawn into the hand
in steammodded
It shows the next card in the deck in the description
^?
local setbaseref = Card.set_base
function Card:set_base(card, initial)
local initial_suit = card:get_suit()
setbaseref(self, card,initial)
local final_suit = card:get_suit()
end
i think
anyone wanna hear the worst version of the combined balatro themes ever?
Try setting the trigger to 'after' in the event? But it should work, not sure why it isn't
trigger is already after though
sorry, meant 'before'
Play with the padding
If i understand right, it might be easier to hook the function instead of patching it.
https://forums.kleientertainment.com/forums/topic/129557-tutorial-function-hooking-and-you/
In LUA, it is a very important concept to understand that everything is a variable and all variables may be edited in runtime. This includes functions. With modding other peoples' LUA files, like Klei's basegame code, you may find yourself wanting to run your code before or after the original fun...
Oh, like this, haha
might be because of the rest of the code but i dont know the issue to begin with
hahah thanks either way ill surely watch the video because i know nothing about that
can you send a screenshot of the new code?
i tried putting it next to context.cardarea as well
wait, it seems like it is
it works now
though
i mean i went on lookin at this too but it worked after removing G.play
thanks
is there a smods function to open a shop?
I think just specifying context.before by itself will make it trigger more times in other situations, so just be aware of that
ik its fine if its triggered by any other thing but itself
Oh
Is there a reasonably vanilla way to easily know the chips result of the hand? I'm trying to do a joker that scales if the first hand doesn't immediately win the blind, but he only point I can find (without adding a new context in SMODS ofc) is by adding another event to the queue that will eventually follow when the game eases the player's total chips to the correct number. But I'm not seeing if there's any context that just straight up passes in the hand_chips*mult number that it eventually eases to
just calculate it manually
i did that and it says card is a nil value
I don't believe so
you can add a spacer above it to shift it back down if needed
fair
is there a way to affect cards not in hand?
if so i wanna see a spectral card that deletes your entire deck except for one random card and makes 51 copies of it
How can I add/remove an amount from the total payout? (at blinds end)
shouldnt this open a shop when s blind is skipped? it doesnt work and I cant find any work around
no
i think it should be self not card sorry
opening a shop is a lot more involved than that
how would I do Xchips? I heard there was support for it in steammodded
should it be with a hook or a patch, or can it be done within the joker calculate?
a patch 100%
return xchips = ...
oh i changed it but now it says the method get_suit is a nil value
that might be wrong then, I just copied what a comment earlier said but i can't look up what the correct method is rn
oh okay thanks either way for your time
oh, its not xchip?
'x_chips', 'xchips', 'Xchip_mod',
these are the valid keys
got it
where is the code when a round is ended and you go to the shop? cant find it
the closest thing that i found is update_shop in game.lua
in game.lua
look for the line G.CONTROLLER:snap_to({node = G.shop:get_UIE_by_ID('next_round_button')})
does anyone else please know how i could change the set_base function so i can check if the suit of the card is the same after the change?
okay thanks
How is the order of cards in the deck randomised? Or more specifically, what does it depend on other than the seed? Is it determined by the number of cards played and/or discarded previously during the run?
so thats the end of the shop, im more looking at the beggining
when you press "cash out" how does it open the shop?
sendDebugMessage("This is a debug message", "MyDebugLogger") what can i change here to make is display a variable value?
Ended up just patching the after context to pass the number I needed since the game already calculates it anyway
i cant seem to figure out why this isnt working, even when i take out the name check it still does nothing
context.other_card.config.center.key ~= self.key and
string.sub(context.other_card.ability.name, -2) == "er" then
return {
message = localize("k_again_ex"),
repetitions = card.ability.extra.repetitions,
message_card = card,
}
end
end,
Hey folks, looked around and couldn't figure out a solution based on existing problems.
The line in question - callback(context.blueprint_card or self, ret, context.retrigger_joker)
im going to take a different approach, anyone knows a mod that opens a shop when its not usually open?
is there a way to make it so a line of loc_txt only appears if a config setting is enabled?
So I want my consumable card to enhance a card into a custom enhancement, is this correct?
Try it out man
crash
Rip
Average day of programming tbh
Is this too strong even for a legendary?
It seems like a cool idea, and probably not too strong since it's only a single hand per boss blind. Personally though, I probably would've just had it select from a randomly drawn hand of regular hand size, consistent with how most of the other "select x cards from hand" things in the game work (like opening Tarot/Spectral packs)
That'd inevitably make it weaker though
does context.other_card have something for checking if the card has a seal?
or just like is there a way I can check if a card has any seal
this
card:get_seal()
what are the names of the card areas, or where do I find that in the vanilla code? (or does smods change that?)
wow how did you referenced that message that quick
i remembered it
it's the same in vanilla code, you check for card area by doing context.cardarea == [area]
G.jokers, G.play, G.consumables, and G.hand are the primary ones
there are others for contextual actions (G.pack_cards, G.shop_jokers, etc)
right, I'm looking at the shop ones, yes
What are you looking to do?
a thief joker that makes some of the cards free, but preferably not the voucher, and... not the packs, maybe
Probably, but I think right now its similar to Chicot in a way that lets you win the boss blind easily
trying to make a spectral card that has a 50% chance to either destroy a card or turn it into an edition but the destroying part of the code isn't working for some reason
So bad news is that you may need to add a custom context for that via a lovely patch, but I've actually made a very similar joker if that would help
It discounts cards in the shop
is there a function that gets a cards suit because get_suit() seems like it doesnt exist
you do card:is_suit('Suit')
So doing is_suit('Diamonds') on a diamond card will return true
is there something like that for seals too? now I need a specific check for purple seals
card.seal == "Purple" afaik
k, should be good
how do i write the debug message to show the value of a local variable for example?
@weak gate You'e using SMODS, correct?
yep
I did do a lovely patch already and it's successfully doing discounts in some cases; actually was stuck on deciding when the joker should say "Yoink!"
I basically just have my joker activate its effect right after tags are applied to the shop (Rare, Uncommon, Foil, Holographic, Polychrome, and Coupon tags)
I.E. basically do "Yoink!" at about the time that this joker says "Discounted!", roughly 48 secs in?
Yes; it shouldn't say it if the 1 in X roll failed for all cards and the joker didn't manage to set any card's price to 0
mhm mhm
although I'm interested in how you detected that you entered the shop; or is it that you're detecting when a card's price was set?
there's a context for shop rerolls (context.reroll_shop), so my card works in rerolls, but not on the first shop screen
I basially just have a "start_shop" context (constrasting the "ending_shop" context perkeo uses in vanilla) right here in game.lua after the shop sets all its cards, and then the card just evaluates here when it sets the cost of every card in the game to some amount, which it resets on leave
That said this one literally just reuses the inflation mechanic from the challenge, because that's built into the set_cost() function
You can do it far more directly though
Hmm, where's the code in the first screenshot? Inside a function that gets called inside a lovely patch?
is there a way to retrigger Joker effects?
It's inside Game:update_shop(dt), in the vanilla game's game.lua file
I inserted the SMODS context function at around line 3228
oh look, a zodiac card
Erm actualy it’s a Horoscope card 🤓☝️
Jokes aside we called them that cuz there’s already so many zodiac card mods
Hmm, and is there a start_shop = false somewhere else in code...? I don't suppose you'd like to show your source?
kinda clunky but i think is the best that i can do, any tip on how to improve it?
No, due the way lua works, it's literally just appending a new property to the context table, so this isn't in comparison to any vanilla code or anything
Did I miss something? This enhancement is supposed to make it so the scored card would gain +0.5xMult when scored but it doesnt for some reason
btw, no need of any patch, maybe a atch could do it better but i managed to get it like this
This is the lovely patch I used for it (I don't think it's against the rules to share files?
its not against the rules, dw
is there a way to append extra information to the description of a card with a custom enhancement? i've managed to do it to base and the vanilla enhancements, but my custom ones don't display it
how do i write the debug message to show the value of a local variable for example?
To the description, rather than as an info box?
yes, specifically something like hiker extra chips, where the info may not be present
If you're using debugPlus, sendDebugMessage('variable: '..[var_name])
Ahhh, your first screenshot was a lua dump that lovely made, I think? I was confused as to how it was formatted and syntax-colored like a normal lua file
thanks
How can I add/remove an amount from the total payout? (at blinds end)
Yeye it was, but in fairness both the lovely dump and the source code reference I use look the same formatting wise
I.E. this is the equivalent space in the vanilla code, obviously sans the context check
how can i specify a custom hex color for text in descriptions? :o
Is there an easy way to retrigger a joker's effect? something like returning "repetitions = 1"
You'd do something like this in the card's loc_vars function, and then in the actual localization string, you specify the color from that "colours" property passed in with {V:1}, the number depending on the index in the table
for context, i'm just trying to make "Stellated" here be a solid #CE001F
key = 'chuTeng',
set = 'Spectral',
loc_txt = {
name = 'Chu-Teng',
text = {
"Summons one {C:CE001F}Stellated{} joker",
"{C:inactive}(must have room)"
}
}
}```
(obviously this isn't done yet, i haven't even added functionality, but shhhh)
key = 'chuTeng',
set = 'Spectral',
loc_vars = function(self, info_queue, card)
return { vars = { colours = {HEX('CE001F')}} }
end
loc_txt = {
name = 'Chu-Teng',
text = {
"Summons one {V:1}Stellated{} joker",
"{C:inactive}(must have room)"
}
}
}```
why are none of the Enhancement Tarot card use effects like Magician and Chariot in the Card:use_consumable function of card.lua, and where are they?
also, does anyone know how to add a legendary card-esque layer to a consumable?
SMODS.Consumable {
key = 'chuTeng',
set = 'Spectral',
loc_txt = {
name = 'Chu-Teng',
text = {
"Summons one {V:1}Stellated{} joker",
"{C:inactive}(must have room)"
}
}
loc_vars = function(self, info_queue, card)
return {
vars = {
colours = {
HEX('CE001F')
}
}
}
}
end
Im definitely off on braces and tabbing
it's a coder's favorite game show: WHERE'S! THAT! BRACKET!
oh. wait, it was comma
wait no it's mad still?
where do playing cards keep their suit stored? i mean in which variable
card.base.suit
because when i use is_suit() to check wether it is a certain suit it says attempting to access field base a nil value
yea makes sense
but why does it say its nil
i mean you cant answer without code i know
3 commas in fact
one after loc_txt, one after loc_vars, one after pos
this is the code its in the set_base method in card.lua and the print(is_suit) is throwing the error
it says base is a nil value
it happens during initialization of the game if that is any help
should i maybe put an if so it doesnt do that line if it is nil?
nvm got it
what's the game setting for joker slots? essentially, I know it should be G.GAME.fillinhere but unsure what the variable is
yeah look how jank it is though
would it be possible to get the calculate function of a vanilla joker? so that i can essentially hook it when taking ownership
could you not just pull that from the source code, or from steammodded/examples if the joker you want is there?
well i would like to do it this way so it can account for any other mods that mightve like lovely patched it or something like that
i mean like, can i access a vanilla joker's calculate function from within the code itself, like you would when hooking a function
is there anywhere i can check for the available ui colors?
in globals.lua
oh fire
just check for "colours"
someone already answered but it doesnt seem to work, how can i check whether i have a certain joker/consumable
so if i have the joker do something
(SMODS.find_card("j_[mod_id]_[joker_id]")
and that returns true or false?
that is going to be either true if you have the joker or false if you dont
i guess so
okay thanks
ive never worked with consumables, but thats the prefix for them
works like a charm thank you so much
it returns a table of the cards, which registers as "not false" to boolean operations
you need to do next(SMODS.find_card("j_[mod_id]_[joker_id]")
oh so basically the other one will always be true pretty much
yes
ohh okay thanks
okay so i'm working on my first attempt at a booster pack and i'm a bit overwhelmed. if i want the pack to give specifically rare and legendary jokers, how do i declare the pool that the booster pulls from? or like how do boosters work in general LOL
i'm a bit lost
whats your current code
whats the difference?
haven't really done anything other than fill in some variables
How do I change the max playable cards to 6 with better calc?
find_card returns a table of all the cards or {} if none
in lua {} is truthy so it doesn't work to check if there's any. next() gives you the next value or nil if none
in create_card you need to pass SMODS.create_card, where you specify the set and all that stuff
return SMODS.create_card({ ... })
end,```
If you need a chance-based card creation, you can do something like this
```create_card = function(self, card, i)
return cs_utils.random_alignment(true, false)
end,```
local card_type = pseudorandom(pseudoseed('alignment'))
if card_type > .98 and architectable then
return SMODS.create_card({
set = 'Alignment',
key = 'ali_cs_architect',
no_edition = true
})
elseif card_type > .95 and chameleonable then
return SMODS.create_card({
set = 'Alignment',
key = 'ali_cs_chameleon',
no_edition = true
})
else
return SMODS.create_card({
set = 'Alignment',
no_edition = true
})
end
end```
does the wiki have a modding guide?
I'm trying to mess around with the drop rates of vouchers, tags, jokers etc and i have no idea what im doing
i mean the wiki is a modding guide
so the answer is yes
may i have a link?
thank you
np, any doubt just ask it
oh im sure theyl be plenty xd
speaking of the wiki, I can't find examples of adjustable joker slots... I'm trying to make a card that makes it -1 joker slot when held in hand. The Balatro source code has a joker_slot option but it looks only used in starting configurations, never mid-round from what I see
what im trying to do is manually lowering the chances of all the content from a particular mod from apearing ( cryptid ). like do you have any idea where should start?
honestly no idea
im also kind of new
I think if you look at the SMODS.rarity documentation/examples on the wiki? You could make a custom rarity and make all modded jokers that rarity?
thank you! and what about other content?
oh im looking at the documentation it works for any object (I think?)
thanks
is there a way to find the key of a card for example c_[modid]_[jokername] because i cant seem to write the right one and im not even sure where i put what it should be
is there a way i could guarantee a booster pack spawning for testing purposes?
i just keep winning blinds until it appears
lmao
you can spawn it using the debug console i think
are you using debug plus
wait i forgot i have like a super barebones debug
use that mod it makes life a 1000 times easier
but im not sure if it can spawn boosters i never tried it but it does everything so safe to assume it can
np
Im trying to do this patch but it doesnt seem to work, the dump file for game.lua doesnt change
is this how to do the thing
i guess its because the loc_text?
print or sendDebugMessage card.config.center.key
edited
has to be but no clue how to do it right
i think thats the name and the desc that appears in the shop
no clue how to get the one inside the pack tho
is that the group_name?
im just freestyling, never worked with packs
the link sends you to the line
dont scroll when you enter, wait for it to load
ah, im just illiterate thanks
or go to the line 1803
i did that and the thing you said before as to find a card
and its not working
can it find cards that are in a seperate area than the regular ones
because my mod has it in a new area
try group key also? i had a pack working with loc_txt before but i switched to localization before releasing it so i dont really remember
i set the group key to this just to have something lol
I assume SMODS.add_card{set = card.ability.extra.usedConsumes, key_append = 'spellbook'} is incorrect, what do I want to do to call a card saved in that list?
i mean i think you should use a localization file too yk
iunno i've never done localization so idk how it goes
if you're saving a key then this should work
SMODS.add_card{
key = pseudorandom_element(card.ability.extra.usedConsumes, pseudoseed("spellbook"))
}
although you might want to check if the pswudorandom key exists first
Perkeo2 uses pseudoseed instead of pseudokey so that might work
yes that
nnnnope, I get the same error
are you checking if card.ability.extra.usedConsumes has anything first?
so where's boosters
added if **card.ability.extra.usedConsumes[1]** and #G.consumeables.cards + G.GAME.consumeable_buffer < G.consumeables.config.card_limit then
which stopped the crash
the card doesn't do anything though
oh it should be config.center.key instead of just key when you add
really? A previous card I made that creates specifically Cryptid used just key
other i think
boosters go in other, yeah
assuming like?
how do i make a card that gives a random joker card of a given rarity, a-la soul? unsurprisingly my first try, uh, didn't go well. (i'm yet to add the check for if you have room but i want the actual "give a random card" part working first)
I mean when you add it to the usedConsumes
ohh
use the SMODS one
where do i put that in, do i replace the event manager thingamabob?
replace create_card or you can use SMODS.add_card (same parameters) and replace the next 2 lines after create_card too
bump
the whole line, or do i keep "local"?
it's add_card im too sleepy to be answering questions
How can I add/remove an amount from the total payout? (at blinds end)
mine is in misc,dictionary
for add_card, how do i force it to be of a custom rarity? i want it to generate only jokers with stla_Stellated
aghh it still shows up as error
how did you do it?
it should be the same as your group key
huh
okay it works thx!!
no worries
I think writing that key might just work?
SMODS.add_card({rarity = 'stla_Stellated'}) iirc?
what command can i use to create a specific card from its id
still a crash here
"Joker" for set
you forgot a comma before use
anyone know what the weight value of spectral packs is?
0.3 for both normals and the jumbo, 0.07 for mega
okay, game boots, but i uh. i apparently need to actually give a use condition. ;P
if #G.jokers.cards < G.jokers.config.card_limit or self.area == G.jokers then
return true
end
end,```
ae
nvm i give up editing
Okay, what does context.poker_hands actually contain? I was under the impression each hand table inside it contained the respective cards for each hand it represents
works now!
nice
Hey I'm running into this error when clicking on a card in my custom Booster pack. I think it has to do with my method of forcing the button types via this patch, but not exactly sure what needs to change
no worries, it happens lol
ur problem too complex for me 😭 im still struggling with the basics
is there a way i can change the colors of the text on a card based on conditions
Heya, Anyone here who would be willing to look trough my code to see if i'm doing weird/incompatible shit?
x_mult?
So, I have a custom tarot card that successfully creates two base game enhancements, but the moment I change it to my custom enhancement, the game crashes. I assume the issue is somewhere in my SMODS.Enhancement?
SMODS.Enhancement{
key = "m_loaded",
name="Loaded Card",
atlas = "New_Enhance",
pos = {x = 0, y = 5},
loc_txt={
"{C:green}#1# in #3#{} chance",
"to win {C:money}$#2#{}",
"{C:green}#1# in #5#{} chance",
"for {C:mult}+#4# Mult",
},
config = {
extra = {
base_prob = "1",
p_dollars = "6",
money_prob = "5",
mult = "60",
mult_prob = "15"
}
},
loc_vars = function(self, info_queue, center)
return {vars = {
center.ability.extra.base_prob,
center.ability.extra.p_dollars,
center.ability.extra.money_prob,
center.ability.extra.mult,
center.ability.extra.mult_prob
}
}
end
}
This is the line in question that gives the enhancement:
G.hand.highlighted[i]:set_ability("m_loaded");
let me bring the log up real quick
You should include your mod's prefix btw
where should the mod prefix be included?
Does your booster have a kind value?
also smods adds the m_ before your key, so you don't have to do that
Yes
m_prefix_enhancement
I think
^
and that's in every reference to the enhancement, including the key?
Oh you might actually need the func for it, I never gave it to you
context.hand_drawn is the moment when you draw cards or the cards that you draw?
--Code from Betmma's Vouchers
G.FUNCS.can_pick_card = function(e)
if #G.consumeables.cards < G.consumeables.config.card_limit then
e.config.colour = G.C.GREEN
e.config.button = 'pick_card'
else
e.config.colour = G.C.UI.BACKGROUND_INACTIVE
e.config.button = nil
end
end
G.FUNCS.pick_card = function(e)
local c1 = e.config.ref_table
G.E_MANAGER:add_event(Event({
trigger = 'after',
delay = 0.1,
func = function()
c1.area:remove_card(c1)
c1:add_to_deck()
if c1.children.price then c1.children.price:remove() end
c1.children.price = nil
if c1.children.buy_button then c1.children.buy_button:remove() end
c1.children.buy_button = nil
remove_nils(c1.children)
G.consumeables:emplace(c1)
G.GAME.pack_choices = G.GAME.pack_choices - 1
if G.GAME.pack_choices <= 0 then
G.FUNCS.end_consumeable(nil, delay_fac)
end
return true
end
}))
end
Here you go
thank ya kindly
If i understood this correctly, yeah
No worries
cuz its the new version?
ill reformulate, how do i check the cards that ive drawn?
Prob yes
I made the change, still got an error
Which SMODS are you dev'ing on?
calculate = function(self, card, context)
if context.hand_drawn then
for k, v in ipairs(context.hand_drawn) do
if pseudorandom(pseudoseed('braille')) < G.GAME.probabilities.normal / card.ability.extra.odds then
v:flip()
return {
message = ('FLIPPED!'),
colour = G.C.RED,
card = v
}
end
end
end
end
I want 1 in 2 cards to be drawn face down
main branch
Whats the code like rn
Im on mobile so i cant really help tht much
right now is just flipping the last card that you draw, since context.hand_drawn trigger after drawing the hand
The enhancement looks like this now
key = "m_reverse_tarot_loaded",
name="Loaded Card",
atlas = "New_Enhance",
pos = {x = 0, y = 5},
loc_txt={
"{C:green}#1# in #3#{} chance",
"to win {C:money}$#2#{}",
"{C:green}#1# in #5#{} chance",
"for {C:mult}+#4# Mult",
},
config = {
extra = {
base_prob = "1",
p_dollars = "6",
money_prob = "5",
mult = "60",
mult_prob = "15"
}
},
loc_vars = function(self, info_queue, center)
return {vars = {
center.ability.extra.base_prob,
center.ability.extra.p_dollars,
center.ability.extra.money_prob,
center.ability.extra.mult,
center.ability.extra.mult_prob
}
}
end
}
and the line applying the enhancement is
G.hand.highlighted[i]:set_ability("m_reverse_tarot_loaded");
Iterate over G.hand.cards instead of context.hand_drawn
for the loop
What is the prefix for your mod?
The key on line 2 should just be the enhancement name, not the m_ or the prefix
I'm silly
I forgot I made the prefix different from the name of the mod pack in the config
it's just "reverse"
well still, the key for the enhancement should just be loaded on that line
How can I add/remove an amount from the total payout? (at blinds end)
now its just flipping the leftmost card of my hand
something that doesn't seem to be covered in the docs is how to get the joker activation wiggle when you apply the message to another card 😅 is there any way to do that
Thanks! Your suggestions seemed to fix all of the crashes
Oh yea because you're returning something the first iteration
how does the {V:1} color modifier work ?
Replace the return by a card_eval_status_text function
yeah, but it keeps iterating on my whole hand instead of the cards that ive drawn
not fully sure so someone correct me if i'm wrong
at the end of ur vars as your last variable: add "colours =" and then what the color V:1 should be. i haven't diven far into it but this is a surface guess that might get you furhter
thanks
and follow up question
is there a lua command that can let me do an if function when declaring a variable i know other programming languages use ?
the question mark
Try replacing context.hand_drawn by SMODS.drawn_cards ?
oops typo
no clue i've not looked into lua at all i just read code from jokers and interpret everything i need to know from that to write my own 😅
You mean a ternary operator?
and still use G.hand.cards?
No replace G.hand.cards by SMODS.drawn_cards
Should have replied off this one sorry
yea exactly
thanks!
:juice_up()
yeah that was it
thanks :D
one doubt
whats the difference between context.joker_main and joker.individual?
context.hand_drawn and SMODS.drawn_cards are exaxtly the same
could you give a little more info on how this should be implemented. do i just add this in the return statement? and if so, which variable?
card:juice_up(0.5, 0.5)
or self instead of card, it depends on the context
The first didn't work for them apparently
you don't use it in return, since it's actually a function used in balatro
a:juice_up(b,c)
a being the card you want to shake, b and c being stuff to modify the shake
oh so just put it inside the if statement and thats it
mhm
LMAO
I DONT THINK
uhm
that was right
it made a full 360
oh
wait
mbmb
i used a comma instead of a point
:D
dutch troubles
soms heb je dat
klassieke fout
doesn't the base game use 0.3 btw?
(i dont have source available on this device sooo can't double check)
idd idd
why does it say that this is a number value? shouldnt it be a colour
hmm do i sprite up (badly) 2 jokers or work on coding the 8th joker
still it only activates once when you play the hand not per card that activates the effect 😅
lol
i'm making a joker that converts scored cards to enhanced cards, but after running the code in context.before, the seals and enhancements don't get counted in scoring. anyone know how to recalculate enhancements after context.before?
so like, it shakes the entire hand once instead of shaking each individual card?
The color is represented as a number
calculate = function(self, card, context)
if context.hand_drawn then
for i = 1, #SMODS.drawn_cards do
if pseudorandom(pseudoseed('braille')) < G.GAME.probabilities.normal / card.ability.extra.odds then
if SMODS.drawn_cards[i].facing ~= 'back' then
SMODS.drawn_cards[i]:flip()
end
end
end
return {
message = ('FLIPPED!'),
colour = G.C.RED,
card = SMODS.drawn_cards[i]
}
end
if context.individual and context.cardarea == G.play then
for i = 1, #context.scoring_hand do
if context.scoring_hand[i].facing == 'back' then
xmult = card.ability.extra.xmult
end
end
end
end
no so like, the joker applies +4 mult per card i'm just trying to recreate the walkie talkie, but when you play the hand it shakes once and then it just applies the effects without shaking
i see
how are you calling the shake
probably need G.EVENTS and and add event stuff in this case
I know that the context.individual is not working because when you play a card, it turns face up, but how could i do it so previously faced back cards give the xmult?
card:juice_up(0.5,0.5) inside of the same if statement that applies the +4 mult for every card
card in this case is probably your joker...
i had to do this for my Overflow joker so feel free to take a peek at this
https://github.com/BeepSterr/Balataro-BargainBin/blob/master/Items/Jokers/Overflow.lua
yeah u use the G.E_manager to activate it for every card, i think that might be related to it
yeah you gotta call it for context.other_card like context.other_card:juice_up() and put it in a G.E_MANAGER:add_event so they dont all play at the same time
nah i want to shake the joker, not the other card
oh sorry, mb
if u looked at the code that i shared it'll make it look like this #💻・modding-dev message
hmm i see
nice
this shit too advanced for me alr bro i have no clue what i'm doing anymore 💀
nah what am i saying
i got this
dont ever give up just yell at code till code do good
where does the game handle the visual effect where cards appear to move in 3d space, like how if your mouse is on the top of the card the bottom of it will appear closer to you
there has to be an easier way like
every joker has this effect
i thought it was implemented in base game
automatically
i must be missing something
Look trough the source for similar cards
lol i'll see
The events are honestly a pretty easy way to make it all run in order and pretty
the print comes up, but the card doesnt give the xmult
apparently its x_mult
i had the same issue
i read the code, replicated it, and it doesnt work :D
Oof unfortunate, is it crashing?
nope
it just
doesn't do the activation thingy
the little
wobble
it wasnt that, still not working
Do you have… reduced motion on?
nope hahahaha
I don’t see a return statement, could that be it?
i have a lead tho
the baseball card does have it added
maybe
if i look at that code
OMG
IT WORKED
just addd
``
G.E_MANAGER:add_event(Event({
func = function()
card:juice_up(0.5, 0.5)
return true
end
}))
and then it does work
yup
still not working
Ur trying to get x-mult to activate right
strange, since the print does work
yes
If so you need to do x_mult = {whatever var u used} in the return statement
wouldnt the return break the loop?
wait
do i need the loop since im using context.individual?
Oh yeah correct you need to apply it differently
If ur using individual then no you shouldn’t need a loop
I haven’t used individual thought so idk
Look at baron or smth I guess
still it activates when the card is being played instead of when the effect is applied 😭 but its better than nothing i guess
I am an arbiter of fair and balanced gameplay
oh god lol
individual doesn't need a loop
How can I add/remove an amount from the total payout? (at blinds end)
hey guys does context.setting_blind happen before or after the boss blind like the water or the needle activate its effect
I believe it's after
Yup i'm not using it, but the joker is just rotating when the chips of the cards are being scored (so when jt starts scoring the card) instead of when it scores the mult
Which is what is coming from the joker
hmmm?
redid my code after a brainwave to simplify things
doesn't even work 
So instead of it acting like bloodstone (just to paint an example) it does the wiggle when the chips are bing scored instead of when it activates the mult after the chips
NO WAIT
I FORGOT TO USE INSERT >_<
@fluid pecan I don't know anything about your card
i'mma gon work on it myself
is there a keyboard shortcut in the debug mod to reboot the game?
found it, it's hold M
out of curiosity does anyone know how to make a child node of a card have a different size than its parent (for example card.children.front)
I've been trying to make my first custom joker, and ive been looking at tutorials/ reading guides, as well as fiddling with the joker's code, for a couple hours now and im quite stumped.
I have the card art and the modded joker showing up in game just fine, it's just actually giving it an effect/ coding it properly.
Could i ask someone for a bit of help/ guidance? especially because i dont think that the joker im trying to make is that complex. figured i should ask for help instead of struggling alone for 2 more hours lol.
alr screw this
it's calling the insert method a field
If you're able to send your jokers calculate function we can point to the issue
UI Error when attempting to add custom Enhancement description to the info_queue, and when applied to card 🙃
in the SMODS.Consumable before the return:
loc_vars = function(self, info_queue, center)
info_queue[#info_queue+1] = G.P_CENTERS.m_reverse_loaded
And the Enhancement
SMODS.Enhancement{
key = "loaded",
name="Loaded Card",
atlas = "New_Enhance",
pos = {x = 0, y = 5},
loc_txt={
"{C:green}#1# in #3#{} chance",
"to win {C:money}$#2#{}",
"{C:green}#1# in #5#{} chance",
"for {C:mult}+#4#{} Mult",
},
config = {
extra = {
base_prob = "1",
p_dollars = "6",
money_prob = "5",
mult = "60",
mult_prob = "15"
}
},
loc_vars = function(self, info_queue, center)
return {vars = {
center.ability.extra.base_prob,
center.ability.extra.p_dollars,
center.ability.extra.money_prob,
center.ability.extra.mult,
center.ability.extra.mult_prob
}
}
end
}
you know, i deleted it like an hour ago because im pretty sure what i had was completely wrong, so i decided to start over. So i don't have much right now. I wasnt really sure what calculate function to use/ where to put things.
this is what i have currently
which is not much lol
loc_txt is meant to have a name field, which is just a string, and a text field, which is an array of strings
hmmm
only name and text should be inside loc_txt
As for what a calculate function might look like, the simplest one would be
if context.joker_main then
return { mult = 4 }
end
Which would give 4 mult after a hand is scored
ah shoot I put name in the wrong spot thanks
we are so back
alright, and i did find this which would be what im looking for- an end of round effect.
so this would be my if statement, using G.hand and listing all four types of jacks, and then my return would be something like money = 5 ?
I'm trying to make a joker that gives -1 joker slot, which means I need to have functionality that makes it so a joker can't be purchased or duplicated when at situations like 4/5 cards, any mods that have an example of a joker with -joker slot or a function where it can't be purchased in certain scenarios?
you will have to count the amount of jacks in G.hand, and then return { dollars = 5 * amount_of_jacks }
I really don't want to have to compromise for removing it from the pool
gotcha, okay thank you for the guidance
removing it from the pool wouldn't be a solution, since you could be at 3/5, reroll to get it, and buy two jokers
that's true
doesnt removing it from the pool stop it from appearing in the shop at all?
he's saying if there's 3 purchasable jokers from the shop
ah ic
or even 2
is there a function that gets a cards suit, similar to get_id()
card.base.suit
As for how to make it unable to buy, I presume you'll have to find the function responsible for making the use button ui and see if you can make a patch to disable the button
card.base.suit like N said, but make sure to check if not SMODS.has_no_suit(card) beforehand
I've been looking all over the base game code to find where it can't allow purchases at max joker limit, but I haven't thought to disable the button
no wait that doesnt work cuz nil is stupid lol
yea just make sure to check SMODS.has_no_suit(card) (when checking suit) and SMODS.has_no_rank(card) (when checking rank)
hell i oughta go through all the ortalab boss blinds and report like 70% of them for not using the latter my stone card builds keep getting killed by shit that shouldnt affect them
this looks awful but it's probably not the worst looking piece of code I've written for my mod
cool got the mistakes I made fixed
how can i make an effect proc only if a joker is destroyed?
key = 'searc',
loc_txt = {
name = 'Serac',
text = {
"{X:dark_edition,C:white}^#2#{} Mult for every",
"joker destroyed",
"{C:inactive}(Currently {X:dark_edition,C:white}^#1#{C:inactive} Mult)",
"{C:inactive}Art by: rixithechao"
}
},
config = { extra = { Emult = 1 , scaler = 1 } },
loc_vars = function(self, info_queue, center)
return { vars = { center.ability.extra.Emult , center.ability.extra.scaler } }
end,
rarity = "stla_Stellated", --giving it special rarity
atlas = 'StellationJokers',
pos = { x = 8, y = 1 },
soul_pos = { x = 9, y = 1 },
cost = 34,
blueprint_compat = true,
calculate = function(self, card, context)
--actually do the effect thing
if context.joker_main then
return {
Emult_mod = card.ability.extra.Emult,
colour = G.C.DARK_EDITION,
message = "^" .. tostring(card.ability.extra.Echips) .. " Mult"
}
end
end,
}```
me when i forget the and keyword
Implementing a custom probability-based enhancement is harder than I anticipated
it looks like the edition check is failing, if both have an edition, even the same one, it doesn't activate
Like, the easiest thing to do seems like it would just be to edit the vanilla Card:get_chip_mult() and Card:get_p_dollars() functions, because that's how Lucky Cards are handled. But that feels like it would create more issues than it would solve
You can make the joker change the number of joker slots
afaik editions are tables and can't be compared 1 to 1
how would i give multiple enhancements to a card? i'm doing this but it doesn't seem like that context runs at all, despite the documentation?
the code gets worse!!
this doesn't prevent it from being purchased at 4/5 total jokers tho right? if purchased, wouldn't this just make you 5/4? I don't want that to happen
is it possible to check for if a joker has been destroyed, and then run a calculation accordingly? i want to make a joker that scales whenever a joker gets destroyed
sigh
you need to hook G.FUNCS.check_for_buy_space
Oh shoot I didn’t think of that thanks for helping me realize it’s an issue! A weird bandaid would be to add it to hand, and in the add to hand check check if joker slots are 5 and if so, remove/refund?
I just was looking at this, I have not worked with hooking yet so idrk what I'm doing
if a card doesn't have an edition, card.edition is nil, you need to account for that
smth like this
ok so looks like I'm not too far off
already done 🫡
that check should be first
How do I check if a modded joker exists in the jokers? I saw the next(SMODS.find_card("j_stencil")) already mentioned, but it doesn't seem to work for modded jokers
well it works
Friend has a modded joker with key "wah_knife" and it's not picking it up. I saw somewhere that you have to add the prefix as well but we couldn't get that to work exactly either
oh sorry didn't see the edit
it's j_modprefix_jokerkey
does anyone know how to add a linebreak in main_end or main_start?
so this isn't inside the joker stuff info right, this has to be outside of it
yes
ok ok, tysm
oops, i didn't enable the optional feature
Tysm! That works
anybody know what file the pseudorandom() function is stored in off the top of their head?
misc_functions
thanks 🙏
sorry to nag about this, does anyone know if there's a way to check if a joker has been destroyed?
could you clarify what 'outside' the joker means? Do you mean outside the SMODS.Joker {} object?
yes, anywhere in your code outside of that
to my knowledge there isn't a way to check for this in the base game or API
hook into card:remove
thats what i do
I just made a lovely patch for it
i do this for my mod
where would i put this in my code, out of curiosity? for reference, i want to make it so that, when a joker is destroyed, it resolves card.ability.extra.Emult = card.ability.extra.Emult + card.ability.extra.scaler.
key = 'searc',
loc_txt = {
name = 'Serac',
text = {
"{X:dark_edition,C:white}^#2#{} Mult for every",
"joker destroyed",
"{C:inactive}(Currently {X:dark_edition,C:white}^#1#{C:inactive} Mult)",
"{C:inactive}Art by: rixithechao"
}
},
config = { extra = { Emult = 1 , scaler = 1 } },
loc_vars = function(self, info_queue, center)
return { vars = { center.ability.extra.Emult , center.ability.extra.scaler } }
end,
rarity = "stla_Stellated", --giving it special rarity
atlas = 'StellationJokers',
pos = { x = 8, y = 1 },
soul_pos = { x = 9, y = 1 },
cost = 34,
blueprint_compat = true,
calculate = function(self, card, context)
--i think a check for joker deletion goes here???
--the primary joker effect of actually giving Emult
if context.joker_main then
return {
Emult_mod = card.ability.extra.Emult,
colour = G.C.DARK_EDITION,
message = "^" .. tostring(card.ability.extra.Echips) .. " Mult"
}
end
end,
}```
you probably want something like anywhere outside of the SMODS.Joker and then in the calculate function you check for context.joker_destroyed
(i dont know if this covers all the cases)
that will happen when a joker is sold too
oh yeah
that... would be a problem ;P
you can add a and not G.CONTROLLER.locks.selling_card check
that's what I do anyway
so like, wait, i'm confused (if it isn't obvious my lua is rusty to heck), is this creating a new function, and the idea is that i could make it just say smth like
return {
--do stuff
}
end```
in the actual joker's script?
I'm going to have to hook for edge cases with duplication with like invisible joker and stuff like ankh with eternals as well huh. Can someone also like define hook as well I don't really know what it means
is it taking something from the base game and changing it?
hook is simply adding your own code to an existing function
ok
In LUA, it is a very important concept to understand that everything is a variable and all variables may be edited in runtime. This includes functions. With modding other peoples' LUA files, like Klei's basegame code, you may find yourself wanting to run your code before or after the original fun...
the limitations are that you can't add stuff in between lines
ye
ok thank you guys so much
if i wanted to curb selling counting, would i add and not G.CONTROLLER.locks.selling_card like so?
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```
even if the original function doesn't return anything, you should return card_remove_ref(self) just in case
and that looks good
well, that's annoying
move the modifying of Emult before the return
is there any reason why with malverk it isn't retexturing card backs? everything else works from the enhancers texture
silly question, how do i add a custom sound effect?
Is there any way to get the mod folder from within the mod? Like if I make a mod with code to get the mod folder in the folder "foo" I'd get (in my case) C:\Users\USER\AppData\Balatro\Mods\foo
I saw "mod.path" in the smods code but mod isn't a global variable
SMODS.current_mod.path
Or if you're doing it in a function that's not in the loading stage
SMODS.Mods[your mod id].path I think
Tysm!
so, wait, how do i call the sound exactly? do i clarify it and then call its key later? :o
play_sound('prefix_key') would be my assumption
where prefix is your mod prefix and key is the key you have to the sound
Define the key and then you can call the sound by specifying modprefix_keyname as string.
SMODS.Sound({key = "win95start", path = "win95start.ogg"}) + toga (my prefix) = toga_win95start
when i specify the path do i need to include the /assets/sounds/ part or is that automatic?
Automatic.
for whatever reason it's still not playing? no idea why?
& SOUNDS &
&&&&&&&&&&&&]]
SMODS.Sound { --serac buff sound
key = "seracBuff",
path = "serac_buff.ogg"
}
--check if a joker died
if context.joker_destroyed then
card.ability.extra.Emult = card.ability.extra.Emult + card.ability.extra.scaler
play_sound(stla_seracBuff)
return { message = "Demo!" }
end```
play_sound takes a string
You can do this too.
return { message = "Demo!", sound = "seracBuff" }
With the mod prefix, no?
does this answer that question
Discrete math actually coming in handy, using the inclusion/exclusion principle to get the probabilities right (I think)
calculate = function(self,card,context)
if context.main_scoring and context.cardarea == G.play then
loaded_val = pseudorandom('loaded_both')
if loaded_val < G.GAME.probabilities.normal * G.GAME.probabilities.normal/75 then
return{
mult = 60,
dollars = 6
}
elseif loaded_val < G.GAME.probabilities.normal/15 then
return{
mult = 60
}
elseif loaded_val < (G.GAME.probabilities.normal/5 + G.GAME.probabilities.normal/15 - G.GAME.probabilities.normal * G.GAME.probabilities.normal/75) then
return{
dollars = 6
}
end
end
end
I'm trying to make a joker similar to raised fist when the game selects the lowest ranked in your hand and destroys it but for some reason this code either doesn't work or it targets every single card instead of the lowest ranked one
can someone please help me?
Hey guys, my joker doesn't show as compatible with Brainstorm & Blueprint, despite the fact it is, why is that?
set blueprint_compat = true on your joker
Ah, thanks!
oop, yeah. forgetful. 😅
Putting this here for anyone who might be looking for it in the future:
To find/search for files in a directory, Smods uses a filesystem library called "nativefs," (not lfs) so if you want to split your additions into separate files like I do, you can implement it like this:
local nativefs = require("nativefs")
function string:endswith(suffix)
return self:sub(-#suffix) == suffix
end
-- Written by Melody (https://meluhdy.dev)
function load_files_in_dir(relative_path)
local absolute_path = SMODS.current_mod.path..relative_path
local files = nativefs.getDirectoryItemsInfo(absolute_path)
for i = 1, #files do
local file = files[i]
local file_info = nativefs.getInfo(absolute_path.."/"..file.name)
if file_info.type == "directory" then
load_files_in_dir(relative_path.."/"..file.name)
elseif file.name:endswith(".lua") then
assert(SMODS.load_file(relative_path.."/"..file.name))()
end
end
end
Then you just need to call it with the name of the folder you store your stuff in (yes it is recursive). For example:
load_files_in_dir("jokers")
I think I got all the keywords in but just in case:
load, file system, fs, filesystem, lfs, nfs, nativefs, mod path, split files, directories, separate
nativefs is distributed alongside smods :-)
Fixed, thank you
pre-smods filesystem access was a monumental pain in the butt
nativefs is such a useful library
I can imagine, seeing as the only way I could see base lua doing it is through command line and obviously that's not inherently cross-platform compatible
yeah, either that or ffi. I've contemplated implementing something like nativefs entirely in lovely but it's a lot of work haha
If you do try to go ahead and do that, then I wish you the absolute best of luck
it's a really weird design choice by the love2d team. like, I get it, but it makes really common fs operations so much more diifficult and convoluted
I would if nativefs didn't already work great :-)
Also I think this is genuinely the first time in my 12 years of programming so far that I have genuinely used recursion when not specifically asked to by some assignment or question or smth
In the following code, why is the line mult_mod = card.ability.extra.mult necessary? When I take it out, it seems to change the color of the message sent, but I don't understand why and can't seem to find any explanation on the Steamodded wiki documentation.```lua
-- When Joker is triggered and Mult is >0, add Mult
if (context.joker_main) and (card.ability.extra.mult > 0) then
return {
message = localize {
type = "variable",
key = "a_mult",
vars = {
card.ability.extra.mult,
},
},
mult_mod = card.ability.extra.mult,
}
end
mult_mod actually modifies the the mult value in scoring
the message is purely visual
if you're not in an old version of steamodded you can replace your entire return table with { mult = card.ability.extra.mult }
Oh ok, I was wondering why that wasn't working but it was because I downgraded recently. Lemme try that

what'd i bork here?
brackets at the if context.ending_shop methinks
dont trust me tho im tweaking
lua uses then and end, not brackets
lua doesnt use em
youve written it more like js (dw me too)
make sure to end the if
ok i been at this for exactly an hour now and still don't have a single line of working code 😭😭😭 does anyone know how to change the config of a booster pack from the same mod? im tryna make a voucher that gives an extra option and extra select but can't figure out how to change the extra and choose config
I'm attempting to implement a Reverse Fool Tarot, which creates an inverted version of the most recently used tarot, Fool and Reverse Fool excluded. However, I'm running into an issue where if I hover over the normal Fool card after using the Reverse Fool, it creates a stack overflow error. I think it might be because the info_queue is infinitely adding the description for the Reverse Fool, but I'm not entirely sure. This is the loc_vars function where I'm adding the info of the previously used card to the queue
loc_vars = function(self, info_queue, center)
local fool_c = G.GAME.last_tarot_planet and G.P_CENTERS[G.GAME.last_tarot_planet] or nil
local last_tarot_planet = fool_c and localize{type = 'name_text', key = fool_c.key, set = fool_c.set} or localize('k_none')
local colour = (not fool_c or fool_c.name == 'The Fool') and G.C.RED or G.C.GREEN
main_end = {
{n=G.UIT.C, config={align = "bm", padding = 0.02}, nodes={
{n=G.UIT.C, config={align = "m", colour = colour, r = 0.05, padding = 0.05}, nodes={
{n=G.UIT.T, config={text = ' '..last_tarot_planet..' ', colour = G.C.UI.TEXT_LIGHT, scale = 0.3, shadow = true}},
}}
}}
}
if not (not fool_c or fool_c.name == 'The Fool' or fool_c.name == 'The Fool?') then
info_queue[#info_queue+1] = fool_c
end
end
or maybe it's an issue where it just keeps calling this even if nothing is added to the info_queue?
I essentially just copied the function from common_events.lua
a bit late but you didn't define a config field on your joker
you might be interested in looking at cryptid, they do this with a consumable
But I assume it's just a hook/patch on the part of the code that creates booster packs + a global variable in G.GAME
the behavior works correctly for my Reverse Fool Card, it only crashes when I use the Reverse Fool, then hover over the normal Fool
alr tyty
sounds scary for a 5am 5min coding adventure but will have a look later 🙏
good luck soldier
so, commenting out info_queue[#info_queue+1] = fool_c fixes the stack overflow, but then I don't get the description of the previously used card
wouldn't it be fool_c.ability.name?
that just crashes when I hover over the card
oh yeah it's a center, nevermind
the line does correctly give the effect of the previously used card
Can you print what fool_c would be when it crashes
print(inspect(fool_c))
yeah I'll check what that does
I'm not entirely sure where it prints to, but it's late so it might be better if I just sleep on it
so, it only displays this when I hover over the Reverse Fool, not transitively when I hover over the normal Fool and my previously used card is the Reverse Fool (or at least not before it crashes)
do I need to do lovely patches to edit base game cards? I need to cover edge cases with invisible joker and ankh where they duplicate like in the situation with me not being able to purchase
Yes you do
It’s honestly not as intimidating as it looks. It’s basically just telling it “here’s a line of code, this is how I want you to edit it”
please put code in code thnak you lovely
yeah it's just I just learned hooking, and all of this is still very very new to me, it's just a little overwhelming at times
Welcome to learning lol
can you use context.other_card with context.before?
doesn't look like it in the docs but you can always try
then it's not there but you probably want context.scoring_hand or context.full_hand anyways
If you don't specify them not to, do Jokers' calculate effect still trigger when the Joker is debuffed?
i'm trying to make a joker that gives Xmult = 1 + (high cards played this run * scaler variable). uh. how do i grab how many high cards have been played this run a-la supernova?
local high_cards_this_run = G.GAME.hands["High Card"].played
-# (Derived from Supernova source code found in card.lua line 3734)