#đ»ă»modding-dev
1 messages · Page 54 of 1
no
the main issue is that in the tree of classes that the injection routine passes through, all classes with an obj_table and obj_buffer are considered to be leaves
yes makes sense when I look at this function
but doesn't Center (a class with obj_table/buffer and thus a leaf) also get extended?
yes, but there are no subclasses of Center that need a custom pre-/post-injection step
Center is a "leaf" (which is becoming a very vague definition for me now) and thus has it's subclasses only inserted on steamodded start?
as opposed to GameObject that isn't a leaf, and thus inserts any subclass at any time, including those after steamodded start?
Center is a leaf in the sense that its subclasses are not considered by injectObjects, as subclasses would usually have the same obj_table/obj_buffer inherited from Center
so exploring these as additional nodes would mean centers get injected multiple times
I think this should work?
at that point the obvious question is why is Center even a leaf? because it has objects?
this would be a consistent way between classes that do/don't call inject_class to hook onto the injection process
because they share a common object table and are all injected roughly the same way, with the difference normally being controllable by inject functions
it would be perfectly reasonable to call SMODS.Center directly and provide these functions yourself because there's nothing inherently different
I mean the code seems valid to me, but I'm just starting to understand steamodded itself đ
maybe <pre/post>_inject_class is more consistent naming
otherwise it would be kinda confusing with inject yes
so I could do :inject_class() myself? I tried doing something like that but all the attempts I made failed lol
you shouldn't
fair 'nuff
spends like an hour trying to find the problem
realise that i wrote 'voucher = ' instead of 'vouchers ='
đ
hey, at least you found it >:D
though you really should be using steamodded...
true but i was too lazy to write more stuff
I was thinking I might have to push 0805i, but thankfully it's the next day now 
not worth it to be that lazy, it'll mean more work later
- you literally can't distribute anything or get anyone to help you debug
yeah but it was really more of a one-time thing

Tomorrow ?
tomorrow
recent steamodded is fairly good at not making you worry too much about the specifics of the game's code
is having responsive deck sizes worth this horrible 0.4 second delay if a deck adds a consumable?
if "a deck adds a consumable"?
can the delay not be changed?
pushed tomorrow's fix upstream btw
oh I see, that gets rid of the super
Darn they almost all fit in one page
This is sick tho
they would do if cryptid wasnt installed
Damn you Cryptid
also selecting some of your decks spits out a LOT of debug messages
when I click the alst one I get a whole load of keys
oops, that thing I changed reversed the stake order đ
lmao
Wdym by "click on it"
when he clicks on it in his deck selector lol
Huh
those are from SMOD itself
actually i don't even know what may not work correctly đ
Neither do I.
ejwu wrote that so that makes sense
is this correct stake selection behaviour I haven't played this game properly in ages lol
its when apply to run is called
Welp, I should start making ranks
yeah, it's the only way I can find to actually calculate decksize consistently
I actually saw SDM's debug message then but discarded it cuz it was in apply lol
I mean, what other options were there? đ€
gah, stake api has so many bugs in
Pushed the debug message removal
What do you mean âconsumeableâ?
aure just enabled hard mode lmao
what on earth is this for thunk
oh I found a fun little piece of thunk code a minute ago
if node.config.vert then local thunk = tx; tx = ty; ty = thunk end
swapping variables by using a temp thunk (don't mind the fact you can a, b = b, a in lua)

wtf lmao
no no, it's a
var named 
yeah wtf
anyways I still have no idea why the version where the same code runs for every stake works but doing it only once doesn't
so i just reverted to that
aure do you know who wrote stake api?
hey do u guys understand how the fusion mod api works because im trying to make my own addons for a pack and i am so lost đ
just for clarity, which fusion mod? I think there's multiple lol
Right, so, quick follow-up on that:
G.P_LOCKED = {}
for k, v in pairs(G.P_CENTERS) do
if not v.wip and not v.demo then
...
if not v.unlocked and (string.find(k, '^j_') or string.find(k, '^b_') or string.find(k, '^v_')) and meta.unlocked[k] then
v.unlocked = true
end
if not v.unlocked and (string.find(k, '^j_') or string.find(k, '^b_') or string.find(k, '^v_')) then
G.P_LOCKED[#G.P_LOCKED + 1] = v
end
...
end
end
these string patterns are kinda preventing my sleeve___rest_of_key from entering P_LOCKED
I could just hook into SMODS.SAVE_UNLOCKS() but that also seems a tad hacky
The fusion jokers by itayfedder and lyman! Im trying to do my own fusions right now but im a little too new to modding and coding
I know they have a api for it, but im not a very great coder and I dont really understand what im doing lol
okay yeah it's probably within reason to change that
that's just mindlessly copied base game code
before you make anything, you're aware they're using a version of the modloader that will soon be outdated?
I mean, I'd argue 0.9.8 is the "last stable version" if that makes sense
Yeah, I know, but I also need to use that specific version since I play on my phone lol
mostly cuz it's the last one in releases tbf
I would argue it's less stable than current 1.0
fair enough lol
Alright, as long as you know your options. Did you look at their github code?
0.9.x were pretty much daily releases for a week lmao
I did, Im looking around and poking at it but im not the greatest with lua, and alot of it is wierd. Im just trying to figure out how to use the api to create a new mod alltogether
Yeah, it avoids some centers:
-booster packs
-enhancement modifiers
-c_base, soul, undiscovered_joker, undiscovered_tarot
Could do a blacklist on those instead of a whitelist on the rest?
have you actually made a basic mod yet?
trying, I thought doing the api would be easier đ
their API doesn't make creating jokers any easier đ
wait so what's the issue?
oh i think i see
i kinda left some of the stake stuff the same that probably needs to be overhauled because I assumed that you would still have a linear progression in the stake selection UI
this new UI doesn't do that, so it's causing things to break down and the unlock behavior should be completely refactored
and I think stakes should (somehow) have a specific order, rather than jumbled everytime
Or else it wouldn't be called "progression"
Ordering is already handled I think
But different mods can add different branches
Yeah but for now I just wanna use existing jokers to make fusion jokers yknow? Learning it step by step!
Yeah this is the part that isnât handled, but only for unlocks
I have to imagine creating a new Joker is easier than a Fusion Joker
Yeah thatâs what Iâm learning now, still trying to figure out how to do both
Try Steamodded 1.0
I donât think I will for now because Iâm building these to play on mobile and the mobile version doesnât properly support that
please don't talk about modding on mobile in this server
Oh, sorry
Luckily I am playing on windows
Realll, feel bad for the people who are trying to mod on r2modman lol
It also doesnât handle stickers on decks very nicely at all
Iâve sort of cobbled together an unlock system
In that it checks for wins on the applied stakes, but I havenât been able to get the stickers working properly, although that could just be because my test alternate stake path doesnât have any stickers
Yeah I think that relies on the same system :/
Well you would also see tooltipsâŠ
Like âGold Sticker - Used this Joker to beat Gold Stakeâ
Or whatever
Oh I see
It fucking worked.
huge
if you care how it works i can add some comments
but my recommendation is pretend like nothing ever happened đ
cheeky having it be checking the previous cards rank instead
thank you so bloody much for real ;-;
how do you set up consumable booster packs correctly?
getting started on making custom jokers, what is the canvas size for the sprites? aseprite puts it at 71x95px with a 1px transparent border, but i wanted to confirm
yup, 71x95 with 1px transparent border
noice. ive never done digital art before but this'll be a fun project
ok so i just finshed making my mod for 0.9.8, how to I transfer it over to 1.0.0?
Seek Steamodded wiki
is it on the GitHub?
isn't draw_hand what makes the deck draw to hand in booster packs? it doesn't appear to be working did I miss something?
Who needs balance when you have style
(has zeros style)
What can cause my mod to not be seen?
(it's in the right folder, it has the ---Steamodded header)
Is your mod working?
Well, Balatro can't even see it, so I guess no?
Here's all the code
What's the logs say?
Duplicate Mod prefix joke used by JokersButArya, JokerDisplay
WHAT
Welp, I finally got it to work.
Doesn't mean that code is working.
what id give right now to understand anything im looking at
what do you mean error on line 10 i dont even understand what line 10 does
Youâre missing a comma after your loc_txt. The reason it wasnât loading is because you didnât define a mod prefix in your header, and it defaults to the first 4 characters of your name, which Joker Display is already using
Can you show your code?
Wow, I completely messed this up.
Thanks for spotting those.
is there any quick way to check whether a smods object originates from vanilla or a mod?
Check if the objectâs center has âmodâ in it.
Although it doesnât really work with cosmetic mods like Aura.
i'll check if that'll work for what im doing, and also i dont mind not detecting cosmetic stuff
yup it works, thank you
That fixed the registration and loading.
Now the name and desc are not showing up in game.
you need loc_vars too, hold on
loc_vars = function(self, info_queue, card)
return {}
end```
Ayyyy
now how to do that fancy text for the mult...
Anyone here know how that's done?
{X:mult,C:white} X#1# {} Mult
(the #1# is for using vars, which you should, but technically you can just put 2 there)
couldnt u also look off the other already existng jokers and d work from thrrere
as like a example almost
@languid mirage what was the mod that only worked with cryptid / modded consumables that stacked them?
incantation?
is there a simple way of changing the saved message to say saved by another joker?
Not sure if this helps, but maybe this'll give a hint
btw you have anything that checks for when a card of a certain suit is played?
look at Lusty Joker code
I imagine it's somewhere in the ui code
ill go digging
ay thank you
table.insert(left_text,
config.saved and
{n=G.UIT.C, config={padding = 0.05, align = 'cm'}, nodes={
{n=G.UIT.R, config={align = 'cm'}, nodes={
{n=G.UIT.O, config={object = DynaText({string = {' '..localize('ph_mr_bones')..' '}, colours = {G.C.FILTER}, shadow = true, pop_in = 0, scale = 0.5*scale, silent = true})}}
}}
}
just replace localize('ph_mr_bones') with a string
although you will have to make a seperate config.x var bc
currently config.saved only ever means that you're saved by mrbones
ah right
I found loc_vars = {self.ability.extra.s_mult, localize(self.ability.extra.suit, 'suits_singular')}, but what in the world is all this meant to do?
local variables for the card desciption
Oh
The text stuff is already good to go
Now I just have to find out how to make it actually work.
So how do I make sure this only counts scored cards that are Hearts?
first name the function calculate
Done
context.other_card:is_suit('Hearts') should be the check you want
Iirc theres multiple context.individual, you'll have to add another check
So that code above's not going to work?
(also, if you know how to double the mult, pls tell me)
It will, it will just trigger too many times
So do I just rid the context.individual and problem solved?
yeah you also need the cardarea check
What does your Joker do?
For every Heart suit card scored: Double mult
The mult to the left or a specific modular mult?
The mult to the left
that doesn't display a message though
Could always create its own
I just realized the joker is just a boosted bloodstond
WELL UHHH
This patch is not working. Why? 
[[patches]]
[patches.pattern]
target = "functions/UI_definitions.lua"
pattern = '''colour = challenge_unlocked and G.C.RED or G.C.GREY'''
position = "at"
payload = '''colour = G.C.GREEN'''
match_indent = true
overwrite = true
Small issue:
- No indicator / shake when triggered
- Mult number to the left doesn't change
Just check bloodstone's code
"Oh dear Neptune..."
IT BROKE 18 MILLION (using [10, 9, 8, 7, 6] Straight Flush)
Nope, I gotta fix this
incantation, it works with most of the mods actually, and now has a "Vanilla" option, that only stacks negative consumables
found it
i dont know how to make cards have effects using decks
but i did the first part
card.ability
replace self.ability with card.ability im p sure
also you still need the cardarea check back
else it tries to trigger on other things as well, e.g. cards held in hand
Oh how Balatro modding treats me.
wait, what's Lua's AND operator?
why did you add a +
I don't know how to chain conditions in Lua
and
its and
if context.other_card:is_suit("Hearts") and context.cardarea == G.play then
that's a new one haha
I'm a baby in Lua
i don't know of any programming language that uses + for its logical and
I saw it in some code snippet when searching
works for javascript maybe
Nope, JS and Java are &&
In JS boolean + number is working fine
I don't think I need that card = self, do I?
you named your card argument cards
change it to card and do card = card
instead of card = self
Bloodstone gets a pass somehow.
because bloodstone isnt using the smods api
touche.
- is just xor
oh didn't realise jen still didn't merge my PR, so vanilla option isn't there yet
All the changes are made, and here we are.
you didn't put extra in your config
so is that gonna be a seperate file or just another variable in the code?
Not really. By using +/- as boolean operators you most likely messed up with order
SMODS.Joker {
key = 'your_key',
-- other stuff,
config = { extra = 2 },
calculate = function()...
}
look I'm not saying it's useful
I'm saying js is cursed
I know, and that's why i like it
Same error somehow
self.config.extra
i didn't actually check the code smh
you can change card.ability.extra.Xmult to just card.ability.extra in this case
but that crash shouldn't be, maybe you didn't save the file?
I've saved all the changes
can i see the full joker definition
wait why is it legendary now
is there enough contrast between beaten, unlocked and locked?
or maybe I create a locked sprite
locked should be darker imho
is bottom locked?
i think the fact that the chips are smiling at me is throwing me off
Locked should def be a sprite
locked are row 2, 4-8, row 3, 1-2 & 7-8
making locked completely dark could also work
that smiling chip is unsettling
someone pls make me a locked sprite
I'll try after lunch
ill see what i can do
this also works I guess
It does the job
thanks lol
I will plug it in after lunch
in return could you maybe help me w this??
calculate = function(self, card, context)
if context.setting_blind and not card.getting_sliced and not context.blueprint and not card.debuff and not context.control_devil then
if context.blind.boss then
G.GAME.blind.chips = G.GAME.blind.chips * (self.config.decrease / 100)
else
G.GAME.blind.chips = G.GAME.blind.chips + (G.GAME.blind.chips * (self.config.increase / 100))
end
end
G.HUD_blind:recalculate()
end,```
that doesnt seem to be recalculating the hud
im not really sure how im supposed to
i thought for sure thatd work
aha
mightve figured it out
Trying to adjust the required score?
yeah i got it
G.GAME.blind.chip_text = number_format(G.GAME.blind.chips)
G.HUD_blind:get_UIE_by_ID('HUD_blind_count').UIBox:recalculate()
oops
i think my math is off tho
yeah im stupid
would it be too broken to have it compatible with
because boss blinds pretty small
but the other blinds will be so big in comparison
i guess you could skip tho
Two slots to do that might balance it a little
thats true
What does your config look like btw?
In your joker
oh right
local control_devil = SMODS.Joker{
key = 'control',
name = 'Control Devil',
rarity = 'csm_epic',
discovered = true,
pos = {
x = 0,
y = 0
},
cost = 12,
config = {
extra = 1,
},
loc_txt = {
name = 'Control Devil',
text = {
'Trigger the effects',
'of all Jokers {C:attention}worth{}',
'{C:attention}less{} than this Joker',
},
},
atlas = 'csm_epic_jokers',
eternal_compat = true,
perishable_compat = true,
blueprint_compat = true,
set_badges = function(self, card, badges)
badges[#badges+1] = create_badge('Devil', get_badge_colour('devil'), nil, 1.2)
end,
calculate = function(self, card, context)
if card.debuff then return end
local jokers = {
message = 'Bang!',
card = card,
}
local ret = false
for _, joker in pairs(G.jokers.cards) do
if joker ~= card and joker.sell_cost < card.sell_cost then
context.control_devil = true
local joker_ret = Card.calculate_joker(joker, context)
if joker_ret then
ret = true
if joker_ret.chips then
jokers.chips = (jokers.chips or 0) + joker_ret.chips
end
if joker_ret.mult then
jokers.mult = (jokers.mult or 0) + joker_ret.mult
end
if joker_ret.x_mult then
jokers.x_mult = (jokers.x_mult or 1) * joker_ret.x_mult
end
if joker_ret.h_mult then
jokers.h_mult = (jokers.h_mult or 0) + joker_ret.h_mult
end
if joker_ret.repetitions then
jokers.repetitions = (jokers.repetitions or 0) + joker_ret.repetitions
end
if joker_ret.Xmult_mod then
jokers.Xmult_mod = (jokers.Xmult_mod or 1) * joker_ret.Xmult_mod
end
if joker_ret.mult_mod then
jokers.mult_mod = (jokers.mult_mod or 0) + joker_ret.mult_mod
end
if joker_ret.chip_mod then
jokers.chip_mod = (jokers.chip_mod or 0) + joker_ret.chip_mod
end
end
end
end
return (ret and jokers) or nil
end,
}```
I mean the reduction one
oh
mb
local fox_devil = SMODS.Joker{
key = 'fox',
name = 'Fox Devil',
rarity = 3,
discovered = true,
pos = {
x = 0,
y = 0,
},
cost = 7,
config = {
increase = 50,
decrease = 25,
},
loc_txt = {
name = 'Fox Devil',
text = {
'Small and Big Blinds',
'are {C:attention}#1#% more{}, Boss',
'Blinds are {C:attention}#2#% less{}',
},
},
atlas = 'csm_rare_jokers',
eternal_compat = true,
perishable_compat = true,
blueprint_compat = false,
loc_vars = function(self, info_queue, card)
return {vars = {card.ability.increase, card.ability.decrease}}
end,
set_badges = function(self, card, badges)
badges[#badges+1] = create_badge('Devil', get_badge_colour('devil'), nil, 1.2)
end,
calculate = function(self, card, context)
if context.setting_blind and not card.getting_sliced and not context.blueprint and not card.debuff and not context.control_devil then
if context.blind.boss then
G.GAME.blind.chips = G.GAME.blind.chips * ((100 - self.config.decrease) / 100)
else
G.GAME.blind.chips = G.GAME.blind.chips + (G.GAME.blind.chips * (self.config.increase / 100))
end
end
G.GAME.blind.chip_text = number_format(G.GAME.blind.chips)
G.HUD_blind:get_UIE_by_ID('HUD_blind_count').UIBox:recalculate()
end,
}```
Ah you fixed the math đ
yeah lol
Did someone say math?
it was a bit too broken with my terrible math
yeah im terrible at it
i think im gonna try make some custom boss blinds too which should be fun
never tried it before
local chainsaw_devil = SMODS.Joker{
key = 'chainsaw',
name = 'Chainsaw Devil',
rarity = 2,
discovered = true,
pos = {
x = 0,
y = 0,
},
cost = 5,
config = {
mult_min = 16,
mult_max = 36,
chips_min = 57,
chips_max = 73,
chips_chance = 0.6,
money_min = 1,
money_max = 3,
money_chance = 0.4,
woof_chance = 0.7,
},
loc_txt = {
name = 'Chainsaw Devil',
text = {
'Just a friendly {C:attention}Devil{}',
'trying his best to help',
},
},
atlas = 'csm_uncommon_jokers',
eternal_compat = true,
perishable_compat = true,
set_badges = function(self, card, badges)
badges[#badges+1] = create_badge('Devil', get_badge_colour('devil'), nil, 1.2)
end,
calculate = function(self, card, context)
if not card.debuff then
if context.cardarea == G.jokers and not (context.before or context.after) then
local chips = pseudorandom('pochita') < self.config.chips_chance
if chips then
local temp_chips = pseudorandom('pochita', self.config.chips_min, self.config.chips_max)
return {
message = localize{type='variable',key='a_chips',vars={temp_chips}},
chip_mod = temp_chips,
}
else
local temp_mult = pseudorandom('pochita', self.config.mult_min, self.config.mult_max)
return {
message = localize{type='variable',key='a_mult',vars={temp_mult}},
mult_mod = temp_mult,
}
end
elseif context.after then
if pseudorandom('pochita') < self.config.woof_chance then
return {
message = 'Woof!',
}
end
end
end
end,
calc_dollar_bonus = function(self, card)
if pseudorandom('pochita') < self.config.money_chance then
return pseudorandom('pochita', self.config.money_min, self.config.money_max)
end
end,
}```
is there a way to pass a mult_mod and chip_mult into the joker calc return thingy?
id like him to give a random amount of mult and chips
uh
You can pass both but it wonât do the messages properly
well first
id rather he just do one and have the correct message then ig
if not card.debuff is unnecessary
yeah steamodded checks it for you
awesome
You can throw your own messages though
i never had a chance to test it w debuffed jokers
oh yeah
and if context.cardarea == G.jokers and not (context.before or context.after) then can just be if context.joker_main then
for two separate messages, i guess you can use SMODS.eval_this, or use your own message for both
i can just called the eval text directly right?
i think ill just do that then
i didnt know about smods.eval_this
just a wrapper for card_eval_status_text
oops
That seems wrong
am i able to pass Xmult_mod and mult_mod?
I think insert_pool becomes inefficient somehow?
Badges for blinds when? 
20k is taking 2-5 seconds it seems
bottleneck seems to be recalculating table length
t[#t+1] = vs. table.insert sure doesn't seem to make a difference
i might be biased but i think it looks pretty good
i like it
yeah I think it looks great
it works
Looks good and works
No, but cool
thats hella cool
i love how the stake chips like stack
when im putting colours into joker descriptions
by doing {C:colour}
idk how to not make that long sorry
can i put in any colour?
or is there somewhere i can find all the available colours?
The colours are in globals.lua but
the colours for {C:colour} are in the loc_colour function in functions/misc_functions.lua
thank you
Corollary: you can hook loc_colour to define your own
I did it like this
local loc_colour_ref = loc_colour
function loc_colour(_c, default)
if not G.ARGS.LOC_COLOURS then
loc_colour_ref(_c, default)
elseif not G.ARGS.LOC_COLOURS.vic_colours then
G.ARGS.LOC_COLOURS.vic_colours = true
local new_colors = {
vic_HighCard = G.C.VictinsCollection.POKER_HANDS['High Card'],
vic_Pair = G.C.VictinsCollection.POKER_HANDS['Pair'],
vic_TwoPair = G.C.VictinsCollection.POKER_HANDS['Two Pair'],
vic_3OAK = G.C.VictinsCollection.POKER_HANDS['Three of a Kind'],
vic_Straight = G.C.VictinsCollection.POKER_HANDS['Straight'],
vic_Flush = G.C.VictinsCollection.POKER_HANDS['Flush'],
vic_FullHouse = G.C.VictinsCollection.POKER_HANDS['Full House'],
vic_4OAK = G.C.VictinsCollection.POKER_HANDS['Four of a Kind'],
vic_StraightFlush = G.C.VictinsCollection.POKER_HANDS['Straight Flush'],
vic_5OAK = G.C.VictinsCollection.POKER_HANDS['Five of a Kind'],
vic_FlushHouse = G.C.VictinsCollection.POKER_HANDS['Flush House'],
vic_5OAFlush = G.C.VictinsCollection.POKER_HANDS['Five of a Flush'],
vic_stone = G.C.VictinsCollection.ENHANCEMENTS.Stone,
}
for k, v in pairs(new_colors) do
G.ARGS.LOC_COLOURS[k] = v
end
end
return loc_colour_ref(_c, default)
end
I mean that's what I do, but I did it inside a hook
excep for the animated colours which are in Game:update
cheers
how's this for verbose logging
im not sure what that word means
what's the point of logging atlas is injected if atlas name is not specified
at that point you might just send an end result
yeah this is for benchmarking
it'll only do it on trace level and if it's specificed
just the string formatting from all the individual logs slow everything down when there's lots of items
normally it'll just show the end result
rn we have nothing that's really slow to inject, if something like that gets added in the future we might want it enabled by default so ppl using it don't think the game froze
Coffee joker, it has the jitters
Scalene jonkler has a noticeable diagonal seam in the middle
the freaky juice_up
the latest version looks more like the one on the gif, still not final tho I want to add some effect like darken the image and change the edges somehow
God pls don't break all mods in new update 
only 50%
it only needs to break one mod for disaster to strike
imagine if thunk got rid of the calculation tower tho
The game will be saved from all the lovely injections đ„
đ„đđ„
that's a normal amount of patches
thunk adds functions so people donât have to access variables directly
aggressive scoping? guh
Wait yea I can just pull from Dim's modpack
247 patches Georg is an outlier and shouldnât have been counted
imagine if thunk made the game accessible and I made black hole for nothing
why would he do that
he could be working on updates/fixes but instead he'll be spending time to make code look cleaner đ
all that gets added is one comment that says stop posting about my ifelse tower
This is making me wonder if Thunk just adds a bunch of comments just to troll Steamodded injections.

thunk will obfuscate and inline all the code so it's not an ifelse tower but an endless ifelse line
Huh I didnât know about that mod
Based aure
that's the ultimate way to break lovely patches
Thunk said they wanted to support modding đ€
Thunk puts all of the gameâs code in a single file thatâs one line long
I think when that happens, he will probably go to modding community
Go where
here
Do what
well I'd assume communicate with modders about modding support, lol
how else would he know what modding support is needed
why wont this work raaghhh
of course he could completely ignore current modding, add his own API and whatever, but then you'll have another community split up
between steamodded and "official" modding
hell nah we have jokers with physics before gta 6...
are you doing blueprint for two jokers or something
I think with current state of modding, there isn't that much modding support needed anyways, maybe native lovely patching so antivirus doesnt cry
thunk goes to a fortune teller draws some tarots
sounds about right 
no way is he gonna patch arbitrary code execution? 
blueprints for unexistant jokers...
and brick all the existing save files 
99% chance Cryptid breaks in some way
With how many lovely patches there are
That are pattern patches, too
imagine calculation API gets completely refactored so localthunk can retrigger jokers
I'm more worried about Talisman than Cryptid tbh.
Most of those overrides are pretty internal, I donât think itâll suffer as much damage
would be dope but lovely is community-developed and not complete đ
Plus ideally we can figure out how to replace the Lua DLL to remove the weird lua metatable limitations
I know someone was working on that but theyâre stuck trying to modify assembly to get __eq to work
have your own lovely download that also bundles the updated lua dll
idk if we would really want a thunk version of lovely
or is the problem making the dll itself
Yeah
Marr-Ales-Fios was working on fixing the LuaJIT source code but the problematic part is in assembly 
calculate = function(self, card, context)
if not (context.blueprint or context.control_devil) then
if context.cards_destroyed then
local hearts = 0
for k, v in ipairs(context.glass_shattered) do
if v:is_suit('Hearts') then
hearts = hearts + 1
end
end
if hearts > 0 then
G.E_MANAGER:add_event(Event({
func = function()
G.E_MANAGER:add_event(Event({
func = function()
card.ability.x_mult = card.ability.x_mult + hearts*card.ability.extra
return true
end
}))
card_eval_status_text(card, 'extra', nil, nil, nil, {message = localize{type = 'variable', key = 'a_xmult', vars = {card.ability.x_mult + hearts*card.ability.extra}}})
return true
end
}))
end
return
elseif context.remove_playing_cards then
local hearts = 0
for k, v in ipairs(context.removed) do
if v:is_suit('Hearts') then
hearts = hearts + 1
end
end
if hearts > 0 then
card.ability.x_mult = card.ability.x_mult + hearts*card.ability.extra
G.E_MANAGER:add_event(Event({
func = function()
card_eval_status_text(card, 'extra', nil, nil, nil, {message = localize{type = 'variable', key = 'a_xmult', vars = {card.ability.x_mult}}})
return true
end
}))
end
return
elseif context.using_consumeable then
if context.consumeable.ability.name == 'The Hanged Man' then
local hearts = 0
for k, v in ipairs(G.hand.highlighted) do
if v:is_suit('Hearts') then
hearts = hearts + 1
end
end
if hearts > 0 then
card.ability.x_mult = card.ability.x_mult + card.ability.extra*hearts
G.E_MANAGER:add_event(Event({
func = function()
card_eval_status_text(card, 'extra', nil, nil, nil, {message = localize{type='variable',key='a_xmult',vars={card.ability.x_mult}}})
return true
end
}))
end
return
end
end
else
if context.joker_main then
return {
message = localize{type='variable',key='a_xmult',vars={card.ability.x_mult}},
Xmult_mod = card.ability.x_mult
}
end
end
end,```
so i have this calculate function for a joker
when you destroy a heart card it increases the x mult by 0.25
for some reason its increasing twice for each card destroyed
not sure why and would appreciate any help
just fix the asm then
using hanged man also calls context.remove_playing_cards
easier said than done
thank you
oh
just trolling, ik
not thank you then
err idk
nws
its probably a stupid mistake on my part that ill figure out tomorrow or smth and be like how did i even do that
removing the hung man check seems to have worked
so i think you were right
power now works
Also you got me curious about what the Control Devil context is for đ€
just so it doesnt scale twice as fast
or the control devil joker doesnt gain x mult
Trigger when?
whenever the joker triggers
Also, if it just doubles triggers, then it should scale twice as fast
But it doesnât say when it triggers
i dont really want it to tho its kinda op already
Every vanilla Joker will scale twice as fast
its whenever a joker triggers, the control devil joker triggers too and does the same thing
thats a problem lol
So it should trigger that Joker twice
at least i think
Donât create arbitrary exceptions
ill show the code hold on
calculate = function(self, card, context)
local jokers = {
message = 'Bang!',
card = card,
}
local ret = false
for _, joker in pairs(G.jokers.cards) do
if joker ~= card and joker.sell_cost < card.sell_cost then
context.control_devil = true
local joker_ret = Card.calculate_joker(joker, context)
if joker_ret then
ret = true
if joker_ret.chips then
jokers.chips = (jokers.chips or 0) + joker_ret.chips
end
if joker_ret.mult then
jokers.mult = (jokers.mult or 0) + joker_ret.mult
end
if joker_ret.x_mult then
jokers.x_mult = (jokers.x_mult or 1) * joker_ret.x_mult
end
if joker_ret.h_mult then
jokers.h_mult = (jokers.h_mult or 0) + joker_ret.h_mult
end
if joker_ret.repetitions then
jokers.repetitions = (jokers.repetitions or 0) + joker_ret.repetitions
end
if joker_ret.dollars then
jokers.dollars = (jokers.dollars or 0) + joker_ret.dollars
end
if joker_ret.Xmult_mod then
jokers.Xmult_mod = (jokers.Xmult_mod or 1) * joker_ret.Xmult_mod
end
if joker_ret.mult_mod then
jokers.mult_mod = (jokers.mult_mod or 0) + joker_ret.mult_mod
end
if joker_ret.chip_mod then
jokers.chip_mod = (jokers.chip_mod or 0) + joker_ret.chip_mod
end
end
end
end
return (ret and jokers) or nil
end,```
no this is what ive done
yeah man heres your options
ffs
wh
?
??
????
Actually, hereâs a suggestion
Instead of a custom context, use Blueprintâs
And make the original Joker being copied use Blueprintâs context
That way it doesnât scale anything twice
Because vanilla Jokers already check for Blueprint before scaling
its not scaling anything twice
Doesnât it?
Anyways,
I think if you use Blueprint as a reference thatâs better because the compatibility isnât arbitrary. Jokers donât scale twice as fast because of Blueprint rules
dont think so
ben?
yes
I wonder why. Does Wee Joker trigger twice?
didn't cryptid have to do a lot of modifications to calculate_joker to have joker retriggers working
Skill issue
yeah i looked through cryptid and it gave me a headache
my way round it is a bit sketchy
See, Iâm suggesting you keep it that way, but use context.blueprint. But if it already works, then Iâm trying to understand how
Also
Ben
@atomic falcon can you not interrupt us
To avoid issues with multiple Control Devils
@crisp coral
thats smth i gotta test actually
they way im doing it atm is basically just collecting all the effects from jokers whenver calculate joker is called, and then having my joker do all of those effects
it only triggers the effects of jokers worth less so there shouldnt be any issues with multiple
I mean two of them could have different prices
yeah but its not gonna break
i dont think context.blueprint gotta be used here
its not gonna get super out of hand like infinite loop
Iâm not saying it has to, but it could
yeah it won't infloop
I think itâs clever
yeah thats the main thing
ty
I was talking about my own suggestion
It could loop infinitely if a repeat effect increased the cost of a different Control Devil
Like maybe if you ordered Control Devils and Gift Cards correctly
true fact^
đč
it wont trigger gift card effects
wouldnt you have to gift card before you buy the special joker
i don't think that'd infinitely loop but if need be just add a check
or special joker would increase in value at the same rate
i did some testing with gift card
had 4 copies with different values
it scores loads but its not game breaking
4 copies of what
the control devil joker
I think something like Control, Gift, Gift, Control, Gift, Gift could infinite loop
^
I was confused by what you said since you tested it
@mellow sable by the way, what did you do to get Joker retriggers working? I donât want to require Cryptid for just a single feature
Oops did I ping? :<
deserved
only thing that could cause an infinite loop i think is something like canvas from cryptid
didn't ping
but thats funny anyway
lmao canvas moment
Override every calculation call to loop for retriggers
Itâs a lot and I want to refactor it and add to Steamodded at some point
peak
i looked through cryptid joker retriggering and i couldnt make sense of any of it lol
that sounds so fragile lmao
My ideas for the refactor were basically:
- You can pass a callback function to calculate_joker to do things with the result, instead of hardcoding those things it has to do
- You can return a second value to indicate the joker did a calculation without returning anything
tell me why i cant just do
for k, v in pairs(deez) do
return v.nuts
end```
why isnt it lua
lol
wouldnt that be nuts[v]
deez[k] = v
no way am I actually doing this?
the names are irrelevant, you should be able to return things like that
generator
i will literally kiss you
Canât you hook to do a callback?
you want python geenrtor?
wdym
in lau?!
can you give an example
yeb
L[ua]ocalthunk
logalthunk
lau
https://www.lua.org/pil/9.3.html
``` This is what you wnat @nkojeo
is?
Ys
a
Start yielding
stress
Unless you want to affect the middle of calculation, canât you hook calculate then add something to the end?
what is calc short for
hes new to chat
loc alt hunk
Chat does he know
better calc sitting abandoned in the corner
clac?
trust the process
Itâs fine I just donât understand. Maybe Iâm missing something
neither do I 
Coding is all about throwing shit at the wall and seeing what sticks. Whatever does stick then you gotta study it for Scienceâąïž
i have to see how I got this to work
Also if you add a new context, please add a key to it
smods_retrigger idk
btw steamodded got another bit of loading perf
if i give you a tenner will you code this for me?
doesn't matter much for the amount of objects we're dealing with, but I got it to the point where I can load more centers within a second than Luajit's memory restriction will let me
If nekojoe gives you a tenner can you steal it and work for me instead
(the issue was the string format calls for trace messages that weren't even firing)
lau
For jker in the hand {
the_amount_of_money_worth = jokr.get_value()
if the_amount_of_money_worth > controldevil.price then uhh
joke.retigger()
else
end.it_all
mods please pin
if you just change it to "retrigger all jokers" this will be easy with retrigger api
I think itâs more interesting if itâs cheaper Jokers
Retrigger all jokers and check if triggered joker has a sell value <= condevil
Actually would it just be less than or less than or equal
my bad ill fixc thart
Make it so retriggers can be conditional per Joker please
gomtry your code is wrong
I think thatâs more applicable
lau
For each and every fucking jker in the hand {
the_amount_of_money_worth = jokr.get_value()
if the_amount_of_money_worth < controldevil.price then uhh
joke.retigger()
else
end.it_all
Also thatâs how card retriggers work
has to be < than
otherwise infinite loops
the main calculate loop is probably going to be the same as I have it, which checks per joker
i have code for this joker already
local cj = Card.calculate_joker
function Card:calculate_joker(context, callback)
local ret, triggered = cj(self, context)
--Make every Joker return a value when triggered
--Check for retrggering jokers
if (ret or triggered) and not context.retrigger_joker and not context.retrigger_joker_check then
if type(ret) ~= 'table' then ret = {joker_repetitions = {0}} end
ret.joker_repetitions = {0}
for i = 1, #G.jokers.cards do
local check = G.jokers.cards[i]:calculate_joker{retrigger_joker_check = true, other_card = self}
if type(check) == 'table' then
ret.joker_repetitions[i] = check and check.repetitions and check or 0
else
ret.joker_repetitions[i] = 0
end
if G.jokers.cards[i] == self and self.edition and self.edition.retriggers then
local old_repetitions = ret.joker_repetitions[i] ~= 0 and ret.joker_repetitions[i].repetitions or 0
--[[local check = calculate_blurred(self)
if check and check.repetitions then
check.repetitions = check.repetitions + old_repetitions
ret.joker_repetitions[i] = check
end--]] --figure out something for this later
end
end
end
if callback and type(callback) == 'function' then callback(ret) end
return ret
end
this is what I have right now
can i have that?
wtf is that
it'l be merged with steamodded
are you single?
it's how you break the game
đ€š
line 9 has syntactical eror
Wait is that Nepeta
calculate = function(self, card, context)
local jokers = {
message = 'Bang!',
card = card,
}
local ret = false
for _, joker in pairs(G.jokers.cards) do
if joker ~= card and joker.sell_cost < card.sell_cost then
context.control_devil = true
local joker_ret = Card.calculate_joker(joker, context)
if joker_ret then
ret = true
if joker_ret.chips then
jokers.chips = (jokers.chips or 0) + joker_ret.chips
end
if joker_ret.mult then
jokers.mult = (jokers.mult or 0) + joker_ret.mult
end
if joker_ret.x_mult then
jokers.x_mult = (jokers.x_mult or 1) * joker_ret.x_mult
end
if joker_ret.h_mult then
jokers.h_mult = (jokers.h_mult or 0) + joker_ret.h_mult
end
if joker_ret.repetitions then
jokers.repetitions = (jokers.repetitions or 0) + joker_ret.repetitions
end
if joker_ret.dollars then
jokers.dollars = (jokers.dollars or 0) + joker_ret.dollars
end
if joker_ret.Xmult_mod then
jokers.Xmult_mod = (jokers.Xmult_mod or 1) * joker_ret.Xmult_mod
end
if joker_ret.mult_mod then
jokers.mult_mod = (jokers.mult_mod or 0) + joker_ret.mult_mod
end
if joker_ret.chip_mod then
jokers.chip_mod = (jokers.chip_mod or 0) + joker_ret.chip_mod
end
end
end
end
return (ret and jokers) or nil
end,```
you must use the for each and every fucking keywords
Mobile formatting makes this hard to read. So you can set the number of repetitions per Joker?
this is my shoddy attempt at making the joker appear as if it kinda retriggers other jokerts lol
somone @ thunk my code is genus
you mean like, 1 repetition from Joker A, 2 from Joker B, etc.
yes
No, I mean like Joker A repeats 1 time, Joker B repeats 2 times, etc.
Babe wake up new joker troggers just dropped
maybe try implement smth like this?
Elaborate
now here's an example of how bad the old API is
its like a big building, but each floor is a mushroom
# ending shop
[[patches]]
[patches.pattern]
target = "functions/button_callbacks.lua"
pattern = "G.jokers.cards[i]:calculate_joker({ending_shop = true})"
position = "at"
payload = '''
local effects = G.jokers.cards[i]:calculate_joker({ending_shop = true})
if effects and effects.joker_repetitions then
rep_list = effects.joker_repetitions
for z=1, #rep_list do
if type(rep_list[z]) == 'table' and rep_list[z].repetitions then
for r=1, rep_list[z].repetitions do
card_eval_status_text(rep_list[z].card, 'jokers', nil, nil, nil, rep_list[z])
if percent then percent = percent+percent_delta end
G.jokers.cards[i]:calculate_joker({ending_shop = true, retrigger_joker = true})
end
end
end
end
'''
match_indent = true
i remember this
i read through all of it earlier
i think it caused permanent damage to my brain, its too small
usually im pretty good at stealing other peoples code but this was unfathomable
So this gets the repetitions of the jokers when ending shop? Or are we brainle5
Simple just do context ~= nil :^)
ok so I think this is good, now I have to refactor everything to use callbacks
function Card:calculate_joker(context, callback)
local ret, triggered = cj(self, context)
--Check for retrggering jokers
if (ret or triggered) and context and not context.retrigger_joker and not context.retrigger_joker_check then
if type(ret) ~= 'table' then ret = {joker_repetitions = {0}} end
ret.joker_repetitions = {0}
for i = 1, #G.jokers.cards do
local check = G.jokers.cards[i]:calculate_joker{retrigger_joker_check = true, other_card = self}
if type(check) == 'table' then
ret.joker_repetitions[i] = check and check.repetitions and check or 0
else
ret.joker_repetitions[i] = 0
end
if G.jokers.cards[i] == self and self.edition and self.edition.retriggers then
local old_repetitions = ret.joker_repetitions[i] ~= 0 and ret.joker_repetitions[i].repetitions or 0
local check = self:calculate_joker_retriggers()
if check and check.repetitions then
check.repetitions = check.repetitions + old_repetitions
ret.joker_repetitions[i] = check
end
end
end
--do the retriggers
context.retrigger_joker = true
for z = 1, #ret.joker_repetitions do
if type(ret.joker_repetitions[z]) == 'table' and ret.joker_repetitions[z].repetitions then
for r = 1, ret.joker_repetitions[z].repetitions do
card_eval_status_text(rep_list[z].card, 'jokers', nil, nil, nil, rep_list[z])
if percent then percent = percent+percent_delta end
self:calculate_joker(context, callback)
end
end
end
end
if callback and type(callback) == 'function' then callback(ret) end
return ret
end
Could this be used (or modified) to improve blueprint behaviour? (like multicard blueprint)
yeah probably
you could probably call calculate_joker directly
except the UI might be janky
To calculate the periodic sequence present in your profile picture, the following equation would be used:
a(n) = 7+5ânMOD3/2â+10(ânMOD3â/1.5â â ânMOD3â/2â)
Yea that's the issue, the eval status would be wrong
ben
hey guys do u know how to figure out joker modding because i have been struggling for 3 days to create jokers so
yeah
Use Steamodded 1.0 and try to mod a single Joker in
I had a single Joker mod for 0.9.8
Here
GitHub
I cant find it on github I only see 0.9.8
Green button
Download
1.0 isnât released yet
o h
@hushed cradle take a look at this https://github.com/MathIsFun0/Cryptid/tree/better-retrigger
i only patched joker_main to use the new callback system so far but it should work decently
also separated to its own file so it can be easily merged with Steamodded when Iâm done
you are incredible
heaven sent
đ
im not by any means good at programming
but if you would ever like help with anything id be happy to oblige
im helping out w saturn so that proves im not completely stupid
i doubt you would ever need proper help but if you gotta do smth thats long and tedious then just drop me a dm
tysm
if I need to work on overhauling some UI I might have to hit you up
Because to me that is still mostly a mystery
UIAPI
im gonna be honest i dont fully understand it either, but i somehow manage to make it work lol
actually i say that but i understand it a decent bit i think
feel free
if BalaUI adds a visual UI editor we would all be set
The modding API we need but not the one we want
Also what is that
I have no idea how a UI editor would work
does anyone else have a bug where wheel says 11 out of 7 cards are flipped?
or 22 with oops
what am i supposed to be looking at??
What did you just ask
Itâs like the last message
oh right it took me way far back in the chat
is that just a known bug then?
I thought it had been fixed
i dont know what im doing lol
Look at how something like canvas or boredom is implemented
i am trying
calculate = function(self, card, context)
if context.other_joker then--context.retrigger_joker_check and not context.retrigger_joker then
print('deez')
print(tostring(card.sell_cost))
print(tostring(context.other_joker.ability.name))
if card ~= context.other_joker and card.sell_cost > context.other_joker.sell_cost then
return {
message = localize('k_again_ex'),
repetitions = card.config.extra,
card = context.other_joker
}
end
end
end,```
so
jokers arent my strong point
What are you trying to do
i should probably read more thoroughly
idek at this point
oh card should be card
is there a way for sendDebugMessage etc. to auto populate the second variable with the mod prefix?
how does Cryptid's Misprint and Edition decks affect all cards?
I hook all of the functions that create cards
if you search for cry_misprintize in the cryptid code you can see where this is being done
the biggest use is in create_card but there are more
Blind idea:
The Octopus
You have 8 hands
and it has X8 Base size
That seems⊠too hard đ€
The Hand:
X1 Base Blind requirement
+3 hands, +1X Base Blind requirement per total number of hands
(applies after jokers)
Burglar spam vs this for mega violet vessel any day of the week
Eremel keeps winning
slowly making the game's UI functional step by step
fr
oh I was thinking too, should the tab colours in mod settings be the same as the badge colour?
hm that's a neat detail
I first thought the entire button to open that menu, but that might be a little garish
but the tabs would look neat
regex is so powerful when you know how to use it
i'm not even close to using its full potential
^
all I'm doing is basically multiline pattern patches with it
there are some patches I look at and am just mind boggled as to how they work
and then one or two I have that just check for a variable number of spaces because of janky
syntax
i still have so much left to port
but this went down from 269 lines to 105
and a lot of the later ones should need no patching with how I set the API up
btw as effective as regex patches are, they're also inefficient
things like this don't need any modifications
how so
basically the retrigger logic is done within the calculate_joker function fully now
before calculate_joker would only run an extra check to count how many times the joker should be retriggered
but because the callback function tells it what else to do with those retriggers, it now also calculates the retriggers within calculate_joker
Ooh
anyone aware of a way to add an event to E_MANAGER that triggers on the next round?
I made a tarot that modifies hand count, it's only issue is if it's used during a shop or blind selection the hand count resets when a blind is selected
was looking into creating an invisible tag, but couldn't find a way to make that work either
the way codex does things is it adds a flag and reads that out on round end
why don't you set a variable in the Joker and give it a start of round trigger
Oh wait you said Tarot
I would but its a tarot
There's a Tag with a similar effect
i guess an event might work, but the event system is super jank
juggle tag might be a good reference?
yeah, i was trying to make something similar to the +3 hand tag but it would have to be invisible
the way I have it working rn is it calls ease_discard()
more synergy
which works great, but discard and hand counts reset at the beginning of a round
was really just looking to see if anyone had another idea, but yeah ill look more into the tag
ngl im feeling way too lazy to do that rn lmao
I think something like this might work? not sure though since events are scuffed
local rounds = G.GAME.rounds
G.E_MANAGER:add_event(Event({
blocking = false,
func = function()
if --[[condition]] G.GAME.rounds > rounds then
your_code()
return true
end
-- if the condition isn't met, this is run again next frame
-- that's inefficient and probably unwanted since a function exists
-- that fires when the round ends, but it can work
end
})
I mean you could add a new global variable but I think I like the tag more
if this doesn't work you could wrap the event in a function and have an else that calls the function and return true
this could work, i haven't tried checking for the condition within func
ill try implementing it and check back
crazy syntax
why did you even need that
was it meant to match multiple instances with different spacing?
future-proofing
because the spacing is bad
with that, it's my last lovely patch for retrigger api
but i have a question
i may have an answer
some vanilla jokers aren't going to play nicely, should I solve that by taking ownership or with lovely patches
i think either is better than what I had before
depends on how not nicely they're playing
i'd need like 20-30 patches that are basically return nil, true
yeah definitely use lovely patches for that
could potentially even save space by making them the same patch 
oh powerful crashomancer, go and break galdur all night whilst I sleep
oh i shall sleep as well, it's past 2am over here
but you're so efficient at finding crashes
i will stay up coding this api
(it's well before 2am here don't worry)
this will be fun
Caino my beloved
it's still called that internally đ€·ââïž
WHAT THE HELL DID I JUST DO
oh I checked too little in my patch
Baltro
When does set_ability get called? This code doesn't work and mult is always 0 (the default value I set)
Ok, I guess my question is how can I make the card default to being triple the card's sell value when it's first spawned? I can't access that value in the extra section I don't believe
set cost is called after set ability
Ok, so that won't work. Unless I somehow hook into set_cost for this joker only
If I wanted to have an option cycle in mod config, is the opt_callback required in order to save a change, or am I missing something?
finally got that discard function working
here's how I ended up doing it if you're interested
props man
anything to do with regex is miserable by nature lmao
hold on, the hell is that return statement đ
i learned it today... for this
so many patches in card.lua (I added like 30 new ones)
it's finally done
once i finish this modpack im down to work on the loader if you need the help
now all I have to do is test it out
nice
i don't rly need help rn
I'm refactoring retrigger API so I can move it from Cryptid to Steamodded soon
aight bet
anyway I'm just gonna spawn canvas and see what happens
lol gl đ€
is there documentation on how to make a mod or how do i start?
tysm
Also going to throw out this somewhat WIP example Joker mod.
https://github.com/nicholassam6425/balatro-mods/wiki/Contexts I've found this page very helpful as well if you end up dealing with contexts
where do i find this pages? cuz i kept looking on google and couldnt find much
which pages?
like the getting started one, or this one u just send
most of the useful docs I've found have been through this server
just search for your topic in the 'modding-dev' channel and someone's probably talked about it
ok then, tysm!
Going to recommend this version for both of you as well. #1247703015222149120 message
oh sweet, thx
idea: baltro as a mod that removes all aces and ace-related jokers
scholar is dead
superposition is dead
How would I use the ante_scaling modifier used for the plasma deck as a modifier in a challenge? I know that it's limited to 1, 2, and 3, but that's fine for my purposes
1,2,3 isnât the plasma one
Ah okay, I was going off a note in the code for 5 Legendary Challenges
Either way, my question still stands - how do I change ante scaling in a challenge, preferably without custom coding it
Bumping this question up.
Basically just allows you to call a function that's located in G.FUNCS.
Here's the current code I have for the option cycle,
create_option_cycle{colour = G.C.SO_2.Diamonds, scale = 1, w = 0, shadow = true, options = {'Test 1', 'Test 2', 'Test 3'}, current_option = config.Option_Cycle, ref_table = config, ref_value = 'Option_Cycle'}
Main thing is just that it'd be a hassle to make a custom G.FUNCS function for every single one, but, changing this as is doesn't seem to save any information.
This is probably a really dumb question, but I'm just starting out here. In what folder do you have to put a mod in order for it to... work?
I see the example mods in the example_mods folder, but where do I put them to actually run them?
Once you've installed your mod loader of choice, you put lua files (or mod folders) in %Appdata%/Balatro/Mods (you may need to make the mods folder)
I'm pretty sure you do, yeah
is there a seleton code for adding a joker with steamodded 1.0.0?
So glad I read upwards to find this, time to do some studying! (Thanks for making this!)
Do you know that every sprite in Balatro has an invisible yet COLORED border, and that's why every sprite has a transparent 1-pixel border around them?
I don't think it is the (main) reason for 1 pixel border tbh, border makes more sense for shaders
The reason is probably the shader that wobbles the cards, since when the pixel gap is absent the sprite of a card starts to peek throgh the closest 4 sprites in the canvas
Besides being a common practive in spritesheets lol
not true actually
cards wobbling is a vertex shader
which allows you to go far beyond 1 pixel gap
Could almost make it a paper plane
fuck no
No it is. See that tiny line on the border of an orange and purple card? That's the overlap I mentioned. Though, sure, it isn't caused by the shader, it enhances its size nonetheless. That's more like a sprite cropping issue though
it'd probably bleed due to rounding errors etc.
I see a stray pixel đ
not a stray pixel but rather a misplaced one*
I see a half of a spade broken
yup, that's what I was reffering to
So, gotta fix that
you're talking about smoothing
On the topic of that, what programs do you use for your sprites? Aseprite annoys me greatly
which as far as I understand is actually a shader 
but the cards floating effect does not require a pixel border, you're just confusing different things
I do
well, not exactly a shader, but a scaling algorithm, and it is a native love2d thing
local ret = context.other_joker:calculate_joker(context)
print(tostring(ret))```
this is printing nil even tho context.other_joker is def a thing
calculate = function(self, card, context)
if context.other_joker then
if context.other_joker ~= card and context.other_joker.sell_cost < card.sell_cost then--context.retrigger_joker_check and not context.retrigger_joker and context.other_card ~= self then\
local ret = context.other_joker:calculate_joker(context)
print(tostring(ret))
if ret then
ret.message = localize('k_again_ex')
ret.card = other_joker
end
return ret
end
end
end,```
ignore the other joker
its because i removed some code that didnt work
im just confused why its returning nil when calculating the context of the other joker
oh
im so stupid
i swear i always have these questions and the second i ask about them i realise how dumb i am
I don't like UI.
UI is life đ»
Graphic design is my passion
now everyone must suffer... like i have...
calculate = function(self, card, context)
if context.retrigger_joker_check and not context.retrigger_joker then
if context.other_card.sell_cost < card.sell_cost then
return {
message = localize('k_again_ex'),
repetitions = self.config.extra,
card = card
}
else
return nil, true
end
end
end,```
am i doing this wrong?
also ui is great
sonic heroes
triple baka
You shouldnât be returning anything in the else statement because the check doesnât need to be retriggered
Seriously, though, I just want checkboxes on the left, lablels on the right, under that, cycles with text elements that can show tooltips on highlight. This is the code for this mildly updated blue section, and I have no idea where I'm going wrong.