#💻・modding-dev
1 messages · Page 51 of 1
my option a?
I think I have that in the enhanced deck code
pretty sure random with seeds is possible in apply, see erratic deck
erratic has a special case in Game:start_run lol
oh it does?
it's like
Game:start_run()
...
back:apply()
generate_seed()
if self.GAME.starting_params.erratic_suits_and_ranks then
...
fixed*
all the enhanced decks in Items/Enhanced.lua right? yeah you hooked after Game:start_run
maybe it's best to change that order then
yeah my option b which is why I brought it up
it uses its own seed tho so it’s fine
fellas, i need help. i'm currently adding jokers to a mod i'm making and when i load the jokers, everything works except the soul sprite (i'm only working on legendaries atm). this is the part of the code that loads the jokers (i just took it from the RiskOfJokers mod and modified it a tiny bit lol), it just doesn't recognises the soul_pos part i think
0.9.8 code takes me back lol
it's on steamodded 1.0.0 (i think) don't worry
but you're using 0.9.8 calls/methods
would you be okay with me trying to create a PR for that?
yeah ofc
how would i insert new cards and stuff into the game?
by using steamodded-?
guys do I need to save my global variables in G or in G.GAME?
like, how should i mke it into like stuff steammoded willl recognize
sorry if im asking too many quetion
there's plenty of mods out there you can take as examples
some are also included with steamodded
Also have a look at the docs at https://github.com/Steamopollys/Steamodded/wiki
your global variables can be their own global variable or a field in some global table like G or SMODS
run-specific data belongs in G.GAME
hm
what do you think of making a table GlobalVars = {...} with all my global variables (run-specific) and then modifying back_apply_to_run to include G.GAME.mymodsname = GlobalVars?
sounds like a bad idea because modifying the values in G.GAME will also modify the global table
and the changes will stick when you start another run
so you're better off constructing the table directly in back:apply_to_run
I think it’s a good idea
A few mods do it
But not as you described
Just adding a custom table to G.GAME
Oh wait I’m repeating what Aure said
Because I misread what you said
launched the PR
this should be the end of jank with bigger hands
please do break it
btw, can I only "move" a codeblock by doing it like this?
[patch1]
target = "game.lua"
pattern = '''<codeblock>'''
position = "at"
payload = ''
[patch2]
target = "game.lua"
pattern = '''<move-to>'''
position = "after"
payload = '''<codeblock>'''
i don't think there's another way
yeah alright
😭
i tried removing the card h_popup when you hover over it
and now they dont get removed
they just stay
its like the opposite of what i was trying to do
https://github.com/Steamopollys/Steamodded/pull/215 Something else people may want to play around with.
loc_txt should be closed a whole lot earlier
oh i see it now
Quick one, are mod changes hot reloaded/reloadable or do we just nuke and restart?
Lua reloading was thanos snapped
the reloading wasn't helpful for almost all the mods so it was removed yeah
ah that makes sense then welp gotta get my alt f4 game up xD
I think wilson was working on the exe restarting?
disabling or enabling some mod should restart the game for you as of now
Mods do a lot of overriding so soft-restart is basically impossible
it uh... just exits for me
can you change the name of a joker mid-run?
Which platofrn are you on?
windows 10
like this?
exactly
what does you loc_vars look like?
Hmm it should work. I'm at work now but in about an hour or two, I should be available to help debug?
you can return a key = 'key' as well and change it accordingly
whhere the key is the reference to the localization table
ohhh okok
Also as a sanity check, does the restart work for anyone else on Windows? It worked for me but I was testing using Linux and wine
didn't know that was possible
I akos have a fairly unusual setup
return {key = (card.ability.extra.uses_left > 1 and self.key or self.key..'_single')} this is the balloon example
Does It also works for blinds?
For 1.0 SteamModded will this work the way I think it will work:
if context.cardarea == G.jokers then
local chips, mult = GREED.evaluate_greed(G.GAME.dollars, card.config.greed)
return {
message = localize{type='variable',key='a_mult',vars={self.ability.mult}},
mult = mult,
chips = chips,
}
end
i.e when playing it will calculate and assign the mult etc
Okay so I'm running Balatro entirely through a native-Linux setup (the exe it came in is intact)
Holy cow that's some speed
It's telling me that it's getting 500+ fps if I don't framelimit it
last I checked you can't pass a set through loc_vars, it needs to be in the set of the original card
maybe it makes sense to add that
do you ever sleep 😛
should work if you move it to Joker though
what's the set in this case?
come on it's only 12:20am
Joker
kk
fax fax still early too early to quit
fr fr
are there any API docs on 1.x atm, or perhaps a point to the source so I can read, mainly joker/deck related
there's https://github.com/Steamopollys/Steamodded/wiki but it's definitely not a complete documentation
oh the return { key = 'key' } is even mentioned in there, neat
yeah been using this as the bible for modding Balatro, but would like to know if there are specific funcs like end_calculate_context which are used in the joker example
and what they do etc
and why for the joker calculate it returns mult_mod instead of just mult as per the source code in card.lua e.g 🙂
end_calculate_context specifically is deprecated, we just didn't really noticed that context.joker_main exists
the calculation stack in general is spaghetti code
so is which syntax is the best to use right now
SMOD.Joker:new()
SMOD.Joker{}
first of all, SMODS, not SMOD
second, SMODS.Joker:new() is 0.9.8 syntax, don't bother with it
yeah i sped typed and copied pasta'd ... dont trust macros xD
disregard all of the pages on the wiki that are not numbered
there's efforts to fix it, but it's been kind of stale
card.config.center.pos = {x = card.ability.extra.Rank - 1, y = card.config.center.pos.y}
card:set_sprites(card.config.center)
is this not a reliable way to change sprite?
Right , so i can use that! And one more question, dor deck calculations what can I use ?
And if you guys need help updating docs, be happy to help contribute once I get some more understanding 😉
you want to add a deck? check SMODS.Center
inside the wiki
I mostly just need to get myself to do it, been ironing out bugs and adding features more than anything lately
nah I have added the deck, more just calculations are wahat im looking for where it uses it etc
that is too explained in that part
LEGEN- Wait for it - DAIRY
I'd consider something like card.children.center:set_sprite_pos({ ... })
it was so easy with soul_pos
I think you want Back:apply and Back:trigger_effect?
Like Cerlo said, they're at the bottom of one of the pages
HOREE SHEET, Centers has the API expl I was looking for.. i was ignoring it..ree
xD
never understood the difference between card and card.children etc
card.children has the Sprite objects
Thank you ❤️ What did we learn today... read.. use the eyes, even tho i have glasses
UPDATE: doesn't work
me getting tired of explaining "doesn't work" never helps in resolving the issue
you are right
the sprite remains the same
even when I call spriteCalc inside the calculate function
So does that mean for using joker_main it would be
if SMODS.joker_main_context(ctx) then
-- some main code here
end
-- or
ctx.joker_main = function (ctx)
-- code here
end
calculate = function(self,card,context)
if context.discard then
card.ability.extra.Rank = card.ability.extra.Rank + 1
end
UTIL.spriteCalc(card)
end,```
you do
calculate = function(self,card,context)
end
and between these you write HOW the joker behaves
you should probably also go have a look at https://github.com/Steamopollys/Steamodded/blob/main/example_mods/Mods/JokerAPI_Example/JokerAPI.lua#L51
got it open xD
in this case you do
calculate = function(self,card,context)
if context.joker_main then
-- code here
end
end,
yeah within the inner calc..
no inner calc is needed
ah yeah like that, figured after I saw the discard example 🙂
i mean the calculate on the joker constructor sorry, long day english is hard now
@brisk pond what does your atlas look like?
does it work if you put the set position line directly in calculate?
useful
thanks
how are you testing cerlo?
i add the joker and discard cards
the rank changes because loc_txt changes
so Rank is not the problem
does card.config.center.pos.y exist?
is there a rough expl on how the events work in game
LEGEND and is there an expl on how G.E_MANAGER:adde_event works
how much does blueprint cost?
ah i didnt read it just yet, just did a quick skim 🙂 Cheers for that
10$
$10? why you asking this in modding chat btw lol
its for modding purposes
feedback more than welcome
This is cooking.
neat :)
awesome
guys what you think?
it's gorgeous
yoo that looks awesome
okay slight design problem: if I have a consumable type that creates a new card type (biome), what should the consumable type be called? I've been using CBiome in development but that doesn't feel like it would work with an actual release
maybe just a Tarot?
it feels like a waste to create a new consumable type for just a card
the consumables will also level up the biome if the biome they would create is already active, which is why I didn't want to just put the biomes themselves in the packs, but thats a good point
i cant seem to emplace multiple cards to these card areas
it shows the tilt as if it was the last card in the area
which makes it seem like there should be more
what does your area definition look like?
args = args or {}
args._type = args._type or 'Back'
args.col = args.col or 5
args.row = args.row or 2
args.specific_center = args.specific_center or nil
local deck_tables = {}
local cards_per_page = args.col*args.row
local current_center = 0
local deck_limit = 10
S.card_display = {}
for i = 1, args.row do
local row = {n=G.UIT.R, config={align = "cm", padding = 0.07, no_fill = true}, nodes={}}
for j = 1, args.col do
S.card_display[#S.card_display+1] = CardArea(
G.ROOM.T.x + 0.2*G.ROOM.T.w/args.row,G.ROOM.T.h,
G.CARD_W,
0.95*G.CARD_H,
{card_limit = deck_limit, type = 'title', highlight_limit = 0, collection = false})
table.insert(row.nodes,
{n=G.UIT.C, config={align = "cm", padding = 0, no_fill = true}, nodes={
{n=G.UIT.O, config={colour = G.C.CLEAR, object = S.card_display[#S.card_display]}}
}}
)
end
table.insert(deck_tables, row)
end
if args.specific_center then
local center = args.specific_center
local card = Card(S.card_display[1].T.x + S.card_display[1].T.w/args.row, S.card_display[1].T.y, G.CARD_W, G.CARD_H, nil, center)
S.card_display[1]:emplace(card)
return {
{n=G.UIT.R, config={align = "cm", r = 0.1, colour = G.C.CLEAR, emboss = 0.05}, nodes=deck_tables},
}
end
for i = 1, #S.card_display do
current_center = current_center + 1
local center = G.P_CENTER_POOLS[args._type][current_center + (S.current_page*(args.row*args.col))]
if not center then break end
local card = Card(S.card_display[i].T.x + S.card_display[i].T.w/args.row, S.card_display[i].T.y, G.CARD_W, G.CARD_H, G.P_CARDS.empty, center)
card.tilt_var = {mx = 0, my = 0, dx = 0, dy = 0, amt = 0}
card.ambient_tilt = 0
card.s_stats = true
card.s_deck = true
for j = 1, deck_limit do
S.card_display[i]:emplace(card, 'front')
end
end
local t = {
{n=G.UIT.R, config={align = "cm", r = 0.1, colour = G.C.CLEAR, emboss = 0.05}, nodes=deck_tables},
(cards_per_page < #G.P_CENTER_POOLS[args._type]) and {n=G.UIT.R, config={align = "cm",}, nodes = s_create_page_cycle_options({_type = args._type, colour = lighten(G.C.GREEN, 0.03), _type = args._type, row = args.row, col = args.col, button_func = 'deckview_page_cycle'})}
or nil
}
return t
end```
you should really use
```lua
btw
could be to do with them being title type perhaps?
ive tried all the different types
you can use type = deck
"right"?
i cant seem to change it either
card.children.back:set_role({major = card, role_type = 'Glued', draw_major = card})
and
ill try that
card.children.back = Sprite(card.T.x, card.T.y, card.T.w, card.T.h, G.ASSET_ATLAS[G.P_CENTER_POOLS.Back[count].atlas], G.P_CENTER_POOLS.Back[count].pos)
this is what I use
thanks
i had the second bit in my code at some point cos you sent that
but not the first bit lol
interesting, that's normally what happens to all the "non-top" cards
never figured out where/why/how it applies the darkened effect though
is there a way i can emplace a card at the top
in the game code it says if its deck it always goes to the front
but surely i dont want that if its greying them out
do you just want one card?
any :emplaced card should already be at the top (?) btw, not sure what happened to your decks
oh
it might have something to do with the decksize but I'm kinda just taking a shot in the dark here
wait what? what'd you change?
yeah okay so it is doing something with the card limit
what's the problem now then?
well if i have it show multiple cards it doesnt work
it only shows 1
just like before it would only show 1
even if i emplaced like 10
ah
are you creating 10 cards, or just emplacing the same one 10 times
... yes
let me rephrase - I'd be surprised if it didn't matter
I'm just imagining someone in real life slamming the same card repeatedly into the table and wondering why the pile doesn't get bigger 🤣
also is it just me or does the plasma deck look like it has only 1 card
its because i was hovering over that one i think
ah yeah, it does appear to be a bit bigger so that would make sense
guys. i just updated my cryptid mod and there's a problem: there is a syntax error, in the lovely. in function/common_events.lua
line 1144 is saying it needs a } to close out line 1126
i put it there and it still says line 1144 needs a } to close out line 1126. there's only one { in line 1126. what is going on
can you send the error screen?
looks like this
transcription because the image was too blurry:
Error
Syntax error: functions/common_events.lua:1144: '}' expected (to close '{' at line 1126) near 'play_sound'
Traceback
[love "callbacks.lua"]:228: in function 'handler'
[C]: at 0x7ffbef9a2fa0
[C]: in function 'require'
main.lua:23: in main chunk
[C]: in function 'require'
[C]: in function 'xpcall'
[C]: in function 'xpcall'
did you try "upgrading" an older version of cryptid?
(aka copied and replaced over old cryptid files)
...yeah
yeah apparently you shouldn't do that haha
just nuke the whole mods folder and redownload it I guess
I am trying to make a joker like blueprint, I feel like this code should be working but other_joker keeps returning nil.
Would anyone know where to change the number of booster packs in the shop
If that's even possible
Is there a way to make an enhancement not count as suit or rank like a stone card?
Look at how stone does it in card.lua
function card:is_suit
You'd have to shove whatever enhancement you don't want counting as a suit there and doing the same thing to it as stone cards
Also if that doesn't work for ranks idk what to tell you I only know about suits because I tried fucking with smeared joker once upon a time
you have jokers[i] == self, but self isn't a card
you need card there
had to remove an extra bit of code in the if statement and replace self with card, now it works Thanks👍
hey. i downloaded Lovley and Steammodded, on the github guide it says to but the Steammidded folder in the Mods directory but not what to do with Lovley (version.dll) file. where do i put it or is it a later step?
Put it where balatro.exe exists
Assuming steamodded, as there’s no enhancement docs yet it’s a bit tricky, but if you read around the code in core/game objects.lua there are some comments that are quite helpful for doing this. I can share with you how I’ve done it when I’m on my pc later today if you’re still struggling
oh definitely, i know very very little about lua so some help would be greatly appreciated!
at the core im trying to make a card that gives +4 mult when held in hand and x1.25 mult when scored
That’s definitely possible! Have you got anything set up so far?
as in somewhere in the modding discussion? no not yet, though I guess I could make one
No in your code 😂
oh! i have it at the point where the card shows up in the enhancement card section
also the description seems to be broken
Oh cool that’s good, can you send a screenshot of the code?
SMODS.Enhancement { -- Ofuda Card
key = 'ofuda',
atlas = 'snyo_enhancementatlas',
pos = {x=0, y=0},
config = { x_mult = 1.25, mult = 4},
loc_text = {
name = 'Ofuda Card',
text = {'{X:mult,C:white} X#1# {} Mult when scored',
'+#1# Mult when held in hand.',
'Does not count for rank or suit,'}
},
loc_vars = function(self, info_queue)
return { vars = {self.config.x_mult, self.config.mult,}}
end
}
and what's wrong with the description?
oh your loc_vars needs to be outside the loc_txt
it's also loc_txt not loc_text
in terms of making it actually score, here are the two enhancements I have made right now. You might need to set the mult to be inside extra in your config, I don't remember off the top of my head how the no rank/suit cards re handled internally when scored.
SMODS.Enhancement({
key = "postcard",
loc_txt = {
name = "Post Card",
text = {
"{C:chips}+#1#{} chips for every",
"{C:attention}card{} held in hand"
}
},
atlas = "new_enhance",
pos = {x = 0, y = 0},
config = {hand_chips = 10},
loc_vars = function(self)
return {
vars = { self.config.hand_chips }
}
end,
calculate = function(self, center, context, effect)
if context.cardarea == G.play then
local chips_return = 0
for _, card in pairs(G.hand.cards) do
chips_return = chips_return + center.ability.hand_chips
end
sendDebugMessage("Hand Chips: "..center.ability.hand_chips.." Cards in hand: "..chips_return/center.ability.hand_chips, "Enhancement Effect")
SMODS.eval_this(center, {
chip_mod = chips_return,
message = localize({type = 'variable', key = 'a_chips', vars = {chips_return}})
})
end
end
})
SMODS.Enhancement({
key = "ore",
loc_txt = {
name = "Ore Card",
text = {
"{C:mult}+#1#{} Mult",
"no rank or suit"
}
},
atlas = "new_enhance",
pos = {x = 1, y = 1},
no_rank = true,
no_suit = true,
replace_base_card = true,
always_scores = true,
config = {mult = 10},
loc_vars = function(self)
return {
vars = { self.config.mult }
}
end
})```
you'll need a calculate function to handle the scoring whilst held in hand, but the no-rank to always_scores lines should handle the played condition for you
Are all contexts already supported?
already looking much better! need to tweak it though, of course
Is there any way to summon this card in game using the debug menu?
hold tab, press 3
dont need to hold tab for that
tab only shows keybinds
just pressing 3 does the job
But I need to hold it for some reason
¯_(ツ)_/¯
Anyway
i dont think that works for enhancements, just jokers
and spectrals or tarots
if you have #1228149931257237664 you can apply any enhancement
Press W on a card to cycle through
I think they are, yes
none of the controls work???
@zealous glen did you see this?
Do you have debug plus?
yeah, just installed it
log says to use backslash to open the console but nothing opens
not even the base game debug options work
How did you get debug to work before?
something about enabling a setting in the controller section of the game
Try verifying your game files
ah, apparently it was uh. unverified i guess?
there we go, much better!
yeah the cards are a little finnicky right now, but that'll get ironed out
thank you so much!
verifying game files makes sure that changes you did to the source code in balatro.exe reset to normal
because lovely patches rely on the source code being same for everyone
How can I make transition for UIBox offset change?
:0
No, I hadn’t. Looks cool but the custom decks threw me off XD
Oh yeah I forgot I’d turned them on 😂
I started work on the chip tower idea, just need to adjust how it aligns and it should look okay
what does quick start do
and does it need to stay when you go to select stake menu
Probably not
but that's pretty cool
Love how the decks lay in a grid like we are actually picking from a vender
Well this is extendable so things like card sleeve can add their own page, so I’ll leave quick start on them all
Maybe it can just say play instead
is info_queue in playing cards broken?
I think it needs to indicate it’s using your latest settings and what they are
If all the information was already there to start with, it could be just “play”
nvm something else broke and idk what
oh
fuck you whoever added the regex breaking
I was planning on adding them as a tooltip when hovered, maybe quick start is fine as a label
I think it needs to be visible without hovering
true
love the font lol what is it
04b03
sweet
the updated sound api makes the base game songs loop incorrectly
unless i'm late by a few commits uhh
no i'm not
I- what?
why the frick would it do that
@frosty dock i recoded get_straight() and modified get_X_same() to check the special playing card for functions instead of only checking it's rank
we were discussing how to make get_straight work with multi-rank cards some time back in #1209564621644505158 and didn't really come up with anything of use, so I'm curious
do boosters and seals have in_pool functionality in smods? i'm seeing a booster pack appear in the shop when i know it should be disabled
wellthe actual mod this is for isn't ready yet, but i can send you the function for get_straight i used in dms.
sounds good, ty
They currently don't. There's a PR for seals, and I think packs are just an oversight
would you use SMODS.Center if you wanted to create a new type of card?
Yes
cool, ty
You could reference the Contracts mod if you need extra help
oooh, interesting - i'll take a look at it
@frosty dock steamodded update idea:
when you check for repetitions in seals, instead of getting true o false, you get an integer. This way you can easily mod a seal that does 2,3,4.. retriggers without changing the code for the basic red seal
what you think?
they already work like that?
yeah that's already how it works
hm the specialist music broke
the base game music does stop but nothing plays
local sound = SMODS.Sound({
key = k,
path = v..".ogg",
pitch = 1,
volume = 0.9,
no_sync = true,
})
if k == "arcadespecialist" then
sound.select_music_track = function()
if config.no_music then return false end
return (G.GAME and G.GAME.blind and G.GAME.blind.boss)
--[[if lobc_isaac and lobc_isaac.states.drag.is then
return true
end
return false]]
end
end
the code in question
oh wait. god fucking damnit it has to have music doesn't it
oh sry
I did notice that
oh I think I get it better now
for me it looped like 20-30 seconds before it should
it loops through all music sources and restarts as soon as any one is not playing, which causes issues with different lengths
it's also inefficient, it should just check if the current track is still playing
I hate that, that's such a nasty bug
Hey, I'm new to modding and I'm looking to learn how I would replace vanilla audio files. I am currently using SteamModded 0.9.8 to run mods.
Currently I have my files organized as:
Mod Folder
mod.lua
assets
audio
audioFile.ogg
In this case, I'm trying to replace generic1.ogg with the new sound. Any help would be greatly appreciated!
If you update to 1.0 they have added a Sound API
Although as just seen it seems to still be haunted
non music shouldn't be affected
So it should be possible to change the non-music audio for custom files in SteamModded 0.9.8?
Just installed 1.0.0

Yeah I guess the ghosts of Balatro modding past are going to haunt me now 💀
Currently looking through the docs for any info on the Sound API
I've been holding off on them because there were some major changes left to do
they're done now so I should be getting the docs up soon
:D
The Sound API downloads a small Jimbo to your computer who must learn to parrot new sounds to its Steamodded ringmasters
Most of the stuff that accumulates after a game in Balatro seems to be tables without metatables, time to keep digging
Is there a convenient lua library to display hierarchical data?
Was this always a thing? Not sure why it adds a 0.
ppl also noticed this happening in Cryptid, maybe caused by Jimball music even with no_sync?
Yes, it does auto round up to 2 digits even in vanilla, e.g. Constellation Joker
I think it auto shortens 1.5 though
it uses the shortest music
Steamodded number formatting
I would expect no_sync to not be included in this… 🪲
Doesn't seem like it
I have never noticed this, huh
If you check Roffle's video from 2 days ago it doesn't have the 0
I blame the gnomes from the zeroth dimension
all no_sync actually does is reset the music to play from the start when switched to
what the code was doing is checking all possible music sources for if they've stopped, even if they're not the currently played music
Might not be on the same patch
Yeah that makes sense, would be cool if no_sync let other music sources go into their own pool
Or is there a way to accomplish something similar?
the approach there is completely unnecessary actually, checking the current track for if it's still running is good enough
It matters though if music tracks lengths are a few frames apart but you want sync
Which I imagine is the case with Cryptid
you mean like having it sync to some tracks but not to others?
that shouldn't be too hard to add
The way I’d want it for Cryptid is for the syncing to include all the custom music tracks but not Jimball’s music
Or the ULTRAKILL loops when/if they get added
what the syncing actually does it it's playing all music tracks in parallel on volume 0
the exact length doesn't end up mattering that much with how I've changed it, it'll just restart whenever the current track is finished
Ive tested multiple Steamodded version and the 0 starts appearing since this one commit https://github.com/Steamopollys/Steamodded/commit/ddd877f77c92abd18b3b525a2cde999d5ff273c7
I think then it might be reasonable to replace no_sync with a broader sync field
default is always sync, a value of false means never sync, a table means sync the keys that are present in it
that would also happen to support using a function, like setmetatable({}, { __index = function() ... end })
@languid mirage Your last merge branch causes a "0" to be added onto config values. From the first glance this merge was supposed to be only money related no?
nope, not just money related
oops i committed that to the poker hand branch 💀
I added a patch that (hopefully) all UI numbers get formatted
https://cdn.discordapp.com/attachments/1241172556849876993/1264189962505683006/image.png?ex=66aa2726&is=66a8d5a6&hm=1e4992fc558eaf57ce085cec1d0443ac0331446f8bac8433906cee24abc314fe&
https://cdn.discordapp.com/attachments/1241172556849876993/1264189962862067722/image.png?ex=66aa2726&is=66a8d5a6&hm=745b5bcd64386cceeb2ceb8340a675b61c2388485a6de712589bf0f09c4badd3&
like that
which might be somehow causing it? I dunno
I see, however why does it add a 0 to say 1.5? To me it doesn't seem like it needs formatting
Sadly I can't properly check your commit rn
@frosty dock Found an issue with mod disabling system, don't see this issue opened on Git for it but will ask here first
After disabling the mod in the game and restarting, it still reads the localisation files while ignoring everything else. Removing the mod from the folder fixes the issue. Here's demo feat. ma boy Vagabond
Pic. 1 - mod is removed from the folder, vanilla texture and loc
Pic. 2 - mod is in the folder and enabled, modded texture and loc
Pic. 3 - mod is in the folder and disabled, vanilla texture but modded loc
Localisation is called out by these functions in SMODS.INIT (I don't remember from where I snatched them)
local lang_path = balatrea.path..'localization/en-us.lua'
local function patch_lang()
if G.LANG.key == "en-us" and love.filesystem.exists(lang_path) then
G.localization = assert(loadstring(love.filesystem.read(lang_path)))()
init_localization()
end
end
patch_lang()
local set_language_ref = Game.set_language
function Game:set_language()
set_language_ref(self)
patch_lang()
end
```
it's in a patch to number_format or format_number whatever
Was it a mistake or intentioned?
it's an unintended consequence
Oh
not a mistake
well
it should be easy to replace trailing zeroes? tho probably want to make sure that trailing dot gets removed too
Would be nice
i want to make a mod that changes the colour of the "clubs" text on cards, jokers, and consumables, like what bunco does (but without all of buncos actual additions)
how complex would this be?
Why exactly would you have SMODS.INIT? 0.9.8 doesn't have mod.disabling
1.0.0 loads localization automatically, it may be possible I forgot to exclude disabled mods from that
anyways thanks for the report
My code is a chimera of multiple mods' code, I just discovered proper documentation
Gotta remake it for the latest when I find out how
I've never modified invisible joker but it's crashing now
Is this just a rare vanilla crash or
Wasn't on the main menu
It crashed when I sold it
Wonderful
That feeling when no Jokers
Where the fuck did that go wrong
If only there was a crash log to tell us how
are you not using steamodded?
No
This is unga bunga cave man mod
Open balatro.exe and change file
I'm currently downloading older versions and seeing what happens
Although I guess I should've started with vanilla eh
Works in vanilla
Amazing.
I can’t believe people would choose to try and modify thunks code when the apis have been created 🤷♂️
I didn't know about the existence of the modloaders when I started
I kinda just opened the file and saw that it was all lua laying there and said fuck it good enough
How long would it take to remake the mod in an actual api
Also all I want to do is change stuff in vanilla so I'm not sure a modloader would even be super necessary in the first place
Probably faster than rewriting it when an update overwrites the edits 🤔
Anyone know how to use DynaTexf in Steamodded text?
Trying to get this
How the two pair is there
How do I make a Joker display custom text when triggered?
From experimenting, I’m pretty sure dynatext is for text that either changes periodically or for text that does the jiggle
@wooden nexus
Got it.
You got a link on how to use it?
(And maybe also how to delay a joker's effects until AFTER scoring?)
That reply wasn’t for you 😂 you want to look at card_eval_status_text if I remember the name of it correctly
Yeah, I copy-pasted that in my code and crash.mp3
No duh. I know that
I'm trying to implement it
Oh I thought you’d mocked that image up, is that what you have in game?
No
That's the image from old Balatro
I want to implement it into current which acts different
With Smods
And you want the hand name to do the jiggley wave?
The other part
The change periodically
I want it to look and see what most played hand is and load it there where "Two Pair" is and then in () have how many times one has played that hand
That’s not what dynatext is for
You can do that with just a regular text node using a reference table
Oh you can do ref tables in dyna text, hold on
DynaText({string = {{ref_table = stats_table, ref_value = most_played}}) should do the job I think
Assuming you have tables set up that store the information
I’m not entirely sure if you can also have the same object draw the counter
I imagine the answer is yes, but I haven’t really tried to do a lot with DunaText yet
Where do i put that in? the Loc_text?
Also idk anything about table stuff and all that
I'm just trying to replicate the deck is all
Uhhh
I’m pretty sure it needs to be used in actual ui creation
I don’t think it works in loc_txt
That's fine I can ignore the dynatext if needed
is it possible for me to load the table info in normal text?
or should I just keep that line out?
I got the code working at least
I would assume back definitions work like everything else, so it’s possible
I’ll figure it out later then
Oh in fact you can do the jiggle in loc_txt too
I am dumb
I use it in my own mod 😂
Can you split out the mod into different files? I keep getting a file could not be found error as it seems to be looking in the install dir rather than the relative dir
how do i mod
steamodded is the most used Balatro modloader
download it and read the wiki
how do you apply a shader on a modded consumable type, like spectrals?
If you inject into Card:draw or hook it + provide correct args you can.
Speaking of which, why isn't there a SMODS.Center function for draw?
I tried this to create the effect, but self does not exist in the context I also tried with card
do card_drawref(self, layer)
that worked
Also, does anyone know why stuff is getting printed/drawn to the top-left corner?
I've seen messages about it before but I can't find them anymore
It's from UI that gets created but has nowhere to go iirc.
it did start appearing once I started using generate_ui yes...
oh it's because I need to pass something better than {} for full_UI_table
okay fun
thanks flowwey
got a problem and was wondering if anyone can help
self.FILE_MANAGER = {
THREAD = love.thread.newThread('file_manager.lua'),
CHANNEL = love.thread.getChannel('file_request')
}```
the code is in a file called main
in the same place as the file_manager.lua
ive tried moving it about, and also being more specific with the path
but even C:/blah blah didnt work
wait
no way i misspelt manager
that cant be it
nope still doesnt work
for anyone with a problem like this in the future
local file_manager = nativefs.read(self.MOD_PATH .. 'main/file_manager.lua')
self.FILE_MANAGER = {
THREAD = love.thread.newThread(file_manager),
CHANNEL = love.thread.getChannel('file_request'),
}```
that works
but because balatro doesnt use nativefs itself
the file reading/writing is really restricted
shame cos threads seem cool
on the tutorial it says --- BADGE_COLOR: 123456 or --- BADGE_COLOUR: ABCDEF what does badge color mean?
There's probably some way to lovely patch Balatro so it doesn't pull the original file for the sound thread, but a patched file
If it's loaded via loadbuffer then yes, but there are some weird instances where files aren't loaded via that method.
cough cough localization
sound_manager.lua is perfectly lovely patchable itself
i heard its coz the game treats the negative sign as a digit so its trying to put commas in like it would a thousand but its not a thousand
i dont remember where i heard this i think its a lovely quirk
i might just be wrong
So you can use this to load extra lua files in?
Stupid has pushed a format change thats been giving lots of light issues thats why
How exactly do I need to implement this into my custom Blind?
boss: Marks this Blind as a Boss Blind and specifies on which Antes it can appear ({ min = 1, max = 10 }).
boss = {min = 1, max = 10}
change min to the minimum ante required, max does nothing
It was literally word for word. Thanks, overthinking brain.
Do I need to pass the SMODS.Atlas with a custom Blind or will the atlas parameter in SMODS.Blind take care of that?
the atlas param takes care of it, you just put the same string you put as the key of your Atlas
Aaaand we're back to crashing.
Looks like the atlas param isn't carrying its own weight.
Wait a sec, does the atlas need the .png extension?
that means the atlas key you put into the blind doesn't have a valid atlas object associated with it
can you show me the definitions for both the atlas and the blind?
name = 'The Soul Train', -- If you don't know what a "Soul Train" is, look it up on UrbanDictionary.
text = {'Your run ends here. (10x Base Mult)'},
dollars = 7,
mult = 10,
boss = {min = 1, max = 777},
pos = {x = 0, y = 0},
atlas = "SoulTrainBlindChip.png",
discovered = true```
Here's what it is now.
that's the blind
what about the atlas?
oh i see I misunderstood your question 💀
you do need an SMODS.Atlas object
but you don't pass that object through to the blind, you just take its key
So this would work?
Nope, still crashes when attempting to render.
What am I doing wrong? Still getting the "attempt to index field 'atlas' [a nil value]" error
blind atlas is slightly different
SMODS.Atlas({
key = "LobotomyCorp_Blind",
atlas_table = "ANIMATION_ATLAS",
path = "LobotomyCorp_blind.png",
px = 34,
py = 34,
frames = 21,
})
I don't need all of those params, do I? My custom blind is not an animated one
Thanks

as for the description and stuff, your name and text need to go in a loc_txt table
eh
static blinds chips would just be too plain
Yep, still crashing.
key = "SoulTrainBlindChip",
atlas_table = "ANIMATION_ATLAS",
path = "SoulTrainBlindChip.png",
px = 34,
py = 34,
frames = 1
})
SMODS.Blind{
key = "SoulTrain",
loc_txt = {
name = 'The Soul Train', -- If you don't know what a "Soul Train" is, look it up on UrbanDictionary.
text = {'Your run ends here. (10x Base Mult)'}
},
dollars = 7,
mult = 10,
boss = {min = 1, max = 777},
pos = {x = 0, y = 0},
discovered = true
}```
w h a t
you don't have atlas in your blind with this one
Well, it rendered for a split second, but immediately crashed with this error.
What is a "boss color"?
NVM IT'S ALL WORKING NOW
btw Boss Color code:
boss_colour = HEX('FF0000'),
what does it do?
It's a flat 10x Base blind.
so harder violet vessel?
It plays off the definition of "Soul Train" (Referencing an ambulance that is blocked by a train, leading to be considered a sign by a higher power that their time was up.)
Way harder Violet Vessel.
Ante 1 ALONE is 3,000 Chips
i know
Well, almost there.
Shouldn't align = "cm" center this node? c and m stand for "center" and "middle" no?
they do indeed stand for center and middle
Is it because I don't specify an align for the parent node that it doesn't center or I missed something?
yeah try centering the parent node
Anyone got a fix for this?
Yea that fixed it, ty
I'll take that into consideration, but... y'know, kinda by design.
Yeah, still having the issue. Anything else? (Both 1x and 2x are 34x34)\
2x should be 68x68
And that'll fix this?
yes
Somehow, my Jokers & hands have won that 10x blind 100% of the time.
That's actual luck.
This context of destroying a card is not being called when a card is destroyed with this seal, is there a reason why this isn't working?
the create consumable function works, I tested it and just context.destoying_card doesn't
Set the 1x to 34x34 and 2x to 68x68.
Still getting this...
weird
nvm, Krita didn't save the dimensions properly.
the calculate of a seal isn't the same of a joker
calculate_seal only consider discards for purple seal, you'll have to lovely inject your own method or override the function.
Wait Im saying shit
Is the context used for calculate_seal the same as for joker?
it works for my other seal in a different context, and for cryptid
Ah there we go, calculate_seal is called on eval play and discards
Sorry deleting the pics, I think i'm breaking a modding rule
why does the same destroying card context work for cryptid?
Do they have a seal that triggers on destroyed card?
yes
Maybe they injected calculate_seal somewhere
heh what rule
If you show source code and explain how it works, does it relate to rule 2 or 3?
how do u break rule 2 if youre in modding channel
Yea i'm just being you rn
stupid
Hope they still understood what was wrong without the pics
cryptid seal destroys on play, so it makes sense why it won't work just destroying it
You're right, so I kicked it down to 8x Base.
r u just making blinds worse than vanilla ones?
Nah, when I eventually get enough experience with SMODS and Lua, I'll whip up something better.
But thank you all for helping make this blind.
(even if it sucks)
the X66.6 blind in question:
Lavender Loop:
Yeah, but 10x off the bat is a nightmare, so 8x is better.
And to explain the design choice:
"The soul train stops emergency vehicles from reaching someone who a higher power decided shouldn't make it"
Soul Train blind: 8x Base
I'll make it a Showdown Boss Blind, that way it'll only pop up on Ante 8
violet vessel:
me when my ||12 apostles show up||
i hope that wasn't a ping
wasn't
ok good
I made Bell into a Boss Blind
I tweaked it to only force select Face cards but I think it would be fine as the original Cerulean Bell
Blind name: "The Final Nail"
Description: "RIP to your run bozo"
the luchador in question
"skill issue"
wouldn't it be funny if luchador didn't work
the literally any endgame build in question
then again obscure overshoot solos all of them
wait. ante 39 boss idea
indigo infinity
Sounds terrifying
Go on
Xω Blind size
X3.372e306 blind size
I would RQ on the spot.
btw how does the Showdown param work?
in other terms, nightmarishly large blind
makes the background not use the boss blind color
used for ante 8 bosses, or showdown bosses
and whoever call them finisher blinds are objectively incorrect
Right, I want to get my custom blind to have a chance to pop up on Ante 8.
oh yeah that too
And having the boss min set to 8 didn't seem to work.
showdown = true also puts it in the showdown pool
alright, put it in.
Now let's see if I get it
Yeah, nope.
Rerolled the blind 10+ times, no Soul Train
iirc it needs to be in boss
Possible, may not show in the docs
best to check how vanilla blinds do it in source
though I'm on updating docs currently, so I'll keep that in mind
Yeah the requirement comes from vanilla
It works
Not SMODS
Putting showdown = true puts it in the Showdown pool
It makes sense it wouldn’t be in SMODS docs because it’s not SMODS doing it
I should still add it
chat what's this
un chat !
if these are static, why do they need : 🤔
they modify properties of the class object
or read them
technically I could hardcode them using SMODS.Sound instead of self, but subclasses
what do i use to make a mod rn im useing a notepad Xd
I'm using vscode
VSCode is goated for Lua
ok il go look at it
Syntax Highlighting is a definite perk.
chat 👀👀
good because im trying to make a mod and ive never heard of lua before so i hope it also helps a little
We good?
hey meth what's up
meth 👀👀👀
aure 👀👀👀👀
🌝
wow that's cursed compared to how it looks on android
@glass scaffold chat 👀👀👀
the fuck
why does that not show smh my head
because discord is stupid and svg does not embed
how goes the moddings chat
I've been away for a month because of vacation and a move
the what now
so I'm totally out of the loop rn
oh modded is getting bigger than ever
(thanks to cryptid)
cant wait for jens almanac
balatro incremental
ante 5 million
also the numbers start to go funky
Me who only makes mods but doesn’t play:
we're skipping b7
we're skipping b8
damn
LSP 😻
dam
uhhh my textures broke
Edition?
yeah
edition: glitched. no wait that's already
It looks like astral but you put it in a deep fryer
I assume it scales the grid size based on brightness
lightness but yes
I would say it should also make the purple parts darker cuz rn theres only a difference between white and everything else
Unless you look really close everything else is the same shade of purple
That is nonexistent
ERROR edition
is there a way for me to add onto this function?
also what is the context for the cards in hand when scoring, like the scoring cards are in a list called scoring_hand
I think G.hand.cards should work
yeah, hook or lovely patch it 
that will include non-scoring cards
yea, I'm making those into scoring cards
that's just splash?
well the non-scoring ones in hand
ah
well in-hand cards don't have a "scoring" state, they just score if they have a way to
you can tell them to in context.individual with context.cardarea being G.hand
how would you cause the cards in hand to trigger?
Anyone have any devtools to recommend for a beginner?
What do they need to do?
just apply the cards in hand to scoring cards, make the cards in hand score
Hi guys, do you know how to check which jokers from G.jokers is selected/clicked on? Like in this case how to check that Jolly Joker is selected?
Just add their chips or do all the joker scoring too?
would that account for enhancement, seals, and/or editions on them?
It’d be a lot harder to do those, but in theory it’s possible
I wonder if it’s easier to patch the evaluate stack with checks for your joker
I think is .highlighted
i'll try to get the cards in hand highlighted and chips score first
For chip scoring I think you just need a SMODS.eval_this in that check you have
I mean there is a scoring step for cards in hand
It’s what Steel cards use
Also Baron IIRC
I don't know how to use that function correctly, I read the code for it but this doesn't work
also is there a fuction to deselect cards?
Pretty sure that question was asked before but I can't find it, what do you do to avoid setting a tooltip duplicate? Like if a Joker is negative and explains what negative means already.
I figured out how to use it, now I just need a way to unhighlight the cards
Can you throw a negative check where you assign the info queue? It’s not elegant but it should work
I’m wondering those should be color coded 🤔
Unlikely to be a SMODS suggestion but it would be good QoL for these scenarios
Flowwey, how the heck did you get Ortalab the way it does? I tried editing it and got a black screen in-game lol
I'm sure it's me
Yeah I’m not sure whether we should just dump out duplicate info queue items or not
I think yeah
I’d say no, since decks like Misprint have a reason to show you current.
Theres literally no poimt im having them
Base game stuff probably, but that’s more due to already knowing what the effect does so they’d serve no purpose.
Just displaying current is more efficient.
Although trying to check for if it’s duplicate reference to the effect vs. displaying effect + effect reference would be difficult.
When would there ever be a need for 2?
There’s an unhighlight_all
This is triggering every card in hand many times, is there a way to stop it after one (one loop though hand)?
Why do you iterate over every card inside?
Just do the current card
Oh yeah context individual is called on each card
then how do you get the current index of the selected card?
I don’t know what you mean
Anyways, just look at the game’s code
Usually it’s context.other_card to get the current card
oh yeah, i forgot about that
I could try yea
is there a good overview on how to implement number-compatibility for talisman somewhere?
or is it just the to_big function?
#1241172556849876993 message See if this is usefull
I really need to add that to the wiki or readme or something
my method right now is just pressing play and seeing if anything crashes but that doesn't seem very... scientific
You could try searching for score variables in the relevant mod code (both for hand levels and current/blind score) and then seeing where you use comparisons
.chips and .mult?
debuff = {hand = {['High Card'], ['Pair'], ['Two Pair'], ['Three of a Kind'], ['Straight'], ['Full House'], ['Four of a Kind']} = true} gives the crash in the image.
Any ideas on how to fix?
don't need the []s
what's the = true supposed to do?
You've got to do = true for each one individually, so
debuff = {hand = {
['High Card'] = true,
['Pair'] = true,
...
}}
I am requesting someone with more time than myself to create a "check if lovely needs an update" mod
i don't know shit about https sorry
I don't think http works on macos
but otherwise it shouldn't be too bad
I don't think
anyways the reason I came here
does anyone have a config page crash course or smth?
mostly just waht are the functions to help and what do values do?
virgin https vs chat http
for the config page, it needs to be a function that returns ui nodes
something like this
right I copied something already, but more like how do I add a button, or a text box, etc
save your SMODS.current_mod.config to a local var and use ref_table and ref_value in your nodes to change it
oh
for buttons, add a button = "function_name" to the config of your n = G.UIT.C node and it will call G.FUNCS.function_name when pressed
I'm not using SMODS config library
cause I need to support loading and saving config when SMODS isn't loaded
for checkboxes, use create_toggle function
unless it can make a config I handle the saving or loading of
Doesn't save or anything but stuffs happening
hell yeah
Also that additions tab needs a better check
All I add is an atlas for the mod icon
after getting the file header in the .lua file and i showing up on the mods directory what do i do next for creating a Joker?
There is one in the SMODS.Sticker PR but it hasn't been merged yet.
Previously it just checked if any object was registered since there wasn't a direct way made to check if the button should exist or not, but one was made afterwards.
OK good
Just pulled latest smod 1.0, does M to reload not work anymore?
Fix: install Source
Oh yeah
This is peak Balatro modding
recalc_debuff = function(self, card, from_blind) return card:is_suit('Clubs') or card:is_suit('Spades') end
This doesn't keep the cards debuffed the whole way through. What can I do to make it work?
custom sounds played using play_sound don't respect the volume parameter anymore
my bad, needs to be like is_suit('Clubs', true) I think
so the line should be recalc_debuff = function(self, card, from_blind) return card:is_suit('Clubs', true) or card:is_suit('Spades', true)?
Ayyy good stuff, it's 100% working now.
Thanks
I'm trying to get an enhanced card to trigger in hand but I just can't seem to get it to work, I'm very inexperienced with lua so excuse me if this is a slightly dumb question
SMODS.Enhancement { -- Ofuda Card
key = 'ofuda',
atlas = 'snyo_enhancementatlas',
pos = {x=0, y=0},
config = { x_mult = 1.25, extra = {mult = 4} },
loc_txt = {
name = 'Ofuda Card',
text = {'{X:mult,C:white} X#1# {} Mult when scored',
'{C:mult}+#2#{} Mult when held in hand.',
'no rank or suit.'}
},
no_rank = true,
no_suit = true,
replace_base_card = true,
always_scores = true,
loc_vars = function(self, info_queue)
return { vars = {self.config.x_mult, self.config.extra.mult}}
end,
calculate = function(self, card, context)
if context.cardarea == G.hand then
local hmult = self.config.extra.mult
return hmult
end
end
}
What's the error it's giving?
it's not giving an error but the cards aren't giving mult in hand
nope it's an enhancement, works all the same
I can't find SMODS.Enhancement. Can you link it to me?
Think your return is wrong, should be a table that it sends back
editions are different from enhancements
and SMODS.Enhancement does exist
not sure where are you trying to search
it might not be up on wiki
It's not on the wiki.
and there isnt an example mod for it yet
the wiki is laking in documentation in general though, so its not surprising
lot of the modding scene for this game is in its early stages
who knows where itll be in a year
as in the vars should be in a table or something else?
Like every other calculate iirc
Although maybe the actual values need to be done in the function, I don’t quite remember
But it’s potentially something like return { h_mult = card.ability.extra.mult }
ahhhh, h_mult
i was looking at the base game source code and wasnt sure if the h_mult on the shoot the moon joker would be the same with steammodded
hmmmmm still seems to not work
Might be that you have to adjust the values in the actual function then
doesn't work either
What’s the code look like?
calculate = function(self, card, context)
if context.cardarea == G.hand then
return {
h_mult = 4,
}
end
end
doing it again but adding
card = self
to it too
still nothing
.
You can use SMODS.eval_this or do it by hand
oh like change the function parameters? it might not be self card context?
ill do that hold on
hmmmmmm
No don’t return anything
wait what?
The eval this will add your values to the mult for you
aren't enhancements completely different
I don’t remember if enhancement returns are actually collected
okay so h_mult is not an extra?
no
I don’t think that will matter
Huh
oh youre the one making the lobcorp mod arent you?
But this one has different effects when played or held
The h is for hand/held
Oh of course it’d be h_xmult
hi that's me
well yeah h_mult is checked in hand, x_mult is checked when played
my friend likes the isaac you put in your mod
LOL

i find it funny because im working on a game-specific balatro mod (a touhou themed one, in my case)
-# (They don’t know I’m the Victin)
yooo twohow
hello i would need some help
i'm trying to make a blind that makes a random card per played hand not scoring
(something like that)
press_play = function(self)
G.E_MANAGER:add_event(Event({func = function()
if #G.play.cards > 1 then
if G.GAME.blind then G.GAME.blind:wiggle() end
local unselected_card = G.play.cards[math.random(#G.play.cards)]
G.play:remove_from_highlighted(unselected_card, true)
end
return true end }))
end,
this is the code i already have but it doesn't work
there's similar blind in cryptid
really ? didn't know 'cause i never played cryptid
lemme see
okay
this one
might not be exactly what you want, but I think you should be able to find that example somewhat useful
it sounds like something i'm searching for so
(mine isn't a finisher tho but that's not important)
guys I need to display the 'Negative' description for a joker
info_queue[#info_queue + 1] = {key = 'e_negative', set = 'Edition', vars = {1}}
is this the right one?
you should be able to do G.P_CENTERS['e_negative']
thanks ❤️
Quick question, is there a way to split a mod into multiple files? Seems like the regular require is targetting the actual balatro install loc
SMODS.load_file('path/to/file.lua')()
sick ty!
and I assume they load in order?