#💻・modding-dev
1 messages · Page 592 of 1
how bad is this
No, you could check if a message was called during the seal calculation or if it returned anything.
If I wanted to give money when a joker triggers would "dollars = card.ability.extra.dollars" be correct? It currently doesn't seem to be giving money when it triggers
Yes, if you're returning it.
Would this not be "dollars = dollars + card.ability.extra.dollars"? Otherwise to me this reads as setting your dollar count to however much your joker is trying to add
Sorry I should have specified, this is for returning the value not setting it to be a new value
Then that should be chill, yeah
idk it isn't working for me annoyingly
config = { extra = {mult = 10, chips = 30, dollars = 3, type = 'Three of a Kind' } },
loc_vars = function(self, info_queue, card)
return { vars = { card.ability.extra.mult, card.ability.extra.chips, card.ability.extra.dollars, localize(card.ability.extra.type, 'poker_hands') } }
end,
calculate = function(self, card, context)
if context.joker_main and next(context.poker_hands[card.ability.extra.type]) then
card:juice_up(1,0.5)
return {
mult = card.ability.extra.mult,
chips = card.ability.extra.chips,
dollars = card.ability.extra.dollars
}
end
Not sure where the issue for the money part is
the mult and chips trigger fine though
Hi people, seeing that you all know a lot about modding can someone tell me what everything means and how to begin?
STEAMODDED 1.0.0
A tutorial on how to make a modded Joker.
https://github.com/art-muncher/Example-Mod -- EXAMPLE MOD
https://github.com/Steamopollys/Steamodded -- STEAMODDED
https://github.com/WilsontheWolf/DebugPlus -- DEBUGPLUS
-----------------------------------------------------...
I recommend this a neat starting off point
Theres another more recent video they made but they go over different things
Thanks, i will look into this with that video
Also if somebody could look and tell me how I am foolish I'd appreciate it greatly
<@&1133519078540185692>
Hey, is there a way to check the cards in a played hand in context.before? I want to sync up the sound it plays and the message it displays, but the way I have it at the moment - checking for aces as it scores them - the sound plays instantly because the game already found them in an instant, and then the message displays after the hand is scored, whereas I would like to have both play together before the hand is scored. Naturally context.scoring_hand doesn't seem to work because the hand has not been "played" yet
add sound = <sound key> to the return
Does this act differently to play_sound?
no
Then I already have, I'll screen record the current outcome
What program do you guys use for the coding?
visual studio code
arl thanks
Notepad 
any reason why context.using_consumeable fails if the card ur checking is the only card that is being used?
What do you mean?
Why is context.money_altered printing an inconsistent amounts of times, 1st item $5 and prints once, 2nd item $4 and prints twice, 3rd item $5 and prints five times.
calculate = function(self, card, context)
if context.using_consumeable and context.consumeable.ability.set == "Tarot" then
print("hi")
end
end
it works when i have 2 cards but not when i have just 1
Is this in the stickers code?
card.ability.extra_value = card.ability.extra_value * 2
card:set_cost()
why this not increase sell value
card.ability.extra_value starts at 0
ok thx
yea, do i have to do it somewhere else?
Yes, because the consumable is removed from the consumable area before the context is calculated.
card.sell_cost
ok thx
My bad, this took way to long to reset up, gonna start making backup files
Does the video not work for anyone else
it works
Just me then, that's fine
Anyways, right now the sound plays immediately as the hand is scored, whereas the message only drops after the 2nd scored Ace. Is there a way to have the message play right before the hand starts scoring like the sound does?
Vid started working as I was typing smh
Think I found something, one sec
how to check if wheel of fortune fails/succeed?
Gonna try this
card_eval_status_text(self, 'extra', nil, nil, nil, {
message = "CHAT IS THIS REAL?", colour = G.C.MULT, instant = true})
play_sound('kain_LeBronScream')
SMODS.calculate_effect({message = 'aeiou', sound = 'modkey_name'}, card)?
A joker in my mod is supposed to turn a random card in hand into a spade at the end of a round, but it's not
anything to fix?
hey gang, i have a joker that's not loading, any clue why?
this one
other jokers in the same place are working fine, this one just wont load properly
Hmm, have you put it in an altas?
yeah this is my main.lua
it goes through and loads each file in the objects/jokers folder
then idk
Code?
youre setting load_joker to the return value of SMODS.load_file(path) currently
i dont imagine thats what you want to do
Out in spades, no one can hear you win
if context.end_of_round and context.main_eval then
local _card = pseudorandom_element(G.hand.cards, 'seed')
SMODS.change_base(_card, 'Spades')
end
yeah it is, like i said its working for the other joker in my objects/jokers folder but not this one
or wait
thank you
yeah it is, right? so i take the first file_names_jokers value, plug it into path, plug that into load_joker which loads it, then go to the next one and do the same thing
let me try swapping the order and see what that does
ok yeah its just the dudehole joker thats not loading
the order doesnt matter
Gave this a try and I'm running into the same issue I did before, but I forgot how to solve it 😅 "resources/sounds/kain_LeBronScream.ogg. Does not exist."
SMODS.Sound{key = "LeBronScream", path = "LeBronScream.ogg"}
...
SMODS.calculate_effect({message = 'CHAT IS THIS REAL?', sound = 'kain_LebronScream'}, card)
I believe this is correct, my prefix is 'kain'
i immediately need to know what this joker does lmfao this is hilarious
I can now get a demo, 1 sec
-# 'twas the same for me back when I wanted to mod Balatro because I found the Talisman sounds cool as fuck.
Gotta limit it to a 1 time effect though, oopsie
edited edit, no I don't, this is funny
ali do you have any clue abt mine? :3
Real
The loading script?
yeah
local file_names_jokers = {
'bowlatro',
'dudehole'
}
local folderpath = 'objects/jokers/'
for _, v in pairs(file_names_jokers) do
local file = folderpath .. v .. '.lua'
assert(SMODS.load_file(file))()
end
i got the loading to work by just copy/pasting my working joker (bowlatro) code into it so its smth wrong with the original dudehole.lua code specifically
If it doesn't load, but doesn't crash, then the loading itself may not have target the file - verify the path and filenames.
gotcha thanks
loc_txt = {
name = "Hurry Deck",
text = {
"Winning Ante is {C:attention}6",
"Shop's items cost {C:attention}twice more{}",
"Start run with {C:money}$25{},",
"{C:attention}{T:v_seed_money}{}",
"and {C:attention}{T:v_money_tree}{}"
}
},
}
Tried to use the {T:key} to add the voucher description but i guess i've done it wrong ? Can someone help please ?
you don’t put {T:item} between {C:attention}{}
it’s {C:attention,T:v_seed_money}text or var{}
Ah alright, thanks !
Is it nomal it won't work with my custom vouchers ? I set the prexif and everything
loc_txt = {
name = "Complete Focus Deck ",
text = {
"Winning Ante is {C:attention}10",
"Standard Packs are banned",
"Start with the {C:attention, T:v_cymbal_slide}Slide{}",
"and {C:attention, T:v_cymbal_solarwind} Solar Wind{}"
}
},
}```
why is every joker idea I have end up more complex to code than the last lol, it doesn't matter how complex I think it'd be, it ends up more complex lol
flower-pot style effect which makes it so that if you discard exactly 4 cards, 1 of each suit, [effect happens]
apparently flower pot's code is like a full damn novel in length tho lmao
;-;
checked it, it seems pretty simple, just takes up a lot of space. defines an array of the four suits, adds to the suit for each one in the hand, and if they're all 1 or more triggers its effect
well it sure sounds like a requiremend, individual checking of the suit, checking if 4 cards,
mmmm, yeah, I think what I need is... to swap the "scoring hand" bit for a discard somehow... also, the effect I want is gonna need a way to destroy the discarded cards lol (it's sorta like a mix of trading card and flower pot lol)
Alright gang, last thing for today. Making a challenge to start with my new Joker. The name is there and the challenge appears in Challenges, but my custom rule and my joker don't appear. I have put both in curly brackets just to be sure, and now I'm getting a "card.lua:279: attempt to index local 'center' (a nil value)" error. Could anyone toss me in the right direction?
--Challenges
SMODS.Challenge{
key = 'kain_outside',
loc_txt = {
name = "I Like Yer Dog Mate"
},
rules = {
custom = {
{id = "ch_c_kain_walk"},
},
modifiers = {
}
},
jokers = {
{id = "kain_outside"},
eternal = true
},
}
Localization file:
return {
misc = {
challenge_names = {
kain_outside = "I Like Yer Dog Mate",
},
v_text = {
ch_c_kain_walk = {
"Join {C:mult}Kain{} on a {C:attention,E:1,s2}walk"
}
},
},
}
'j_kain_outside'?
I haven't done j_'s for this one
want to let know i've found the issue cuz apparently the reason why was because of the space between the comma and the 'T'
if context.after and context.cardarea == G.play and SMODS.pseudorandom_probability(card, "xmpl_epik_face_hand", 1, card.ability.seal.extra.odds1) then
for i = 1, #G.hand.cards do
SMODS.destroy_cards(G.hand.cards[i])
end
return{
message = "EPIK PRANK",
sound = "xmpl_bruh"
}
end
if context.after and context.cardarea == G.play and SMODS.pseudorandom_probability(card, "xmpl_epik_face_self", 1, card.ability.seal.extra.odds2) then
SMODS.destroy_cards(card)
return{
message = "EPIK OUTRO",
sound = "xmpl_vine"
}
end
why does the first condition activate but the second one doesn't?
return stops execution of code afterwards.
You'd want to move card destruction to context.destroy_card, see https://github.com/Steamodded/smods/wiki/Calculate-Functions
ok thx
Could this be related to my loc_vars? I saw someone else did, although their info_queue was miswritten as 'info,queue'
loc_vars = function(self, info_queue, card)
return { vars = {card.ability.extra.Xmult, card.ability.extra.Xmult_gain, card.ability.extra.ace_count}}
end,```
context.destroy_card and SMODS.pseudorandom_probability(card, "xmpl_epik_face_self", 1, card.ability.seal.extra.odds2)
like this?
if context.destroy_card and context.cardarea == G.play and SMODS.pseudorandom_probability(card, "xmpl_epik_face_self", 1, card.ability.seal.extra.odds2) then
return { remove = true }
end
ok thx alot
ok now the first condition is no longer working
ok I think that the reason is bc it is destroying itself before the context.after activates it doesn't activate the first one
ok fixed the problem by changing from context.after to context.final_scoring_step
how do you modify the values that editions give
like how would you make holo +15 Mult
the best way i can think of rn is to take ownership of the edition
okay speaking of take ownership
how the Fuck do i use it
SMODS.Edition:take_ownership(key, {})
where the table is all the elements that u want to override
hey guys can someone tell me in which file the shop is coded?
hold on
pls explain
because u just call :take_ownership() on the object type of the thing that u want to take ownership of
example here
also as you'd expect
the first arg is the full key of the object without the object type prefix
Why isnt it with the object type prefix anyways
because it's already a function call on the object type (i.e. SMODS.Joker:take_ownership will only ever be taking ownership of jokers)
prob because it knows the object type in advance
its mentioned in a small area within the docs
i suppose that makes sense
but yeah its easy for one to miss if they dont know the exact verbage
also im considering reworking trash bin to be "+1 mult for every card discarded this round"
its just like
actually unique
a bit
I'm not sure if this is viable in Lua but an alternative could be creating an event to add a delay, giving you time to do your stuff before it's properly destroyed. Common C# trick to let an animation play out without instantly being destroyed
is there a mod that adds a new button to the shop?
iirc ortalab adds a flipside button
thanks
so for foil would it be
wait
let me just check the source code
e_foil?
where are the editions
in the source code
okay found them
how the fuck do i access foil's chips
🥀
G.P_CENTERS.e_foil.config.extra
game.lua ctrl+f: e_foil
so would SMODS.Edition:take_ownership(foil, {G.P_CENTERS.e_foil.config.extra = G.P_CENTERS.e_foil.config.extra + card.ability.chips}) work
-# That sure would be perma.
+-50 🔥
If you want to modify the chips of Foil on a card w/o affecting others, do card.edition.chips, where card is the Joker or playing card.
Keep track of the "level" and modify the chips in set_edition hook?
fuck i have never worked with hooks
hi guys
newbie modder here, never worked with lua, thought i'd mess around with the joker forge tool. I was trying to make a life is strange inspired card that basically prevents death, resets the blind and deck and hands and discards, as a rare card. this works once per round thats not a boss blind
joker forge has a prevents death effect but it auto wins the run
can i implement something like this with the site or do i have to get my hands dirty with lua
god damnit wtf do i do
how do you check if a hand has been played this round
hooks arent too scary
i think theres a vanillaremade wiki section on hooks
tldr hooks let u execute code before and or after a function is called
yuh
¯_(ツ)_/¯
im surprised you havent had to use a hook yet
that is also in the vremade wiki
look at card sharp
N' the goat
yeah im looking at it
i dont know What to Do.
not at my pc rn so im not sure what parameters are available on set_edition
function Card:set_edition(edition, immediate, silent, delay) end
surely u'd just define
G.edition_levels = {}
by hooking G.start_run
and then when a edition planet is used,
add the editions key to a table and set it to 1 if the key maps to nil, otherwise increase the mapped value by 1
local old_set_edition = Card.set_edition
function Card:set_edition(edition, immediate, silent, delay)
-- do code here
local ret = old_set_edition(self, edition, immediate, silent, delay)
--more code
return ret
end
basically
- store a reference to the original function you want to hook
- override the function by defining a function with the same name and arguments as the original
- make sure you call the original via the reference somewhere in the overriding definition EXACTLY ONCE
- make sure to return a value if the original function returned something
For some reason the 'test' object in this doesn't have an ability table?
class_prefix = 'md',
set = 'OpalModifier',
discovered = false,
unlocked = true,
atlas = 'OpalModifiers',
pos = {x = 0, y = 0},
config = {},
required_params = {'key'},
pre_inject_class = function(self)
G.P_CENTER_POOLS[self.set] = {}
end,
}
OPAL.Modifier{ -- Test
key = "test",
name = 'Test Modifier',
atlas = 'OpalModifiers',
pos = {x = 0, y = 0},
config = {}
}```
i can redirect you to some examples to hooks in my mod if ur a "learn by example" kind of person
yeah probably
i think the is_suit and is_face hooks would probably be the most helpful to look at
get_id is also the simplest hook in there
ya know im surprised i actually havent needed to do a lovely patch at all
-# ...was gonna send a complete hook...
is there a way to "force-update" a tooltip box? im changing a center's config with lua but it still shows the old value on the card
trying to make a consumable that opens multiple standard packs, however it keeps freezing the game on being used
for i = 1, math.min(card.ability.boosters, 20) do
G.E_MANAGER:add_event(Event({
trigger = "before",
func = function()
if (G.STATE == G.STATES.SMODS_BOOSTER_OPENED) then return false end
local key = "p_standard_normal_1"
local booster = Card(
G.play.T.x + G.play.T.w / 2 - G.CARD_W * 1.27 / 2,
G.play.T.y + G.play.T.h / 2 - G.CARD_H * 1.27 / 2,
G.CARD_W * 1.27,
G.CARD_H * 1.27,
G.P_CARDS.empty,
G.P_CENTERS[key],
{ bypass_discovery_center = true, bypass_discovery_ui = true }
)
booster.cost = 0
booster.from_tag = true
G.FUNCS.use_card({ config = { ref_table = booster } })
booster:start_materialize()
return true
end,
}))
end
end,```
the "if (G.STATE == G.STATES.SMODS_BOOSTER_OPENED) then return false end" part is definitely not being done correctly, but... how *do* i get it to only fire off the second and third pack events *after* you're out of the first pack? (without this line it tries to open all three packs at the same time, which is... also bad.)
what it does:
wait uh
i removed a bunch of mods and now its behaving differently (fuck i shouldve done this first)
its just doing the same "open all three packs at once" thing instead of freezing
so this still isn't the way to do it
uh. how do i correctly only open one pack at a time.
instead of doing...
this
blocking = false to stop freezing game
how do i make it not do this then
i want them opened... one at a time
I think you can check if G.pack_cards exists and If not open one
so if G.pack_cards?
help i think this is all i need to know to get ts working
If It doesn't work check If there's any card in G.pack_cards and If there's not open booster
why the fuck did that open the booster in the shop screen.
hey guys can someone confirm that ortalab adds a new button to the shop? because someone said it does and i cant find any info on it
entropy adds a new button after boss blinds only
thank you
if you remember can you tell me if it has to meet a condition like a voucher/joker or something or does it always show up
always shows up
Hello, I have a joker that creates a planet card for a random hand played at round end, I have mentioned it here before. But I'm having trouble making it blueprint compatible. Is it since it triggers at joker_main to save the hand played and then again at round end to create the card and the blueprint doesnt have the list of hands and as such the game goes boom? How do blueprint jokers work?
as athebyne said it always shows up, specifically only after boss blinds
okay thanks
good news, that works
bad news:
what the fuck is going on
current code:
for i = 1, math.min(card.ability.boosters, 20) do
G.E_MANAGER:add_event(Event({
trigger = "before",
blocking = false,
func = function()
if ((G.STATE == G.STATES.SMODS_BOOSTER_OPENED) or G.pack_cards) then return false end
local key = "p_standard_normal_1"
local booster = Card(
G.play.T.x + G.play.T.w / 2 - G.CARD_W * 1.27 / 2,
G.play.T.y + G.play.T.h / 2 - G.CARD_H * 1.27 / 2,
G.CARD_W * 1.27,
G.CARD_H * 1.27,
G.P_CARDS.empty,
G.P_CENTERS[key],
{ bypass_discovery_center = true, bypass_discovery_ui = true }
)
booster.cost = 0
booster.from_tag = true
G.FUNCS.use_card({ config = { ref_table = booster } })
booster:start_materialize()
return true
end,
}))
end
end```
i. feel like im missing some things here
why are boosters so cursed
i want to not make the game do things twice at the same time
i dont know if im stupid or what but i cant find the button or get it to show up i even looked at videos of other people on youtube and it doesnt show up for them either can someone who has it installed pls screenshot it so i can at least see what it looks like?
how do i make this not be inside of the shop
enter the flipside, purple button
won't appear on ante 1
https://github.com/lord-ruby/Entropy/blob/80de027397ccf68f0f061f2d265e224f423c0de2/lib/ui.lua#L318 code for the button is around here
man you are a life saver
ty
anyways could you help me with my consumable i'm trying to figure out how to get this to create booster packs without it happening on top of the shop
i would love to but im fairly new here too so i dont have much experience im sorry
Sorry I asked for help in the middle of this thread so it kinda didnt go well
do you guys know how I could make a joker blueprint compatible?
more exactly if a joker saves values and then the blueprint copies it how does blueprint access those values?
becase im just getting nil
fixed it, but still dont understand why the previous version didnt work
Current working blueprint version
if context.joker_main then
card.ability.extra.hands_table[#card.ability.extra.hands_table + 1] = context.scoring_name
card.ability.extra.hand = pseudorandom_element(card.ability.extra.hands_table, self.key)
end
if context.end_of_round and context.cardarea == G.jokers then
if G.consumeables.config.card_count < G.consumeables.config.card_limit then
card.ability.extra.hands_table = {}
local planet = Get_hand_planet(card.ability.extra.hand)
return {
message = 'Found ' .. planet.name,
func = function()
G.E_MANAGER:add_event(Event {
trigger = 'after',
delay = 0.4,
func = function()
if G.consumeables.config.card_limit > #G.consumeables.cards then
play_sound('timpani')
SMODS.add_card { key = planet.key }
end
return true
end
})
end
}
end
end
previous version:
if context.joker_main then
card.ability.extra.hands_table[#card.ability.extra.hands_table + 1] = context.scoring_name
end
if context.end_of_round and context.cardarea == G.jokers then
if G.consumeables.config.card_count < G.consumeables.config.card_limit then
local rand_hand = pseudorandom_element(card.ability.extra.hands_table, self.key)
card.ability.extra.hands_table = {}
local planet = Get_hand_planet(rand_hand)
return {
message = 'Found ' .. planet.name,
func = function()
G.E_MANAGER:add_event(Event {
trigger = 'after',
delay = 0.4,
func = function()
if G.consumeables.config.card_limit > #G.consumeables.cards then
play_sound('timpani')
SMODS.add_card { key = planet.key }
end
return true
end
})
end
}
end
end
what function would i hook to do something when exiting the run
bunp
how the hell do i get this to not be overlapping the shop
it should just be opening the booster
i believe this is some inherent jank after using a consumable where it briefly hides/rehides the shop
i diw rtie a function that lets you pretend that anything is a booster pack and open that with 0 problems*
*1 problem but i think ive thought of a way to fix it so it doesnt require global mod calculate dejanking
👀
Game:delete_run iirc
would thee want it
i would i think
would also like it to properly open multiple boosters in sequence
as all ive done is either only open one but still have booster particles, or open all 3 at the same time
well thats not the point of the function so you might need to do some heavy tinkering on it
its main purpose is so you could do the open anything anywhere as a booster pack without both the open function and the state system freaking out
i dont think it does sequential stuff well
but give me like 20 mins and ill hand you the function in whatever state i get it to since i need sleep anyway
okay smods wiki documentation with the hail mary?
god damn it works
pseudo_open no longer uses any global calculate jank 🙏
yeah no i misread the first time but this function def cant do that
youll have to tinker
fuck
okay sadly i forgot that this actually needs a small lovely patch to function, but otherwise its decently self contained albeit very jank
i quickly jotted some comments down so you can understand it a bit quicker
[[patches]]
[patches.pattern]
target = '''functions/button_callbacks.lua'''
pattern = '''
if card.ability.set == 'Booster' then
G.CONTROLLER.locks.use = false
G.TAROT_INTERRUPT = nil
else
'''
position = "at"
payload = '''
if card.ability.set == 'Booster' or card.config.pseudo_open then
G.CONTROLLER.locks.use = false
G.TAROT_INTERRUPT = nil
else
'''
overwrite = true
match_indent = false
the lovely patch in question (may or may not follow best patching practices)
god i really need to make my code cleaner
Say, anyone here do custom cards?
yes
[SMODS _ "src/utils.lua"]:2703: handname 'napoli_Trentuno' not found!
I'm getting this crash even though my hand key and the localizations match, anyone can help?
did not have this problem adding other poker hands
did you restart the run
i am maybe an idiot if that is the problem
what am i doing wrong here? Im trying to make it act like a timed negative
in loc_vars u need to check if card.edition exists first
How do I get my mod's config file? Like for making a config tab for my mod
dumb question but how do i do that?
this other edition works but i jsut dont know
do you even need loc vars? you're not using them in the loc_txt
nvm figured it out
sharing here since everyone in #⚙・modding-general is like. at most 15 years old apparently
Scoring_Calculations is goated as fuck
I'm using it for two mods to replace previous implementations of new scoring parameters, and things are so much more easier to deal with
thank you for your work
I'm trying to implement a stake that makes cards have a chance to get a sticker, but the stake just makes every card have the sticker. Here's the code I have for the stake and sticker:
SMODS.Sticker {
key = "stickernana",
needs_enabled_flag = true,
should_apply = function (self, card, center, area, bypass_reroll)
return (
G.GAME.modifiers.enable_stickernana
and not card.ability.eternal
)
end,
rate = 0.3,
sets = {
Joker = true
},
calculate = function(self, card, context)
...
end
}
SMODS.Stake {
key = ...,
applied_stakes = { "gold" },
modifiers = function ()
G.GAME.modifiers.enable_stickernana = true
end,
}
return SMODS.Sticker.should_apply(self, card, center, area, bypass_reroll) and G.GAME.modifiers.enable_stickernana and not card.ability.eternal
problem now is that the stake doesn't apply any previous stakes, and doesn't apply banana sticker at all
...oh wait
hold on lemme check other code, might be related to sticker ownership taking
nvm, it's not
I have this crash I dont understand why
Maybe try putting the "unhighlight all" call event after of the for loop?
yeah I could see how that may crash
unhighlight_all() is called while you're still iterating over G.hand.highlighted
unhighlight_all() modifies G.hand.highlighted
oh
wait I'll try
but its weird since it crash when I win the round
its not that but it make the code better
ok I found that aether joker made it crash but It never happen i'll investigate
how does SMODS actually check which version is superior to which lol
does it check individual number by individual number (aka it sees 1 < 9 and it doesn't work)? 
it's probably alphabetical yeah
yeah that's why smods versions are like 0711a for example
yeah ok
also for example, if a mod goes from 9.xx to 10.xx, i figure it'll also say 9 > 1 and so the older version will be seen as compatible? but I don't think anyone puts a 0 before the 1st number in the version so 🤔
pokermon's eventually gonna have this problem lol
i think for the standard versioning it works correctly that 9.0.0 < 10.0.0
afaik yes
good to know
Is "repetitions = card.ability.extra.repetitions" the correct writing for returning a repitition because It doesn't seem to be working for me
that part looks correct but I would need to see the full code
if context.cardarea == G.play and context.individual then
for _, other_card in ipairs(G.play.cards) do
if context.other_card:get_id() == 12 then
card.ability.extra.queen_count = card.ability.extra.queen_count + 1
end
end
end
if context.repetition and context.cardarea == G.play then
if context.other_card:get_id() == 12 then
return {
repetitions = card.ability.extra.repetitions,
message = "Retrigger",
colour = G.C.Orange
}
end
end
end, ```
Basically the idea is to only retrigger the queens if there is exactly two queens in the played hand
do queens actually have that id tho
you can do
```lua
```
to format the code btw
yes
print("like this")
gotcha
vessel: you were supposed to obey me
e_mult:
careful joker positioning:
literally any consumable that can strip eternal:
is repetitions in your config
eternal_compat=true,
atlas = 'jokers',
pos = {x = 3, y = 1},
rarity = 3,
config = { extra = { queen_count = 0, repetitions = 1 } },
loc_vars = function(self, info_queue, card)
return {vars = {card.ability.extra.queen_count, repetitions = 1}}
end, ```
This is the config
I will say in debug it mentioned something about a lack of repitions being listed internally when the card scored
Sorry new keyboard so I typo often
it's when you don't return repetitions in a repetition context
"Found effect table with no assigned repitions" or something like that
did you restart the run before testing
wait
had it happen yet returned as intended
omfg
are you kidding me
Yes, thank you, I literally would've kept trying endlessly
resetting the run worked
if you change the config you need to get a new instance of the joker, but usually it's better to restart the run just in case
Will inscribe that into my brain, thank you a bunch
badge_colour doesn’t seem to work for an extension of SMODS.Object
Yes, because that doesn't exist.
I'm sure I'm pretty close to figuring it out, as I just have to do a better job reading the mod badge code, but does anyone know the way to get the ID of a joker/card's source mod?
card.config.center.original_mod.id
You're a champ, N
-# I still want to do that "putting 'Blind' in 'Blind run'" mod after these antics. 😂
btw does 'colour = G.C.Purple' work for making messages purple or can Balatro only do certain colors?
if you want to do custom colors youll need to do V: or lovely patch
there's a list of colours but G.C.PURPLE should work
you don't need to lovely patch for custom colors
i said V:
i had a custom currency in my roadmap
but i know fr fr that this one requires a lovely patch
quick question: does big and small blinds have object key? keys in sense like "v_grabber"
bl_small and bl_big
thank you
sorry for the late response but "options" must contain the objectType pool's key or "type_key" must contain that?
well for starters, that's not how the arguments are passed
ah oof
question: why it doesn't take an ObjectType?
because it was not programmed to lol
objecttypes are not that widely used and most people just use them for jokers
i mean you can keep it
you can get the list easier by iterating G.P_CENTER_POOLS.objecttypekey
aight
you should be able to just put options = SMODS.ObjectTypes.key.cards I think
uh oh-
I tried using the consumable but it did nothing the first time, when I used the second one this error message appeared
if it was this code then it's still not the correct format
what format should I fix?
or what part of it
it should be something like SMODS.poll_enhancements( { type_key = "seed", guaranteed = true, options = { "m_prefix_key", "m_prefix_key2" } } )
aight, question: what is the difference between the key and the type_key?
this isn't applying Eternal to the Jokers
oh, so one defines when to generate one and another defines which one to generate
yeah
is there a way to code a poker hand part for a 3 card flush?
force_stickers = true
i'm struggling with checking for suit
thanks N'!
Thanks, fixed
local oldsmodsfourfingers = SMODS.four_fingers
function SMODS.four_fingers() return 3 end
local result = get_flush(hand)
SMODS.four_fingers = oldsmodsfourfingers
return result
ok so uh, small update now something really peculiar happens when I use the consumable:
basically it creates a card of with the random enhancement applied to it and then destroys it.
I tried using "card.ability.max.highlited" instead of "card" but it gives me error as it is just a number variable.
What should I use to get the selected cards to apply the random enhancement?
i don't need it for a joker i need it for a poker hand part
i mean
i don't need to change base flush entirely
It doesn't.
G.hand.highlighted
check the tarots in vanillaremade they have a manual version commented
You create a new SMODS.PokerHandPart and you put that in the func
aight
sorry, thandfkjnkfsnkjfdn I am tired lol, just woke up, one second, I'll give further info lol
essentially, I'm trying to make an effect which is kinda a hybrid of both trading card and flower pot, where if you discard exactly 1 card of each suit, in a 4 card discard, they'll all be destroyed and you'll gain money in return
I am not sure how I'd exactly swap out the played hand for a discard effect like that however ndfskjnsdfkjsd
if context.pre_discard and G.hand.highlighted[1] and #G.hand.highlighted == 4 then
... -- suit table here.
for i = 1, #G.hand.highlighted do
... -- do the base suit check with this loop.
end
for i = 1, #G.hand.highlighted do
... -- do the 'any suit' check with this loop.
end
-- put the final if with the suit counts and result here.
end
how does one refer to a SMODS.Gradient color in loc_vars or card desc text
liek would i add it to return{ vars = { colours {etc}}} in loc_vars and then use as {V:1}text{} or could i just refer to it by name as in {C:etc}
i get the feeling its the first tho im just
not sure HOW to refer to it either lol
thank you <3 I'll try this out
so like
and
loc_vars = function(self, info_queue, card)
return { vars = { colours = {SMODS.Gradients.wsh_essence} } }
end,```
for each ?
wsh being the prefix and essence being the key in SMODS.Gradient
yeah
you don't need both if you're not using V:
ok, so, I messed up somewhere here I think lol bdfhbj it seems to be crashing ;-;
You didn't replace context.scoring_hand parts.
oh, lmao 😭 dfnsd sorry, one second lol
does this one look good?
it still crashes sadly it seems ndkjsnfksdjf :<
#G.hand.highlighted[i] > G.hand.highlighted[i]
mmm I see...
question, what is the difference between including or not the #?
just curious for future reference nsdmfksdn
#table is length of table.
...as long as table has table[1], table[2] and so on in order... as long as there's not a break in that.
ahhh, I see, so, what I'm doing there basically is "digit length of xth digit of the table" which, just, doesn't make sense to the code lol
Length of the table element.
ok, so, it works now dfnsdfj, well, almost, except for one aspect, the cards aren't being destroyed yet, otherwise, it seems everything is working dfnksdf
If the conditions pass, on one hand, you can pass the cards to SMODS.destroy_cards(G.hand.highlighted)... on the other, you can, once again, iterate over G.hand.highlighted, but this time, tag them, like G.hand.highlighted[i].black_lotus_destroy.
Then, in a separate context check for if context.discard and context.other_card and context.other_card.black_lotus_destroy then, you can return { remove = true }.
mmmmm, interesting interesting
I somehow only just realized from this that I could be making my own contexts for things lol, not sure what I'd use it for yet, but it sure could lead to some interesting stuff mayhaps
I think I wanna do the 2nd option here, for the sole reason that I'm thinking perhaps maybe making it so the money is gained per removed card instead of being all at once could be an interesting thing, perhaps I could make a mime style joker at some point which retriggers discard effects, if that'd even be possible lol
I have 9 Jokers that respond to custom contexts.
so, I'd do this on each instance of G.hand.highlighted I assume?
bonzai buddy in the middle lmao
doesn't even have a joker tag lol
If the Flower-Pot-like conditions pass, iterate over the G.hand.highlighted and set G.hand.highlighted[i].black_lotus_destroy, yes.
mmm, I see, I see, I think I'm getting it
Then
if context.discard and context.other_card and context.other_card.black_lotus_destroy then
context.other_card.black_lotus_destroy = nil
return { remove = true }
end
as context.discard iterates over each discarded card individually.
if suits['Hearts'] > 1 and suits['Diamonds'] > 1 and suits['Spades'] > 1 and suits['Clubs'] > 1 then
for i = 1, #G.hand.highlighted do
G.hand.highlighted[i].black_lotus_destroy = true
end
end
mmm, I see, I think I get it, maybe
(sorry for being stupid lol, I am like this frequently lmao)
I'll try this lol
Just remove the setting of that variable out of the suit checks.
And no worries... my initial antics here were more or less the same when I started.
been trying to mess with it and i keep getting this error,,,
key = "essence",
colours = {
HEX('EB00C4'),
HEX('A60056'),
HEX('CF2B80'),
},
cycle = 10,
interpolation = 'trig',
}```
-# the gradient code for reference if that matters too
so, no crash, but it isn't triggering it seems sdmfsdf
how does the additional text for perma-bonuses on cards work? i figured out that i had to hook SMODS.localize_perma_bonuses, but how do i get the game to actually pass in the variables for a perma bonus into specific_vars
The if context.discard part is supposed to be outside of the if context.pre_discard.
oh? I was under the impression that you could only have a single calculate in a joker lol bjhdsfj tho I may've known that at one point and forgot lol
Question : how do I modify the blind size mid blind I'm trying to do a seal that reduce it but when I play the seal it just get stuck in the playing phase without doing anything
One can contain them all.
I found that on arcanum
can i see the actual joker description and code
key = "essence",
colours = {
HEX('EB00C4'),
HEX('A60056'),
HEX('CF2B80'),
},
cycle = 10,
interpolation = 'trig',
}
SMODS.Consumable({
set = "Tarot",
key = "tar_awesome",
pos = {
x = 1,
y = 2
},
loc_txt = {
name = "The Awesome",
text = {
" ",
"{C:inactive}It would be so cool...{}",
" ",
"{C:green}1 in 100{} chance for either",
"{C:dark_edition}The Soul{} or {C:wsh_essence}The Essence{}"
}
},
config = { extra = { } },
atlas = 'nixconsume',
cost = 2,
discovered = true,
loc_vars = function(self, info_queue, card)
info_queue[#info_queue + 1] = { key = 'c_soul', set = 'Spectral' }
info_queue[#info_queue + 1] = { key = 'c_wsh_essence', set = 'Spectral' }
return { vars = { colours = { SMODS.Gradients.wsh_essence } } }
end,
use = function(self, card, area, copier)
if pseudorandom('itwouldbesoawesome') < 1 / 100 then -- yeah it's fixed odds lol
if math.random(0, 1) == 1 then
SMODS.add_card({
key = "c_soul",
})
else
SMODS.add_card({
key = "c_wsh_essence",
})
end
end
end,
can_use = function(self, card)
return true
end
})```
I may've did this wrong lol ndfksdn still not triggering 🥲
ik you said you dont need both but i was tryna figure out what you had meant by that too admittedly like which shoujld i use ig?
ive been tryna tinker
if context.pre_discard ...
end
if context.discard ...
end
under one calculate.
bump
if you're using C: you dont use return { vars = { colours = { SMODS.Gradients.wsh_essence } } }
although i dont think thats the crash
mmm, ok nsdkfsd, maybe my issue before was just me messing up my positioning of my ends lol
oh so i could just use V:1 instead of C: im guessing, noted
yeah i had tried that too and it gave the same crash

try removing info_queue[#info_queue + 1] = { key = 'c_wsh_essence', set = 'Spectral' }
horray! it works now!
thanks Ali !
you helped me a lot here <3
WAIT IT WORKED LMAO
now add info_queue[#info_queue + 1] = G.P_CENTERS.c_wsh_essence
yeah
ohhhh i see thisprevents the uh
confusion, effectively [or so im guessing]
given the gradient and consumable im referencing share a name
ty 😭
thats not the issue
the issue is that this doesnt pass the loc_vars
its stuck like that
...why the self.config and not card.ability? Replace the if G.GAME.chips block with just return true.
Then it would be card.ability.seal
G.GAME.blind.chips = math.floor(G.GAME.blind.chips * (1 - card.ability.seal.extra.reduce / 100))
like that
Yes.
if G.GAME.chips - G.GAME.blind.chips >= 0 then
G.STATE = G.STATES.HAND_PLAYED
G.STATE_COMPLETE = true
end_round()
end
and this block isnt necessary ?
Yes.
bump
its stuck again
Show the "full" calculate.
is there any way to make the 'hitbox' of a consumable or consumabletype smaller? i have a consumable type that has a lot of free space at the top and bottom and id like for them not to be clickable there
nvm just found the docs for pixel_size
...remove trigger and delay lines? Or just copy paste my entire hook and edit the values accordingly?
its not that
heres the entire code
...oh, you are not return true in the first event add.
Hi! Is there a way to check for system time? Like I'd like for function to work only for e.g at 17:30 and else to return false. Is it possible?
local time = os.date("*t")
if time.hour == 17 and time.min == 30 then
-- code
end
great, thanks
why can't i set the display_size on an undiscovered sprite? i have an atlas for consumables that are less than the size of a full card but i find i still have to use display_size to stop them stretching, and for some reason it doesn't seem to work on SMODS.UndiscoveredSprite
im trying to make an upgradable joker (via consumables) but i don't know how much mult, chip, or both, modifiers the joker should start with.
i have it set to 10 upgrades right now, but i don't know how to start with the values
how do I debuff jokers in blind?
thx
bump
currently scraping the balatro modded wiki looking for mods with weird consumable shapes to see how they manage lol
weird bug i'm having where SMODS.destroy_cards(card, true, true, true) doesn't make it immediate or skip the animation
how do i make it retrigger glass cards instead of face cards?
if context.repetition and context.cardarea == G.play and context.other_card:is_face() then
return {
repetitions = card.ability.extra.repetitions
}
end
end,```
SMODS.has_enhancement(context.other_card, "m_glass")
SMODS.has_enhancement(context.other_card, 'm_glass') instead of context.other_card:is_face()
yeah that worked thank you!
can I make a blind retrigger cards with specific seal?
yeah, same way a joker or something would do it
blinds can use calculate in all the same ways
oh ok
back again, now im trying to make a joker that makes face cards undebuffable, but this isn't working for some reason
if context.setting_blind then
for _, c in ipairs(G.deck.cards) do
if c:is_face() then
c:set_debuff(false)
end
end
end
end```
if context.debuff_card and context.debuff_card:is_face() then
return {
prevent_debuff = true
}
end
that worked thank you!
is calculate = function(self, card) G.GAME.dollars = G.GAME.dollars - 1 end, a right way to charge 1$ for every played card just like in The Tooth?
ease_dollars(-1)
or will it trigger just once for played hand?
also you forgot the context check
just that? without anything else?
oh right
the tooth loops over G.play.cards in context.before for the money stuff
also you should probably look at vanillaremade the tooth if youre doing basically the same effect
sure, I'm only using smods api, so yeah... I'll look for it thanks
Does anybody know how I can set the display_size of an UndiscoveredSprite? just doing display_size = {w=67,h=68} seems to not be doing anything
-# ...does UndiscoveredSprite even support display_size..?
is there any way of getting it the right size or am i royally screwed
i think aikoshen does something with the undiscovered sprite for letter cards? unless those are standard size but pretty sure they are not
override te inject function maybe and like set self.overlay_sprite.T.w and self.overlay_sprite.T.h maybe
ok no i guess alphabet cards are standard sized. yay
time to patch i guess
hang on. i have no fucking clue how to do that
guess i should try anyways
🥀
ok just remembered setting the overlay sprite does nothing ebcause im not using an overlay sprite
this isnt working
Where can I find the hand size as a variable again?
G.hand.config.card_limit
Alr
i have a solution. what f i just design the undiscovered sprite to be full card sized out of laziness. problem solved
I was wondering if there was a method that returns a boolean if card is a joker.
card.ability.set == "Joker"
thank you
help with planet descriptions not showing up?
making a joker that does something else when sold, is there to prevent a card from being sold, instead of just copying the joker when sold?
Hook Card:can_sell_card
gotcha thank you
Hi, will that work for checking the seal on played cards?
Has anyone else had a problem with stack overlow after duplicating a Joker with a custom button (above/below Sell)?
That probably means you made an infinite loop
how do i make it so that the background is set to a different colour until a condition is set
I didnt make this loop and it crashes there
why arent you just using copy_card
I'm not calling this function directly, but using Ankh leads there (or using DebugPlus duplicate hotkey)
and I can't figure out why the custom button is the culprit
I have no idea why won't this joker ever trigger... Any help?
can you also show the seal definition
don't put the _seal suffix in the seal's key itself, it should just be key = "kazakh"
the way you have it now, you'd need to reference "misiek_kazakh_seal_seal" in some places
yeah I do have it like that, the seals work just fine
how do i remove stickers from all jokers?
the joker also subtracts -0.2 per every card with seal but doesn't trigger what's inside context.joker_main at all
ACTUALLY working on my first mod
fire
Battle cats spotted
Get it cuz theres a spot joker
its a poptart
How do you make jokers trigger when other jokers trigger?
iirc its context.post_trigger
From the smods wiki
if context.post_trigger then
{
cardarea = G.jokers, -- G.hand, (G.deck and G.discard optionally enabled)
post_trigger = true,
blueprint_card = context.blueprint_card, -- if applicable
other_card = card, -- the card that triggered
other_context = context, -- the context the trigger happened on
other_ret = ret -- the return table from the trigger
}
post_trigger is also an optional feature that you need to activate
Ah ok
Cos I'm tryna make a joker that gains mult when cards, jokers and consumables are triggered, then resets when the joker itself is triggered after giving it's mult.
Where do you activate that?
is it possible to have a deck start in a different game state? i'm aiming for a custom one but i'll just say i want it to start in the shop as an example
Yeah, should be able to patch into the run start stuff and change something like that
In your main file use SMODS.current_mod.optional_features = {post_trigger = true}
problem is it does the set state stuff before G.GAME is initialized
at least it seems like it
Can you not just set the state after that?
i tried doing it in the deck object, didn't work though that might've been because it also starts you with 2 buffoon tags
calculate = function(self,card,context) --define calculate functions here
if context.joker_main and context.cardarea == G.jokers then
if card.ability.extra.mult > 0 then
local effects = {
{
mult = card.ability.extra.mult,
colour = G.C.MULT
},
{
message = localize('k_reset'),
colour = G.C.MULT
},
}
card.ability.extra.mult = 0
return SMODS.merge_effects(effects)
end
end
if not context.blueprint then
if (context.post_trigger and not context.joker_main) or context.using_consumeable or (context.individual and context.cardarea == G.play) then
card.ability.extra.mult = card.ability.extra.mult + card.ability.extra.mult_mod
return {
extra = {focus = card, message = localize('k_upgrade_ex')},
card = card,
colour = G.C.MULT
}
end
end
end,
The joker triggers twice when playing cards or consumables are used and I suspect it's to do with the joker triggering itself. Anyone know a fix?
"AHHH BAINE I NEED x6 MULT!"
im getting this same issue but every time i find someone else talking about it they get 0 replies
why is this the worlds most ignored problem
not one reply? not one hint?
is there a way to specifically retrigger a consumable's effect
like if its a held-in-area effect or otherwise
i was considering checking how whoprint [yahimod] works with jokers and try to repurpose it with consumables
as i want it to be for a specific Set too
I probably should've looked at this code earlier, cuz I don't think I would ever be able to reproduce using this code.
It might be multiple parts of your mod interacting in weird unexpected ways? If you manage to make a single file mod that you can make crash you can probably solve it
Yeah I'll have to narrow it down
ye
It's not unsolvable, it'll just be boring to fix because the crash log is spectacularly unhelpful
bump
BTW how do you check how many joker slots there are?
#G.jokers.cards
wait no thats
total jokers owned
hangon
G.jokers.config.card_limit
this is the total joker slot count
thx
np lol
is there a trigger for when a voucher is redeemed?
get_xmult = function()
local my_pos = 1
if #G.jokers.cards then
for i = 1, #G.jokers.cards do
if G.jokers.cards[i] == card then
my_pos = i
break
elseif i > #G.jokers.cards then
break
end
end
end
local joker_count = G.jokers.config.card_limit or 5
local x_mult = (((1-card.ability.extra.x_mult_mod) * (joker_count-my_pos)) / (joker_count - 1)) + 1
if x_mult < 1 then
x_mult = 1
end
return x_mult
end,
get_xmult_gradient = function()
local joker_count = G.jokers.config.card_limit or 5
local x_mult = (card.ability.extra.x_mult_mod - 1) / (joker_count - 1)
return x_mult
end,
calculate = function(self,card,context) --define calculate functions here
if context.joker_main and context.cardarea == G.jokers then
return {
x_mult = card.get_xmult(),
colour = G.C.MULT
}
end
end,
loc_vars = function(self, info_queue, card) --defines variables to use in the UI. you can use #1# for example to show the chips variable
return { vars = {card.get_xmult(), card.ability.extra.x_mult_mod, card.get_xmult_gradient()}, key = self.key }
end
The game crashes when I try to call the functions, anyone know how to call them?
nvm put the function in config and called it through that
how would one adjust the amount of cards avilable in a booster pack mid run
ie with a voucher
I believe there's a global var you can access to do that
G.GAME.modifiers.booster_choice_mod
Yes
im specifically tryna adjust the ones for one specific type
could that still work or 
you can probably dig through the smods code to see how the global modifier works, and then make a copy of it that only applies to the specific type
SMODS.Joker{
key = "deltah",
config = {
extra = {x_mult_mod = 5},
},
calculate = function(self,card,context)
if context.joker_main and context.cardarea == G.jokers then
local my_pos = 1
local joker_count = 1
for i = 1, #G.jokers.cards do
if G.jokers.cards[i] == card then
my_pos = i
break
elseif i > #G.jokers.cards then
break
end
end
local x_mult_gain = get_deltah_x_mult(my_pos, G.jokers.config.card_limit, card.ability.extra.x_mult_mod)
return {
x_mult = x_mult_gain,
colour = G.C.MULT
}
end
end,
loc_vars = function(self, info_queue, card) --defines variables to use in the UI. you can use #1# for example to show the chips variable
local joker_count = 5
local my_pos = 1
if G.jokers then
joker_count = G.jokers.config.card_limit
for i = 1, #G.jokers.cards do
if G.jokers.cards[i] == card then
my_pos = i
break
elseif i > #G.jokers.cards then
break
end
end
end
local x_mult = get_deltah_x_mult(my_pos, joker_count, card.ability.extra.x_mult_mod)
local x_mult_gradient = (card.ability.extra.x_mult_mod - 1) / (joker_count - 1)
return { vars = {x_mult, card.ability.extra.x_mult_mod, x_mult_gradient}, key = self.key }
end
}
local function get_deltah_x_mult(position, slots, x_mult_mod)
local x_mult = (((1 - x_mult_mod) * (slots-position)) / (slots - 1)) + 1
if x_mult < 1 then
x_mult = 1
end
return x_mult
end```
Game crashes when I try to call the local function
Anyone know why that happens?
ortalab’s Cantor Loteria does this but for how many cards you can select, should be able to look at that
how can i make this repeat the joker or playing cards? It replays playing cards but doesnt do so for a joker
the joker repeats the playing cards, which is neat too but not my intended effect
context.repetition is only for playing cards
you need to enable the retrigger_joker optional feature and then use context.retrigger_joker_check (and don't use the cardarea or main_scoring checks)
if you only want to retrigger the joker that the edition is on, then you'll need to check if card == context.other_card
alright
say wait in a similar vein
is there a similar method like this for consumables
whether it’s a held-in-area effect or basically a “double-effect” on use
uhhh i don't think so
at least not anything as simple as returning repetitions is for joker or playing card retriggers
unless maybe held-in-area consumables count for the retrigger joker check?
at worst you can copy the code for the retrigger joker check and set it up to run a new context for retriggering calculating consumables instead
kw idk how to make it retrigger the joker if its a joker and playing card if its a playing card but ngl
the concept of making the joker retrigger cards is kinda fun so
can SMODS.blueprint_effect copy multiple jokers at once?
not directly. however:
https://github.com/Steamodded/smods/wiki/Utility
oh sweet thank you
Howdy im uh new here and need help with the tailsman mod for balatro if anyone will help me
how do you add an extra sidebar button to a joker?
it HAS to be a lovely patch but I cant find any examples
Do you mean like the Sell button?
yeah basically
I found some examples in the entropy hooks
i might be able to use that
I also managed to do it but it crashes the game when the Joker is duplicated ç-ç
Another question, how can I get the least played hand? I know how to get the most played but not the least played
You loop through k, v in pairs(G.GAME.hands) do and check which one has the lowest v.played
ill try to figure that out
im not too versed in lua
but it cant be too different from java
and im pretty good at java so
Which Joker specifically?
akyros
gonna check it out too
whats your code
G.GAME.dollar_buffer = (G.GAME.dollar_buffer or 0) + card.ability.extra.dollars return { message = "$".. card.ability.extra.diamonds_count .."", colour = G.C.MONEY, extra = { dollars = card.ability.extra.dollars * card.ability.extra.diamonds_count, func = function() G.E_MANAGER:add_event(Event({ func = function() G.GAME.dollar_buffer = 0 return true end })) end
is this good for granting money on trigger because it doesn't seem to do anything. I know I can use ease_dollars() but doing that has this weird delay with the messages and the money is gained instantly and I don't really like it, any suggestions?
nvm there was an issue I missed, this works just fine
Here's the code from the original shakecard function, after changing the name to mod_shake card:
function mod_shakecard(self) --visually shake a card
G.E_MANAGER:add_event(Event({
func = function()
self:juice_up(0.5, 0.5)
return true
end
}))
end
If you add your code between three backtics on each side, it'll auto format! ` these thingies
Self in this case is going to be empty! I see what you're trying to do, but just shaking a card luckily doesn't need all of this shell code around it. You can comfortably replace every instance of mod_shakecard in your code with a direct call to juice_up()!
Could you link the tutorial your following btw? I'm wondering if it might be older!
From July of this year:
Welcome to the second edition of Balatro Modding 101 tutorial.
Steammodded upgraded their logic so i re-made this tutorial!
In this beginner-friendly guide, I'll walk you through the steps to get started with modding in Balatro. Whether you're looking to install mods created by others or dive into making your very first mod, I've got you covere...
'''function mod_shakecard(self) --visually shake a card
G.E_MANAGER:add_event(Event({
func = function()
self:juice_up(0.5, 0.5)
return true
end
}))
end'''
Like this?
No, haha, those are quotation marks. The backtic (`) is next to the tilde (~) on your physical keyboard, or on a phone probably also next to the tilde!
oop
No worries, probably the most common mistake made, haha
Are you familiar with VanillaRemade, by the way?
No, I don't have too much coding experience, I just love Balatro and wanted to make my own mod so I'm learning as I go
So it should look like this?
wait
wrong section of code
this one
Then VanillaRemade is an even better tool, haha. It's a detailed and documented mod that just reimplements every vanilla joker, blind, consumable, etc, in the smods framework, so it's amazing reference
But yeah, I'm not sure why you'd need a separate event call to shake the card, as juice up accounts for that. I'll grab a link to vanillaremade, and try and find an example of juice_up being used in a joker there!
It's now crashing because of where I'm putting the backtics I guess? I'll take a look at VanillaRemade and try to recreate from there
Oh, wait, sorry, I'm fully misunderstanding, haha
no, the backtic aren't necessary to add to your actual code!
They're for when you share code on discord!
OH
G.E_MANAGER:add_event(Event({
func = function()
self:juice_up(0.5, 0.5)
return true
end
}))
end```
so it looks like this, which is a bit easier to parse, haha
ok i also misunderstood, my bad
can i still get that link? a quick search doesn't seem to pull up what im looking for
Oh yeah sorry, haha. I got distracted way too quickly
im doing smth wrong here and idk what
https://github.com/nh6574/VanillaRemade
Here's the main repository! YOu can check out the wiki at the top for probably the most detailed guide on getting started with Balatro modding
Thanks! I'll try and report back!
return { dollars = card.ability.extra.dollars }
ill try it
https://github.com/nh6574/VanillaRemade/blob/main/src/jokers.lua#L442
This is the code for the reimplementation of Ceremonial Dagger. If you're very new, it might be a bit daunting, but main thing is that, as you can see. The juice_up in these cases are all called in events, but that is to make specific animations line up. Having a generic, separate event for juice_up can be a bit cumbersome for when you're stacking other visual animations, as they would each happen separately, while you may want them to happen all at once!
Also, other_card should be context.other_card.
got it got it
Also, having a look at this, I appreciate the creator's efforts, but man I don't think that's a very beginner friendly tutorial at all. Lots of stuff that's very out of the norm for what most people do, meaning it'll be a bit harder to get help with it, and a lot of prewritten code that just doesn't get explained at all
ah
how can i make this not happen
You can set it up to only return something if the value is > 0
got it
So, the only thing I'm unclear on is how to show the assets of jokers
I have the asset made, in 142x190, and 71x95
Former goes under assets/2x, latter - assets/1x.
The game automatically switches between the two in vanilla.
i did that... let me try a different method rq
Did you also set them up with an Atlas?
ok so i DO need to do that? there is not an atlas in VanillaRemade
Yeah, for new assets you would need to!
VanillaRemade uses the existing art, though I get why that's kinda confusing
DOG COME ON
i dont understand what the issue is, it's the EXACT code format from vanillaremade
may i see the code
key = "test_stoat",
path = "j_test_stoat.png",
px = 71,
py = 95
})```
```SMODS.Joker {
``` key = "stoat",
pos = { x = 0, y = 0 },
rarity = 1,
blueprint_compat = true,
cost = 1,
config = { extra = { chips = 10, mult = 2 }, },
loc_vars = function(self, info_queue, card)
return { vars = { card.ability.extra.chips, card.ability.extra.mult } }
end,
calculate = function(self, card, context)
if context.joker_main then
return {
chips = card.ability.extra.chips,
mult = card.ability.extra.mult
}
end
end,
}
end```
that end in the last line shouldn't be there
do seals' calculate functions not run when theyre held in hand?
because this is just not printing anything
try context.main_scoring
Yes, context.other_card doesn't exist in context.main_scoring
so do i loop through G.hand?
No, it's only called once on the card.
Dog I'm gonna crash out.
fuck im actually stupid 🤦♂️
solved i think
i dont even need a check im actually so stupid
agbilrgargbu thanks
my brain is not spinning
is there not an in pool function for seals?
can someone explain what's wrong with this????? the name and description are not showing up..........
it should be at descriptions.Joker.j_prefix_key
I'm so sorry I'm new to this whole thing. What does that mean
return {
descriptions = {
Joker = {
j_prefix_key = {
name = "Name",
text = {"text"}
}
}
}
}```
No, there is.
i am forever in your debt.
yeah it's not documented but it should work
does anyone know what part of the code hides the shop when opnening a bosster/redeeming a voucher etc.
The , next to the end is wrong
, is used to separate table contents
youre missing an end to the calculate function
i also reccomend you get the lua lsp plugin
it can show you grammatical coding errors and warnings alike
which one is it?
there are a bunch of lsp plugins
mostly for roblox
got it
btw if I just want to level up the played hand what is supposed to go in the first argument of smart_level_up_hand
just using card causes a nil error
not sure WHAT card im supposed to target
how can i change the position of the effect description (it shows up above the card and i want it to be to the left)
can you explain why? thunk made the effect descriptions appear in certain orientations depending on the card position for a reason
because i have a custom card area
that is above the deck
and when i hover the cards in the area
the whole description is not visible
because it comes above the card
Ah, fair enough
hook Card:align_h_popup
thank you
what are you trying to do
there are no highlighted cards in context.before
level up the least played hand (before the hand is scored)
im checking vanillaremade code for reference but that only has level_up = true and its not helping me
some code as example https://github.com/larswijn/CardSleeves/blob/main/CardSleeves.lua#L2121 in case you need it
to level up another hand you can do
return {
level_up = true,
level_up_hand = key
}
but i dont see the logic in your code to get the least played hand
@wicked leaf
the point of the joker is "When a hand is played, level up the least played hand"
im just really confused cuz im just copying code to loop through all the hands and check that the one played is less than the rest
can i see the code?
so not use SMODS.smart_level_up_hand?
I already deleted it cuz my head is hurting
you can use either but that one is easier
the problem is that SMODS.smart_level_up_hand has a "card" argument
why would you need that
what's the point
i dont know what to put there
the card
WHAT card
the card provided in the calculate function id assume
you dont level up cards
it's for animations
i think its just for displaying messages? im not sure i havent used it
I tried doing this but it crashes my game
im gonna try again
this is like. my first or second time trying balatro modding and it confuses me
yep, crashes
isnt context.before the context right before scoring?
yes
idk what im doing wrong then
can you post the full log
woah
context.before is after the cards are moved to G.play
use context.scoring_name
instead of text
ooo internal documentation
kinda sucks that I couldnt find any of these in the current wiki
they are there
im working on a mod rn and i need more colors for the desc what are all the colors pls i need ts
couldnt see them in the Joker or Pokerhands section
just check everything in G.C
Joker is usually where the contexts are
or use HEX("hex code value")
thx
alright im gonna try to use the code from obelisk to make the actual effect
there's an example for most played hand on the vanillaremade wiki
is there a mod that allows you to reload the game so you dont have to turn it off everry time you change something?
I thought changing it from >= to <= would make it the least played but apparently not
I am stuck again
how would i make it so a sticker ignores whether the card its applied on is debuffed or not?
no
you can restart faster with alt+f5
oh nice thats exactly what i needed
because obelisk doesn't get the most played hand, it checks if any hand was played more
like i said, there's an example in the vanillaremade wiki for most played
I checked it but I didnt have an idea of how to implement it
I think I do now, hold on
is there a way i can change a tarot's "effect" value without messing with its config? here i changed card.ability.max_highlighted to 1 but that still doesn't limit it to just one card
i'm assuming it has something to do with card.ability.consumeable? if i change card.ability.consumeable.max_highlighted to 1, it works, but it's then gonna be like that for every future card :/
I recommend you check cryptlib
it should have value manipulation stuff
Can I pin a joker but to the right instead of to the left?
not by default, you would need to recreate what the pinned sticker does
I see, alright
(Cryptlib is just cryptid features in a library separate from cryptid)
card.ability.consumeable = copy_table(card.ability.consumeable)
card.ability.consumeable.max_highlighted = 1
oh wow that works, ty! i dont fully understand why rn but i will look into it soon
It's because card.ability.consumeable is the config of it's center.
is it possible for another joker to prevent a joker from triggering
you can debuff it
i guess
i wanted a joker that either retriggers a joker or causes it to fail entirely
Last time I asked I don’t think there was, but that was months ago
though tbh that'd also probably cause a lot of crimson bean-esque bugs
I play mobile, but plan to buy the steam version pretty soon. I know LUA programming pretty well. How do you recommend I get started modding?
jokers that sorta "prime" themselves in context.before for example could break
might be a better idea to just not do it
you can apply a flag like debuffed without visuals if it fails and use if context.retrigger_joker_check and not context.retrigger_joker then if it succeds
as a side note, you need to enable retrigger for your mod if you haven't already
SMODS.current_mod.optional_features = function()
return {
retrigger_joker = true,
}
end```
i have
i know cryptid's repulsor boss blind does something similar but that's also cryptid and i would ideally not want to reference cryptid code
Does balatro have a mod loader at this point in time?
smods
thanks. and a modding api?
also smods
how would i place this button behind the card
if context.post_trigger then
EMPTY(context.other_ret)
end
last time i tried modifying context.other_ret it seemed to just do nothing
Where you just setting context.other_ret to something?
it was for a cryptid joker that makes chips jokers use the next highest operator, so i was trying to do stuff like replacing chips with xchips
Some jokers use chip_mod
i found a way to do it with an SMODS.calculate_individual_effect hook that works better anyway though
am aware
Why is context.money_altered triggering an inconsistent number of times?
i've posted about my hate for _mod returns in #⚙・modding-general before
It triggered once, than twice than five times.
(for those wondering, it's because it allows it to effect editions and jokers that directly call SMODS.calculate_individual_effect or SMODS.calculate_effect)
is EMPTY an existing function or do i have to make that myself (i'd know how to make it, just want to know)
Yes, it's defined in misc_functions.lua
Odd request, but:
How do I randomly spawn a Joker between 2 mods?
local jokers = {}
for k, v in pairs(G.P_CENTER_POOLS.Joker) do
if v.mod and (v.mod.id == 'modid1' or v.mod.id == 'modid2') then
table.insert(jokers, v.key)
end
end
local joker = pseudorandom_element(jokers, 'seed')
SMODS.add_card({key = joker})
Thanks
how would one replicate the visual effect of Card:start_dissolve without actually destroying the card?
Copy the function and remove this:
is this how i do boss blinds debuffing
No, you use context.debuff_card
could you give a example
trying to get the least played hand, but the card seems to be triggering with all of them
calculate = function(self, card, context)
if context.debuff_card and (context.debuff_card:get_id() == 2 or context.debuff_card:get_id() == 3 or context.debuff_card:get_id() == 5 or context.debuff_card:get_id() == 7 or context.debuff_card:get_id() == 14) then
return {debuff = true}
end
thx
You're checking if any hands have been played less than -1 times.
well yeah, that's what it says to do for the MOST played
instead of > im doing <
theoretically that should do the least played
You need to change -1 to math.huge
I guess I can also do the least played or 0
strange, but I'll try it out
you want to start at a play count bigger than every other hand
ohhh i see
UNLESS its been played 0 times so
give me a second
just add a check that hand.played ~= 0
should it be AND or OR
and
maybe this'd do?
since you want to check if its less played than the current hand AND if its not 0
yeah
I mean I want it to trigger if the number IS zero
huh
Otherwise you're gonna spend all run trying to trigger the card
wdym by this then
I want it to check that its the least played hand
unless its zero, in such case, it triggers regardless of it
so
So it upgrades every hand that is not played if no hands have been played?
isnt that just the equivalent of checking for the least played hand?
or do you not want to trigger it off of a tie for least played otherwise
since by definition hands played 0 times are tied for least played
I do want it to trigger on tied cards yeah
but im not sure if it'll do it with this code
ill test it
oh wait do you mean you aren't counting unplayed hands in this so if a hand has been played 1 time and theres unplayed hands its still fine?
ok so basically hold on
It wont because it only chooses 1
I want the least played hand to be valid, EVEN if its tied with others