#đ»ă»modding-dev
1 messages · Page 28 of 1
I added it after the plasma balance
also those who will be confused, I can't unconfuse no matter what
So I call calculate joker with a custom context
That could work too. I wanted to do it in the same step to avoid balancing twice
I just check if itâs already been balanced
What do you check exactly?
Pretty sure it was the existence of nu_mult
I think that only works with vanilla and could have unexpected behaviour with other mods
But tbh so can my overwrite, since perhaps multiple mods canât overwrite the same line (?)
Why would other mods have assigned a value at that step other than to balance?
Variables can have any names, so mods can name a variable nu_mult because they calculate a new Mult in a different way, or they balance but use differently named variables
Yes but nu_mult is created where Iâm injecting
And I canât think why youâd inject at the balancing point other than to balance
And if the score is already balanced, balancing again doesnât really matter other than animations
You could inject before to add other decks
But wouldnât you just hook into the current line that evaluates the deck?
Iâm just saying itâs possible
Anyways, I wonder if adding a calculate_joker with context.balance is a good API idea
It could probably be included yeah
đ
Iâd probably suggest it coming after the deck evaluation and checking for the existence of variables and whether they are already equal before calling it
Again you canât guarantee the variables mean what you want them to mean
je vais tout wippin
If you can give me an idea for what else they could mean please do
I mean I already did
Any effect that sets new Chips and Mult value could reasonably have the same name
But surely balancing would always be the last event?
Not if you hook after that?
Or before
People will always be able to hook after it, you have to design it with what is already there
Except the hook order depends on the order mods are loaded
So it could happen OTHER HOOK -> YOUR HOOK -> VANILLA, or VANILLA -> OTHER HOOK -> YOUR HOOK, or OTHER HOOK -> VANILLA -> YOUR HOOK
Although if the variables are local you shouldnât see them between arrows.
Well if you have a way of doing it, go for it
I think maybe a warning for a while as a lot of people do do that (maybe show in the mod list too) but then later it can be removed
"a lot of people do that" isn't really an argument when almost all mods have to adapt in order to be usable
but yeah, most example mods are single file and whatnot
The only one that I use that's one lone lua file is AchievementsEnabler.
I think my mod SeedUnlocker also does that
I meant as it is something a lot of people do, so showing an error will help a lot of people spend less time trying to figure it out
I mean, I have a single file that requires the other files where my code actually is
But I guess Steamodded would read it as a single file?
also do we allow nested files?
PLEASE NO
i meant that as in mods not having their own folders
detect them and give a warning
I was thinking about it and it's a bad idea
But I was thinking having some kind of config file for steamosded might be cool
What do you mean
Like where we could just point it to the entry point wherever we want to store it realistive to the mod root
Like Mods/MyMod/Folder/entrypoint.lua
I guess itâs fine to prevent that
I have a top level file that requires the other files that are in lower levels
In separate folders or sub folders
eh, forcing entry points to be at 2 depth should be fine
what's a useful but concise warning message for nested files though?
like Mods/MyMod-main/MyMod/mymod.lua
happens quite easily when downloading source from github
"You nest get suplex"
eh, this mostly happens due to incorrect installs by users
for which I don't think this makes a lot of sense
fair
I would just if anything, show a warning somewhere if it does happen, to make troubleshooting easier
is ther ean easy way to print jokers in the current shop pool?
My game crashes when I use a modded consumable I made with loc_vars inside and then hover a "The Fool" card, any idea how I can fix this?
The color is too flat i think
I could detail it more
Probably need to make them smaller
I'VE DONE IT
not even a dollar for tiny blind
now gotta figure out the tags and shrink them a bit
smh
actually yeah the original intention was to shrink a blind with a consumeable but now a dollar is probably needed
This sure is a long stake I'm making...
ooh nice
oh yeah
skip the ante
skips work perfectly now
just gotta sort out the select buttons text and frame color
im bouta come back after a month and be super lost
How was your vacation?
ok so
i just need to change "when a boss blind is defeated" to "when blind in boss position is defeated" in the function responsible for raising the ante
imma do some stake sticker sprites now
hmmm I wonder if this could be an API
Iâm wondering if it can be an API
I can see reasons to not want the position of the Blind dictate when the Blind increases so maybe it could be a separate variable
by the way when this comes out don't ask me why the second boss position is called ''preboss"
Why is the second boss position called preboss?
why is the first boss position called boss
well i guess you followed the instruction, this hasn't come out yet
now i have to answer
at first i thought it was easier to make a thing between boss and big blind, then i moved it after boss and at that point i used the variable enough that it wasn't worth changing
Find and replace is your friend
Text editors for coding might have a refactor thatâs similar to find and replace
Alright yeah I probably should make it something like âboss2â
đ«
oh god finally some progress
porting the work I'd done on edition api to 1.0 but badges and tooltips are being a nightmare đđ
imo it's been much worse on 0.9.x
maybe I just don't understand how to call them
I should send you Negative Cards API
is it on 1.0?
I get double tooltips for default editions so it's just the way I'm putting data into the tables I think
I had some issues with them until I realized I needed to add the folder to lovely otherwise it wouldnât inject
No but 99% is through injections and I think the remaining 1% is moving code outside of the INIT function
The INIT function might be empty since all Iâm doing in the main file is adding a localization entry for the tooltip actually
In which case I reckon it should be inserted into some function (loc_def?) to work with 1.0
There is a loc_txt variable in Rank Game obj. How do you use it ?
You can't put a description text in it ?
there's no description text for ranks
it's just a string
(or a table of strings indexed by locale)
Thx
I think it depends
for centers, you can use a loc_vars function you can use, anything added to info_queue will be processed as a tooltip
what exactly needs passing to info_queue?
im not familiar with the balatro modding community, question I have are there a lot of artists who do joker art? and do they do it for free or for a price
Iâve mostly seen people make their own art or collaborate with each other
thanks for the information
any good resources to learn the basics of creating a joker mod?
i'm currently just looking at the source of another mod and using what i can see to make a very basic mod
Thatâs the way
See also the Polydactyly Joker in #1209506514763522108
Also I have a single Joker mod lying around that people have used as reference before
Hi!
I would like to add a draggable object/sprite -a card (69x93)- for a mod, can someone explain me how to do it?
I want the same effect as the Blind icon that you can drag and that comes back to its original position when you unclick it.
I know the function is somewhere in the moveable.lua file, but not sure which function exactly and how to apply it for my sprite.
Do you want that behaviour to apply to an existing playable card/Joker/consumable/etc. or do you want a new type that behaves that way
thanks for the response. I want it for a new sprite (so "new type" just composed of one picture)
I mean, I think if that new type had its own CardArea it would behave like that by default
I donât know if Blinds and Tags are stored in something that isnât a CardArea, but I donât see why it couldnât be a Card variant
Unless you want to animate it, I guess
But only because thereâs quirks to work out with animated Cards
Although maybe if nothing else interacted with it it could work? Iâm not sure whatâs the issue with animated Cards since I havenât dabbled into them myself
No, no animation, just be able to click on it, and drag it around, and when I unclick it it goes back to its initial position
I will take a look in the code about the CardArea, thanks
Maybe @mellow crag can help with creating new CardAreas
Someone messaged me?
Look up
Hi itayfeder, I'm just fooling around with the code for a mod, and I wanted to be able to drag an object (sprite) the same way it's possible to drag the blinds, and it goes back to the initial position when I unclick it. No other interaction.
So I was asking it there is a function I can call for that (and if yes, with which parameters), or if I need to create a new function for that
And Victin was telling me to look the the CardArea (function CardArea:init I guess?)
@zealous glen are you home and able to send your negative playing card code my way?
Not home
no worries
Iâll possibly just go to sleep when I get there
i may have done a thing
How many Blinds until you canât see them
it should just keep shrinking
let me try
Exactly
How long until theyâre smaller than a single pixel
The .
i should make it have more rows
All .s are debuffed
thunk..
i can't make soul_pos work idk why
{
name = "Energy Fuse",
slug = "fuse",
config = {},
rarity = 2,
soul_pos = {x=0, y=1},
cost = 5,
set = "Joker",
blueprint_compat = false,
eternal_compat = true,
loc_txt = {
name = "Energy Fuse",
text = {
"After 5 {C:attention}Four of Kind's{} played",
"destroy itself and create a",
"{C:attention}Energy Core",
"{C:inactive}--------------------------",
"{C:green}(0/5 - Four of a Kind's played.)",
}
},
},```
the code
the image
try passing the atlas
see the wiki
base game uses a single texture file for all the jokers and souls, so yes.
you just have to treat it as a spritesheet, not a single image
@solar vale I explained it to someone else recently, but the issue is that the game can store individual variables for Jokers and instructions for what the Jokers do⊠but there's no way to look at the variables and know what they do.
i read the wiki
so you learned how it works?
i still didn't got the point
What don't you understand?
the custom atlas part.
idk what i need to put there
because my mod loads jokers sprites via slug
An atlas is an image file which can contain multiple sprites (or frames) in it
Which tends to save memory
Compared to having individual .pngs for everything
are you on 1.0?
what have you done...?
what i wanna know is when is this coming to deck overview lol
I reccomend the phrase Stoned {x} where {x} is whatever it would normally be called. So Stoned Five, Stoned House etc
feder's already got Ore Of A Kind
I'm not going to recommend an in-development version that is still receiving breaking changes to everyone
there is no released 1.0.0 version
and I recommend 1.0 ports of mods should release when there's at least a beta release
what already has documentation will probably stay as is, but having at least some modders test the APIs is something I appreciate
fuck it we ball
I can't do all the testing myself or we'd be here forever
btw I made breaking changes to blinds
I thought you'd care
oh no
same deal of passing self as the first argument everywhere
aure should i start a new mod for 1.0
sure
shouldn't be too bad for tags, iunno about blinds or stuff like suits and ranks, but I can try to make those work
suits/ranks i could understand, but at least having blinds have mod badges in collection would be a good compromise for them not having a standard uibox
suits/ranks aren't too bad to implement, I'm just a little worried about cluttering the UIBox with badges from suits/ranks, enhancements, editions and seals of you know what I mean
yeah itll be a mess especially since then youd need to likw clarify which mod is which part of the card?
What about making Blind n be scaled by 2^(-n)
0.9.8
are you actually setting soul_pos in your constructor
Its in the local jokers branch
In the right joker
Do you register the sprite?
Yes
But i use that code you handed me to use
Compare with this
Check Polydactyly and
idk then
I will take a look at that when i'm back home
@fast badge [âŠ] Four Leaf Clubber
it's missing the assets but it works in 0.9.8
By the way there's some unnecessary stuff there
I'm using this code
You don't need the name check
you arent actually setting the soul_pos in the SMODS.Joker:new call
:/
Oops
As i said, i can't mess with this right now bc i will not be home
So, later today i will take a look
This works amazing but then get overwrite by something when SMODS init. Does someone knows how localization works in 1.0.0 ?
local loc_additions = NFS.load(SMODS.current_mod.path .. 'localization/' .. 'en-us' .. '.lua')()
local function update_language(old, new)
for k,v in pairs(new) do
if type(v) == "table" then
if type(old[k] or false) == "table" then
update_language(old[k] or {}, new[k] or {})
else
old[k] = v
end
else
old[k] = v
end
end
return old
end
G.localization = update_language(G.localization, loc_additions)
init_localization()
By creating a challenge where you start with it
you need a process_loc_text function
SMODS.Challenge {
key = "key",
loc_txt = {
name = "name",
text = {
"text"
}
},
jokers = {
"j_joker"
}
}
Something like that
Even for playing cards ?
SMODS.current_mod.process_loc_text can handle any general loc entries you need to make
Amazing
â€ïž thx
btw, a while ago I've made an Autumn Joker. You might just have the copyright of it
Not Debug mode?
Pretty
AFAIK Debug mode just sets a single variable to true
so you can do it yourself manually
@mellow sable DebugMode port when
I think it's tab
Did you hold it?
are blind chips 22 frames or 21 frames?
there are 22 frames in source images, but mystblinds uses 21
You can check the game assets
nvm i'm stupid
it's 21
Balatro players versus basic arithmetic
it's in the docs
self, card, dt
..
you're missing the self argument
it's not passed implicitly when defining functions this way
hm
make sure you're not using the card in one place and self, i.e. the center in another
aight
Why did I use SMODS.INIT đ
Meth already wrote a lovely patch that would still work
it's funnier this way
The funny method is to make it more complex
Write an if statement that checks if youâre running in 1.0, and if false itâll run the code in an INIT function, otherwise itâll set _RELEASE_MODE instantly
yeah
just go to the mods menu and disable + re-enable anything
as long as that mod doesn't use lovely, it'll quick reload mods as soon as you exit the mods menu
uh right
it also does that if you have it at top level in your Mods dir
I'll probably deprecate that completely though, given it also doesn't behave correctly with disabling mods
disable one, and it'll disable all mods with lone files at top level
yeah
it's a flaw with my implementation tbf
it could be resolved by using a settings file, maybe I'll change to that when I add more options for mod settings
possible
there's a bug with that where the name doesn't get changed, I totally forgot đ
Are there any tips for finding things within the Balatro source? I'm trying to find the 'quick restart' code (i.e. the thing that happens when you hold R)
What text editor do you use
search across files is goated
That would probably be a good idea, I was just looking through the files in 7zip lol
But yes VSC, SublimeText, Notepad++, etc. probably make it easier
what do you have so far?
if you use generate_ui, you need to do the localize call yourself
In this case I'd use loc_vars and return { main_end = ... }
Is this wrong ?
--- main.lua
SMODS.process_loc_text(G.localization.descriptions.Other, "joker_deck_jokers_Joker", {
text = {
"{C:mult}+#1#{} Mult"
}
})
--- generate_ui()
localize{type = 'other', key = 'joker_deck_jokers_Joker', nodes = desc_nodes, vars = {4}}
wrap the process_loc_text call in
function SMODS.current_mod.process_loc_text()
end
it needs to be repeated on each injection, else it will get overwritten
Also you can use loc_vars and return a key field instead of hijacking the localize call
great, np
when nativefs mod is installed, love.filesystem can't read my file, but when i remove it, it is fine, why would they do that ?
Progress !
of Jokers
wow
Is there any way to remove the "of x" of a suit ?
you'd have to override the localize call, probably easiest with a lovely patch
sounds fun
Yeah ok, not the n°1 priority on the list
ok i'm back, how do make a soul_pos work in 0.9.8?
How do I make a Joker with different dimensions from a standard Joker?
I mean, I can see Wee Joker and Morefluff's CSS
I'm just trying to wrap my head around it
so if my Joker has width my_width, if I multiply the base width by my_width / 71 it should work?
also, i have a question, is it possible to draw shaders in names and descriptions....?
But there's also some scale manipulation
There's no default way to verify a Joker is in the collection AFAIK, but I made my own
is_in_your_collection = function(card)
if not G.your_collection then return false end
for i = 1, 3 do
if (G.your_collection[i] and card.area == G.your_collection[i]) then return true end
end
return false
end
I'm not sure that works while in-game
I mean during a run
Interesting
Because G.jokers should exist during a run
so it should never be nil
Actually
I'm not even sure if a == nil is correct Lua syntax
Because sometimes it throws an error when you try to compare with nil
by the way
if it's only for tooltip reasons
you can check it when generating the tooltip
which is why I created this function in the first place
I'm hooking Card.generate_UIBox_ability_table
and inside I do
I mean, you do
you're generating a tooltip
That's generate_UIBox_ability_table
Yeah but the game calls that function
huh???
That seems wrong
That seems like it could cause breaking changes
I think if you call my function inside your loc_vars it should work
huh
@frosty dock I'm curious why
i'm trying to make this soul_pos work for a long time now
{
name = "Energy Fuse",
slug = "fuse",
config = {},
rarity = 2,
cost = 5,
set = "Joker",
atlas = "j_fuse",
soul_pos = {x=0, y=1},
blueprint_compat = false,
eternal_compat = true,
loc_txt = {
name = "Energy Fuse",
text = {
"After 5 {C:attention}Four of Kind's{} played",
"destroy itself and create a",
"{C:attention}Energy Core",
"{C:inactive}--------------------------",
"{C:green}(0/5 - Four of a Kind's played.)",
}
},
},```
And idk if its because of this:
```Lua
for _, v in ipairs(jokers) do
SMODS.Joker:new(v.name, v.slug, v.config, { x = 0, y = 0 }, v.loc_txt, v.rarity, v.cost, true, true, v.blueprint_compat, v.eternal_compat, "", "j_" .. v.slug):register()
SMODS.Sprite:new('j_' .. v.slug, SMODS.findModByID("RookieJokers").path, "j_" .. v.slug .. ".png", 71, 95, "asset_atli"):register()
end```
i tried setting an atlas
but it doesn't work
here
This is a test I had made to see how Soul Sprites worked a while ago
I'm also trying to make Soul Sprites work ATM
but w i d e
I managed to make the card wide but not the Soul Sprite wide
I want a normal sized card, though off center
I have a file to store some utility functions which I require
huh
Misc is just an array that contains the function
I wasn't saying not to use generate_UIBox_ability_table, but to use loc_vars instead of generate_ui
generate_ui is called from generate_card_ui, which calls loc_vars in its default implementation
they tried to override that default implementation, but didn't call localize, which is part of what that function is expected to do if it exists
nope, you can check against nil just fine
I know you can't compare order
a < nil or b > nil will cause issues
a == nil doesn't care about order
So it's fine
hmmm
do you know about changing (soul sprite) sizes
haven't tried that before, actually
i think its wrong:
SMODS.Joker:new("Energy Fuse", 'fuse',{}, {x=0,y=0},{name = "Energy Fuse",text = {
"After 5 {C:attention}Four of Kind's{} played",
"destroy itself and create a",
"{C:attention}Energy Core",
"{C:inactive}--------------------------",
"{C:green}(0/5 - Four of a Kind's played.)",
}
},1,5,true,true,false,false,"","joker_atlas",{x=0,y=1}):register()
SMODS.Sprite:new('j_fuse',SMODS.findModByID("RookieJokers").path, "fuse.png",71,95, "asset_atli"):register()```
and probably because it is inside the same file where this guy is?
for _, v in ipairs(jokers) do
SMODS.Joker:new(v.name, v.slug, v.config, { x = 0, y = 0 }, v.loc_txt, v.rarity, v.cost, true, true, v.blueprint_compat, v.eternal_compat, "", "j_" .. v.slug):register()
SMODS.Sprite:new('j_' .. v.slug, SMODS.findModByID("RookieJokers").path, "j_" .. v.slug .. ".png", 71, 95, "asset_atli"):register()
end```
@worldly sapphire
Is Energy Fuse inside jokers
you were trying to register the same Joker twice
yea
can i register him inside local jokers?
because he feels out of place now
Yes but you need to change how you do it so it does it correctly
change what needs to be changed to make it work
I think I see why Jokers copies have the wrong back
well, I don't see it, but I have a suspect
i assume itâs the same bug like what duped cards used to have
itâs just that the jokers were never flipped over in the demo
looks alright, what's failing?
nah it should work
what build is this?
hm iunno
try checking the logs
Finally! WIP artwork, which I traced from the Collared MV. Well, I think the soul sprite is finished (?) but the Joker itself needs work
cant have anything in detroit đ
maybe they use a different atlas which you need to set
there's G.shared_seals which is initialized on load
just reassigning to shared_seals in the inject function of your sprite should do
Does anyone know what controls the depth of cards?
the issue there is that these objects don't hold an atlas name, but the atlas table
Some of it is just the scale, but when I select the card it's then draw on top of other cards
oh i remember someone else reskinning the seals with different icons
forgot which mod
In programming and software design, an event is an action or occurrence recognized by software, often originating asynchronously from the external environment, that may be handled by the software. Computer events can be generated or triggered by the system, by the user, or in other ways. Typically, events are handled synchronously with the progr...
no
I mean maybe but it's not the right way to do it
you should be able to just change the atlas Seals use before they're initialized
events mostly serve the purpose of timing things like scoring calculations to the game speed and accel
these sprites get initialized before load, so just doing a postfix patch to the atlas inject function has you set here
barely anything happens between mods loading and the splash screen
inject = function(self)
SMODS.Atlas.inject(self)
your_code_here()
end
Congrats!
@fast badge
yeah it has to be on the same atlas
make an atlas with both
single atlas for all jokers
like the base game
but really if you want to do something like that find out how to pass the atlas when choosing the Joker
no
one png = one atlas
put things in the same png
simple
put one next to the other
like the literal atlases the game uses
Copy the gameâs atlas and add your joker
It wonât work with modded jokers but it should work with any vanilla jokers
(or just add soul_atlas compatibility in 1.0
)
Itâs hardcoded I think
yeah it's hardcoded for soul for some reason
i don't wanna add soul_atlas compat because it encourages individual files
That made it so fun to add it to Gateway đ
So that not everyoneâs mod directories look like this
Inefficient for the game I think
Itâs good practice to not do that (even though I do that)
I donât see why it needs to be barred for that reason
If it limits what mod devs can do (even in just edge cases like this)
Yep (without the overwriting part)
You can use your own atlas and itâll map to the same sprite
it's just how it's handled by the game
no...
it can just be in the same file...
i don't mind adding a soul_atlas, but I don't see how it helps
you can just hook or inject some code that pulls from the correct atlas
m
i'm trying to create a joker that makes all the cards played a random enhancement
and...
injection with lovely
though maybe set_ability is all you need
set_ability
It's executed when the card is created
So you can change the artwork at the right time (once)
Just do the same thing but in calculate_joker
You do it once when it's created and then once each round
you'd probably want to change the registered atlas in the list the game keeps as well as its position
then call reset
hmmm, maybe you can instead override the atlas directly instead of changing it in the list
guys how do i make this affect all cards in a played pair?
SMODS.Jokers.j_energycore.calculate = function(self, context)
if context.cardarea == G.jokers then
if context.before and not context.blueprint then
if context.scoring_hand then
context.scoring_hand[1]:set_ability(G.P_CENTERS.m_wild, nil, true)
G.E_MANAGER:add_event(Event({
func = function()
context.scoring_hand[1]:juice_up()
return true
end
}))
return {
message = localize('k_wild'),
colour = G.C.BLUE,
card = self
}
end
end
end
end
i'm trying to add some new boss blinds and the tooltip on the top left is not appearing at all, nor is the background changing color to reflect boss_colour. the sprite appears in the blind select and collection menus just fine. im really confused since this suddenly stopped working for reasons im unsure about. (using 1.0.0 alpha)
the blind itself is here:
SMODS.Blind {
key = 'the_radical',
loc_txt = {
name = 'The Radical',
text = { 'All enhanced cards', 'are debuffed' }
},
discovered = true,
boss = { min = 1, max = 10 },
boss_colour = HEX("54a74b"),
atlas = "mathblinds",
pos = { x = 0, y = 3},
debuff_card = function(self, card, from_blind)
if self.debuff and not self.disabled and card.area ~= G.jokers then
if card.config.center ~= G.P_CENTERS.c_base then
card:set_debuff(true)
return true
end
return false
end
end
}
maybe try reloading your game
no dice unfortunately
damn weird
How realistic is it that I'd be able to make jokers on my own if I don't know how to code/script? I've got artwork done for 4 jokers but now comes the difficult part haha
you can always look at others people mods
you never know until you try
it very much is
Yep I've been doing that for the past hour or so and am still a bit lost unfortunately. Like I can track some of the ideas, but I'd have no idea how to implement something like "earn $7 if hand contains three 7s" since most of the custom jokers do wild stuff or very simple stuff
you can take a look at my mod's github, jankjonklers, a lot of the effects for the jokers tend to be on the simpler side
mika's is another good mod to look at
Thanks I'll check out the lua dings!
đ«Ą
I will likely be back here with questions at some point lol
it's not helpful to dissuade people from trying to learn regardless
It's kinda fun to learn, if someone is willing to help I'm down of course but it's a fun-ish activity (at least at this point haha)
đ
if you have any questions folks here are generally willing to help you as long as you explain your issue and how you got there
I exist too
ive seen many of complete lua newbies get tracked into making a mod
Vice-versa, I like art so if someone is looking for trade-sies I can help out!
I mean, when I say "I don't know" I mean I'm not fluent in any languages, I understand some basic coding concepts and have made platformers in unity and gscript before, so uneducated but sorta can parse some things out
once you figure out how stuff is formatted, a lot of it is just stringing stuff together from the game's base code
so, while there isn't documentation or good reference, localthunk's code is pretty intuitive to read (still spaghetti code) and you can get a good chunk of the way there simply from looking at how something else in the game does something similar
FYI Lyman I am absolutely ripping your code line for line all over at this point for testing purposes, I assume that's okay with open source stuff but also don't know the protocol so just letting you know
i may or may not do some modding when steamodded full 1.0 and documentation drops
its more likely a may not but the thought that counts
Is there a way for me to force a card to show up? I have succesfully launched the game without breaking it and have my joker in!
I'd like to see if I managed to get the effect correct
you can use debug mode
oh ho ho, how do I enable that?
I'm already testing fortunately, just set it to be common and did some rerolling. It's crashing everytime I play a heart and I can use the error code to make fixes
also fortunately I can just reload to the moment before it crashed, so I always have the joker and a heart in my hand
Okay, I'm getting the following error: 83: bad argument #1 to 'insert' [table expected, got nil]
-- Heartless
if config.j_heartless then
local heartless = {
loc = {
name = "Heartless",
text = {
"Destroys each {C:attention}heart card{} scored"
}
},
ability_name = "Heartless",
slug = "heartless",
ability = {
trash_list = {}
},
rarity = 1,
cost = 8,
unlocked = true,
discovered = true,
blueprint_compat = false,
eternal_compat = true
}
-- Initialize Joker
init_joker(heartless)
-- Calculate
SMODS.Jokers.j_heartless.calculate = function(self, context)
if context.before and context.cardarea == G.jokers and not context.blueprint then
for k, v in ipairs(context.scoring_hand) do
if not (v:is_suit(diamonds) or v:is_suit(clubs) or v:is_suit(spades)) then
table.insert(self.ability.trash_list, v)
local card_to_destroy = v
card_to_destroy.getting_sliced = true
card_to_destroy:start_dissolve()
end
end
end
end
end
end
posting this here to see if i can get another pair of eyes on it because i'm really not sure why these mods aren't working
i'm trying to play with cryptid and it's not loading the mod files correctly for some reason
file structure has been confirmed to be correct by others already, and i also have a 0.9.8 steammodded setup that works correctly and the newest version of lovely (i am using 1.0.0 for cryptid though, i swapped out the mod folder)
how should i add loc_txt to a seal?
this is what the seal tooltip looks like rn
i found "description" and "label" in steamodded's code but it doesnt seem to work
sorry late but
theres a mod that enables it, and when you have it enabled, you can just hover over any joker in the collection and press 3
Is this how to create a modded joker card? I see riff-raff accesses G.jokers so I thought maybe SMODS.Jokers was similar.
And by create I mean spawn it in the cardarea for jokers
Here is the rest in the block:
if self.sell_cost <= 50 then
local card = create_card('Joker', SMODS.Jokers, nil, 2, nil, nil, nil, nil)
card:add_to_deck()
G.jokers:emplace(card)
end
you're on the wrong track there, G.jokers is just the card area for jokers
to that argument is the area to place the card into
Oh, that makes sense. How would I tell it to create a specific modded joker by its name or slug?
I think there's another argument to create_card that lets you specify a key
Ok, I see those spots under Riff-raff with the key_append: 'rif', but where does it get that key from?
I guess my question is, if I were to create my own key, where would I go to create it so that I can specify it in this function call?
you want forced_key
key_append is an RNG thing
Ok, so is forced_key just the slug of my joker? So since my slug is rooster_joker would it be j_rooster_joker or the former? Or something else lol
yep, it would be j_rooster_joker
Alright, let me give that a spin
Sweet, it works, thanks!
I'm still new to this, and wrapping my head around the codebase is tough đ So I appreciate it a lot
Okay so I read all the things and I have questions, what version do you use (smod 0.9.8 or the 1.0) and how did you do this (if you can give me the whole code it will surely help me a lot )
Me? I am on 0.9.8. Here's the calculate_joker function that I modified:
local calculate_joker_ref = Card.calculate_joker
function Card:calculate_joker(context)
local ret = calculate_joker_ref(self, context)
if self.ability.set == "Joker" and not self.debuff then
-- If selling "Egg", checks if its sell_value is 50 or higher
-- If it is, creates Rooster Joker
if context.selling_self then
if self.ability.name == "Egg" then
if self.sell_cost >= 50 then
local card = create_card('Joker', G.joker, nil, 0, nil, nil, 'j_rooster_joker', nil)
card:add_to_deck()
G.jokers:emplace(card)
end
end
end
end
return ret
end
I mostly studied the source code and tried to replicate the way it worked with certain mechanics
Thereâs one or two tutorials in #1209506514763522108 and I have a simple one-Joker mod laying around some people have used as reference
Strictly speaking you should only need the context and maybe the not self.debuff, though Iâm assuming Steamodded makes owned vanilla Jokers work with modded Joker logic
I'm not too sure about any of that tbh. Basically just going off what I see in other people's mods and the source code
Ah I see you didnât take ownership of Egg
So nevermind
@zealous glen how did you did your soul joker ?
This one
The Steamodded wiki lists the argument you need to pass
when creating the Joker
You just need both textures to be in the same atlas
Are you master yoda ?
Attacked by seagulls I was not
modded argument you need steam list, the pass to ?
to pass the argument you need to steam the modded list ?
đ đŻ đ
𫞠â©ïž đ«·
(what is love.toml btw)
love.toml doesn't exist
there's lovely.toml, which contains information for patches
i.e. changing or inserting code to the game source
me when i make a new Sprite object every frame
ofc it's possible by just using a hook
there's no api method for it as of yet
that, I'd have to implement
until I do that, you can just modify Card.draw
checked out 1.0.0 a bit. im seeing tons of require('lovely')s but where is the lovely tho. is it referring to the version.dll or a specific lua file? im not getting any crash just out of curiosity
local card_draw_ref = Card.draw
function Card:draw(whatever)
-- code to run before the function
card_draw_ref(self, whatever)
-- code to run after the function
end
the lovely module is exported from within lovely, that is version.dll
oh ok got it
omg
sorrty for the ping but do you still have that one-joker mod and can i use it as a base for my own thing
@austere schooner itâs missing the assets but you can just create whatever. The code is a bit redundant at a times since I was just starting out
đ
You donât need to check the name of the Joker, etc.
Does anyone have a template to help mod in jokers?
what steamodded version?
0.9.8
Thank you
@ancient finch
I'm using the template, this is super useful
Thank you
I still donât think you should need to modify draw
Iâd start with set_ability then copying that to calculate_joker to make it work every round
is there any doc available for localizing in game texts in 1.0.0?
like adding support for multiple languages
in place of any loc_txt object, you can put a table that's indexed by locale
loc_txt = {
['en-us'] = {
name = '',
text = {''},
},
['fr'] = { ... },
-- etc...
},
How are you drawing the floating sprite?
Again, just define set_ability for your Joker
Configure a soul sprite thatâs just the Joker itself to start with
Then inside set_ability change the atlas and redraw it
You can also change the size and position (within the atlas)
Well, you probably want to change the scale transform rather than the scale
What part canât you follow
I mean the actual image, are you grabbing it from the main atlas and resizing it?
For changing the atlas I recommend looking at Sprite:reset
Look at the Steamodded wiki
And where do you draw the shader?
Wherever you define calculate_joker, there you should be able to define set_ability
Is that before you resize the sprite?
When a card is created
As I said
When creating the Joker, add the variable that tells it to create a soul sprite
Then define set_ability to overwrite the soul sprite atlas and its size
You can refer to Sprite:reset as a reference
@fast badge
You donât need to change the sprite every frame, just occasionally
so if it looks like
joker.localization = {
name = "J",
text = {
"{X:mult,C:white} X3 {} Mult"
}
}
i can do it that way too?
as long as it's processed by a steamodded API, yes
got it thx 
Iâm telling you how to change the soul sprite when the Joker is created
@fast badge
What part of programming the Joker to be created with a soul sprite doesnât make sense?
The Steamodded wiki should list the soul_pos parameter or whatever 1.0 uses to define soul sprites
Again
- Program the Joker to be created with a soul sprite;
- Overwrite the soul sprite texture when itâs loaded;
- Overwrite the soul sprite texture whenever itâs calculated (as a function of context).
I think youâll have to hook into draw regardless because you need to scale the sprite down right?
Thereâs a scale parameter
yeah i should add that
Remember why the game has two different sprite sizes but Jokers always have the same size
Again it should be on the wiki
Itâs there
Itâs clearer in the 0.9.8 wiki, but you can find it in 5. SMODS Center
Look at API methods
Or ctrl+F
nope
I have the wiki open Aure
soul_pos is literally not in there
Iâm talking about set_ability
Itâs just an API function you can define
Like calculate_joker
Or I guess itâs just calculate now
Well, now you know how to use it, you just define what you actually want it to do
Again, look at Sprite:reset
Yes. Importantly, it shows you how to change the atlas
Thatâs where step 3 comes in
Whatever you do in step 2 you also do in step 3
If you want the Joker to appear in the shop while showing a sprite, you can add that in set_ability, otherwise just add a transparent texture for now
I suggest adding a copy of the Jokerâs own texture so you can see something happening
After all, youâll want to do this again anyways
Set its position so itâs redrawn
What does "atlas" mean?
In computer graphics, a texture atlas (also called a spritesheet or an image sprite in 2D game development) is an image containing multiple smaller images, usually packed together to reduce overall dimensions. An atlas can consist of uniformly-sized images or images of varying dimensions. A sub-image is drawn using custom texture coordinates to ...
Which Joker
I think you changed the base Joker instead of changing the soul sprite
Whatâs your code
Then why are you complaining it doesnât work if you donât know that
just tried the new loc feature. things are going nicely. but i'll still wish for a separate lang file (which modders can obviously choose to do so but most of em havent). gotta say, now translating a mod became 3x more exhausting but at least i dont have to overwrite the english texts and pray for no crash on startup
Itâs written inside the sprite definition.
Pretty much
You can also change the size of the sprite
I mean thatâs because you set the atlas wrong
There's probably better ways to do this but this is just the proof of concept
floating_sprite is vanilla code
oh you probably didn't define a soul sprite
to create the sprite
oh wait I misread
in 0.9.8 you just pass it a soul_pos
what UI library is balatro using, is there any documentation or tutorial on it
Aure said it was in optional params
actually no they said this
Whenever I try and run this code I get "tried to index a nil value", and I have no idea what's causing it. I'm using the blank template, and this is the bit of code it doesn't like, specifically the line I marked
function SMODS.INIT.BlankJokerTemplate()
--localization for the info queue key
G.localization.descriptions.Other["your_key"] = {
name = "Example", --tooltip name
text = {
"TEXT L1",--tooltip text.
"TEXT L2",--you can add as many lines as you want
"TEXT L3" --more than 5 lines look odd
}
}
init_localization()
--Create and register jokers
for k, v in pairs(jokers) do --for every object in 'jokers'
local joker = SMODS.Joker:new(v.name, k, v.config, v.pos, { name = v.name, text = v.text }, v.rarity, v.cost, v.unlocked, v.discovered, v.blueprint_compat, v.eternal_compat, v.atlas)
joker:register()
if not v.atlas then --if atlas=nil then use single sprites. In this case you have to save your sprite as slug.png (for example j_examplejoker.png)
SMODS.Sprite:new("j_"..k, SMODS.findModByID("boltExtension-main").path, "j_"..k..".png", 71, 95, "asset_atli"):register() [THIS LINE HAS ISSUES]
end
--add jokers calculate function:
SMODS.Jokers[joker.slug].calculate=v.calculate
--add jokers loc_def:
SMODS.Jokers[joker.slug].loc_def=v.loc_def
--if tooltip is present, add jokers tooltip
if(v.tooltip ~= nil) then
SMODS.Jokers[joker.slug].tooltip=v.tooltip
end
end
--Create sprite atlas
SMODS.Sprite:new("youratlasname", SMODS.findModByID("boltExtension-main").path, "example.png", 71, 95, "asset_atli"):register()
end
đ
But I think you if you manually define a soul_pos at the right time, the game should do the work for you
It's part of a bigger file, but not by much. It has one joker in it
is boltExtension-main the exact mod ID you have in your header?
Ah, no it isn't
first, try writing ```lua
YOUR CODE HERE
``` on Discord
?
you didn't have to ping me for that
my bad
yeah soul_pos is valid, I just forgot to add it to docs
pro tip, "doesn't work" is never specific enough
where
you don't even have your own atlas though?
like all that will do is take the upper leftmost sprite in the Joker atlas (Jimbo) and place it on top of itself
Hex
it's a hex code
I tried hex but it stayed black
Oh, I think I've had this issue even with 0.9.8
show
try no #
You need to make sure you're actually defining the soul_pos at some point
That's how Balatrostuck does it
what do you mean
sorry i just woke up ungrogging myself
Good morning Lyman
when i was trying to do soul_pos shenanigans back when soul_pos just got updated to work i had to tweak how i declared my jokers
now this is for 0.9.8, so im not sure how much of this is accurate but
this was my general function to initialize variables
I removed the # and tried FFFFFF and it was still black
local function init_joker(joker, no_sprite)
no_sprite = no_sprite or false
local joker = SMODS.Joker:new(
joker.ability_name,
joker.slug,
joker.ability,
{ x = 0, y = 0 },
joker.loc,
joker.rarity,
joker.cost,
joker.unlocked,
joker.discovered,
joker.blueprint_compat,
joker.eternal_compat,
joker.effect,
joker.atlas,
joker.soul_pos // important part here
)
joker:register()
if not no_sprite then
local sprite = SMODS.Sprite:new(
joker.slug,
SMODS.findModByID("JankJonklersMod").path,
joker.slug .. ".png",
71,
95,
"asset_atli"
)
sprite:register()
end
end```
make sure you have no trailing/leading spaces either
Hellooo ! Does anyone have a premium freepik account by any chance? I have some questions!
doesn't apply to 1.0, it literally takes a table
This is how its written
--- BADGE_COLOR: BBBBBB
oh i figured its different now, point was that i had to add in the field for soul_pos
This is what the guide says
so id imagine maybe trying doing the 1.0 table equivalent of that of making sure the soul_pos is mentioned? shrug
what version are you on?
I thought I'd made that work, maybe I screwed it up
try BADGE_COLOUR
oh lmao
if so that's on me, either is supposed to work
its because its color yeah
The Bri'ish attack again
consumables vs consumeables moment

with the exact line, too
Testing a background. I think unless I add stuff to the side it won't be very visible, so I shouldn't try too hard
What does this section of the template do?
--localization for the info queue key
G.localization.descriptions.Other["your_key"] = {
name = "Example", --tooltip name
text = {
"TEXT L1",--tooltip text.
"TEXT L2",--you can add as many lines as you want
"TEXT L3" --more than 5 lines look odd
}
}
defines the text for a tooltip
Where does the tooltip appear, I mean, sorry
when you hover over a joker/consumable/whatever thing you put it on
Oh, so its not a necessary thing?
no, completely optional
Ah cool
Thanks for all this help
I'm gonna keep cracking at this, try adding a more complicated joker
How do you make a joker that doesn't add flat chips/mult, and is triggered by scoring cards akin to fibonacci or the sin jokers?
use context.individual in the joker's calculate function
Is there documentation for this, so I don't need to keep badgering people?
for this in particular, no. docs are still incomplete
well it'd have been nice to have the complete definition then...
ok, what is that atlas then?
yeah, the blankjoker one
How does context.individual work?
it triggers for each scoring card, which you get passed as context.other_card
How do you check enhancements on scoring cards? Specifically wild. Would it be context.other_card.wild?
there's plenty examples for this in vanilla code
context.other_card.ability.name == 'Wild Card' something like this
I'm having an issue with a custom joker. It's supposed to add 50 chips and 20 mult unconditionally, but config = {extra={chips=50, mult=20}} doesn't seem to work
That's because for the most part you need to code effects yourself
Ah right, no worries
The game does a small number of automatic effects, but anything inside extra isn't it AFAIK
Would this work then?
calculate = function(self,context)
if then
return {
chip_mod = 50,
mult_mod = 20,
}
end
end,
You still need to do the effects for adding chips and mult
I looked at stuntman, which adds unconditional chips
Like the cosmetic dffects
Oh wait really?
Forget the exact lines that do it tho
Kinda
You need to use context to make it work at the right time
What's the context for end of hand scoring?
context.after
But I think you'll specifically want context.after and context.individual and cardarea == G.jokers
Not sure
context.after triggers once a hand scores, not once the cards finish scoring and the jokers start scoring
I mean, "end of hand" and "jokers scoring" are different moments ;P
True, sorry for the bad clarification
maybe not context.before and not context.after and context.individual and cardarea == G.jokers
Is it possible to make the joker trigger twice, once to add chips, and then once again to add the mult?
This is what I have so far
calculate = function(self,context)
if context.joker_main then
return {
message = localize{type='variable',key='a_chips',vars={50}},
chip_mod = 50,
},
return {
message = localize{type='variable',key='a_mult',vars={20}},
mult_mod = 20,
}
end
end,
replace the first return with SMODS.eval_this
Also on latest main, calculate arguments are self, card, context where self is the center object and card is the Card
What does SMODS.eval_this do?
Does that mean I need to add an additional parameter to the function?
Also, "eval_this" just crashed the game
yeah
It was working fine without card, what does that do?
mb, needs to be SMODS.eval_this(card, { ... })
What do I replace the 3 dots with?
basically I had to change arguments for the sake of consistency
Ah right
just put the table from the first return in place of { ... }
I'm lazy, yk
Wait, so this?
Maybe I misunderstood what they wanted
Also works, but you can return the second time just fine
It scored when abstract, stuntman, RtB etc would score
it's just that you can only return once, so if you need multiple status texts, you can use that function
Crashed on custom new game with the joker
attempt to index local 'context' (a nil value)
Swapped card and context around in order so it reads (self,context,card)
Lasted longer, crashed as soon as hand was played; attempt to call field 'eval_this' (a nil value)
send me that crash screen please
thanks
oh wait you had me thinking you were on 1.0 by the way that function was declared
No, you said 1.0.0 wasn't out yet didn't you?
I must've misunderstood
No, I'm on 0.9.8
its not released, but it's accessible
plenty of people are porting their mods to 1.0.0 already, it's easy to get confused
I should've asked haha
self, context is correct for 0.9.8, and there is no SMODS.eval_this
this is the function as implemented in 1.0, you can just paste it into your mod and use that
or use card_eval_status_text directly, but that's a bit annoying to deal with
I'll try eval_this and see if it works
Does it just get whacked at the top of the file?
yeah, wherever in global scope will do
Cool
Crashed upon playing a hand
Does the function need to be defined as SMODS.eval_this?
It can be defines as eval_this if you don't try to call it as SMODS.eval_this
Though I wonder if one mod could overwrite another mod's functions when defined in the global scope like that
it could
yaaaay
in that case local function eval_this would be safer
Crashed on playing a hand, different crash tho
calculate arguments on 0.9.8 are self, context
technically card, context since self isn't a center but a Card
but there's no third argument
It doesn't affect me much since I'd prefer to define everything inside other files then require then, which by default should have them defined locally somewhere, but it does reinforce me wanting to make people create their own tables for their own mods
What do I do then?
I don't even know where its referencing card
just change any card you might have in your calculate code to self
but it's an argument
But I assume as a thing inside a function it doesn't matter
as an argument name it doesn't matter
IT HECKIN WORKS
THANKYOUTHANKYOUTHANKYOUTHANKYOU
These are the 2 simplest jokers as part of my "TTRPG classes as jokers" list. Barbarian, Paladin, Rogue, Wizard, etc.
the file itself ig
Oh, bizarre thing. When I declare --- BADGE_COLOUR: XXXXXX, the shape of the badge is rectangular, not rounded on the ends like the rarity tags, or tags for other mods
I think that happens when your mod name is too long
you can make it display a shorter one there with --- DISPLAY_NAME:
I just wanna see what's in it
the file is just that one sprite?
ok i don't see what you're trying to achieve with soul_pos then, it would just be that sprite floating on top of itself
I donât think you need to scale the height and width
Let me grab how Iâm doing it in edition api
How do y'all scale your 1x textures up to 2x without it getting blurry? Mine are always blurry
Oof
most image software can upscale without making it blurry
I was using GIMP and it was blurry. What software do you recommend?
Neighbor scaling? I'll look into it
self.children.floating_sprite:draw_shader(v.shader, nil, nil, nil, self.children.center, scale_mod, rotate_mod) this at least draws the shader directly on the floating sprite, I'm not sure what applying the hologram shader would look like but making a custom one is possible
I use GIMP hold on
just replace v.shader with "hologram" iirc
make sure interpolation is none on scale image
Oh, thank you! I'm sure that has been on this whole time
Also I'm just curious, why does it have 2 different texture sizes anyway?
I thinkit should have a default, but I'd either use a transparent sprite or just hide it in another way
yeah a transparent would work nicely
depending on what the game is doing, another way is changing a boolean variable or changing the size to 0
Have you tried the shader? Does it apply properly or is it still too big like earlier?
hmmm I wonder if you need to define the things it's being passed
I know for a fact that what I have works
I mean it's slightly different because it's injected into card.lua but sure
if v.apply_to_float then
if self.edition[v.key:sub(3)] then
self.children.floating_sprite:draw_shader(v.shader, nil, nil, nil, self.children.center, scale_mod, rotate_mod)
end
end
local scale_mod = 0.07 + 0.02*math.sin(1.8*G.TIMERS.REAL) + 0.00*math.sin((G.TIMERS.REAL - math.floor(G.TIMERS.REAL))*math.pi*14)*(1 - (G.TIMERS.REAL - math.floor(G.TIMERS.REAL)))^3
local rotate_mod = 0.05*math.sin(1.219*G.TIMERS.REAL) + 0.00*math.sin((G.TIMERS.REAL)*math.pi*5)*(1 - (G.TIMERS.REAL - math.floor(G.TIMERS.REAL)))^2
these are the modifiers
What makes jokers shake when they trigger? For example in my case, Onyx Agate shakes when it grants +7 mult to a card, but my custom joker doesn't when it grants mult to the same card
I can't find what causes that
juice_up
usually card:juice_up()
But where do I put it in the code?
nope, you just call it
new devtool?
keybind for reloading mods
Where do I call it though? Inside the calculate, or in the main area?
normally passing card = card inside the return should do for calculate
what key should this go on
left mouse button
To pay respects
well it's hold F
Is it a known thing that jokers do the juice_up animation while SteamModded is installed?
what about Ă
Like, seemingly at random when they shouldn't
What do you mean
they're supposed to, only reason i could think of why they wouldn't is you're not passing the card, or you're using talisman to disable animations
No, it does it without my mod installed
perhaps Ă isn't a bad idea, but I think it may be a hassle for some
đ
Perhaps á is better
what about the ~^ key?
â
i might just put it on M
because yk
mods
but also
m
cryptid reference
the combination is holding that key
Here is what I mean
like it is for restarting a run
Watch Druid juice up when Fighter triggers, and then they both juice up when the deck is reshuffled
that's just lag
perhaps `?
the card did indeed shake
No, that's just my shitty laptop
Maybe it's in the wrong context
OBS barely runs on my laptop
maybe ÂȘ or Âș
kinda but keep in mind this doesn't preserve order
one thing to keep in mind is that util variable will be global
you might wanna give it a more unique name
I have this
The user probably does
You can do it the way I did
ipairs iterates only over integer keys from 1 to however many there are sequentially
(AFAIK on the sequential part)
context.selling_self
alpha 0530g is up
you can now hold M to reload mods, or hold Q to restart the game
context.blueprint tells you that this effect is initialized by an effect of a blueprint-like card
you might want certain effects not to trigger when this is the case, like the scaling part of any scaling joker
I'd start by trying not scaling the height and width first, I'd imagine that control s grabbing the soul image from the atlas
If I'm starting a new mod now should I be using main branch (1.0?) or 0.9.8?
in terms of the joker loading, is it possible to do
for k,v in ipairs(CONFIG) do
if v then
load CONFIG[k]
end
end
?
you could try just hardcoding them as something different to test
Take a look at how Jokers like Wee Joker do this