#💻・modding-dev
1 messages · Page 189 of 1
dont you need to check for each card?
calculate = function(self,card,context)
if context.individual and context.cardarea == G.play then
if context.other_card.ability.name == "Mult Card" then
stg.mult = stg.mult + increase (whatever your increase by value is, it goes here)
elseif context.other_card.ability.name == "Bonus Card" then
stg.chips = stg.chips + increase (whatever your increase by value is, it goes here)
end
end
if context.joker_main then
return {
chip_mod = stg.chips,
mult_mod = stg.mult
}
end
do that
? doesnt that happen utomatically
i think you need context.individual
calculate = function(self, card, context)
local stg = card.ability.extra
if context.joker_main then
return {
chips = stg.chips,
mult = stg.mult,
card = card
}
end
if context.cardarea == G.play then
if SMODS.has_enhancement(context.other_card, 'm_bonus') then
stg.chips = stg.chips + stg.dchips
return {
message = 'Bonus!',
colour = G.C.CHIPS,
card = card
}
end
if SMODS.has_enhancement(context.other_card, 'm_mult') then
stg.mult = stg.mult + stg.dmult
return {
message = 'Mult!',
colour = G.C.MULT,
card = card
}
end end end```
this should work
does that context loop the cards played?
context.individual and context.cardarea == G.play goes through every scored card
and context.other_card is the current card being scored in these contexts
ah ok
idk what your increase by value is, make sure you fill that in
i use :has_enhancement as well, does that change anything
idk how that works, i just check by ability name
looks to me to be the dmult/dchips
there's a bunch of ways to do it, whatever way works for you
the context.joker_main makes it actually give chips and mult
if it doesn't, then remove _mod from chip_mod and mult_mod in the return
sometimes it is and sometimes it isn't needed and I don't know why
Genuine question, have you taken a look at the Calculate Functions page on the SMODS wiki?
If not I would heavily recommend it
better yet
look at the example mods
for smods
https://github.com/Steamodded/examples/blob/master/Mods/ExampleJokersMod/ModdedVanilla.lua this one and the other mods in the directory
https://github.com/Steamodded/smods/wiki/calculate_functions here is for calc
It’s never needed
oh
You can use it if you want a custom message though I prefer an alternative way of doing a custom message
i tried that, i couldnt find any that had the same functionality
good point
just swap context.repetition for context.individual
if it's been needed sometimes, you've probably just developed for an old version of smods and upgraded. That's what it was for me 😛
Usually your best bet when looking at vanilla code is to ctrl + f the name of a card that does something similar
i used this a lot
I'm trying to find where the game sets the best hand for the current run. Is that just done in the save function?
if you're using vscode you can
- extract the balatro.exe into a folder
- drag that folder into vscode
to make a "workspace" with the balatro code in it
then ctrl + shift + f to search through all files in the workspace
oh, nvm, there's a function that's called check and set high score, should've used my eyes better
you can add other folders alongside it too
remember that balatro is open source modding
if you see something that does what you wanna do, go see how they do it
It’s arguably more useful to search through your lovey dumps
Honestly I'd rather have the code dump than an exe unzip
Lmao same mindset
.
i never really thought about that
but i'm going to keep doing it with the exe dump
because i'm used to it
have fun debugging patches 
i don't do patches
brave
--dump-all gives you the entire game and love2d source 😈
my mod has two patches and i dont need any more
even buffers that haven't been patched
i dont even know how to patch
What smods version are you on Marie?
-# technically it's 3, but the third one adds an end for the second one
lol
havent updated in a while
i have no idea
checking the exe isntead of the dump is a great way to accidentally try and inject in code that's patched out by SMODS, I found
I have almost certainly broken your patches 🙃
lmfao
like this week somewhere, haha
okay people
so what are patches used for? someone enlighten me
im actually installing vscode now
guess i'll go update grr
tl;dr change vanilla code or place your own code inside vanilla code
marie you should update smods so you can turn better calc into wetter calc
im tired of nil value i got me in my sleep
aight thanks
fun fact i know where it is erroring
im not even ironic
It actually made splash logic a lot cleaner to modify I just haven’t documented it yet 🙃
Imma be so real
all of this commotion made me forget I was dealing with stuff before we were so kindly enlightened
hah
suck it
my patch still works on latest smods
i downloaded the main branch with the green code button and it still works
i am gonna go test my other patch too to see if that works
i wonder when will i need to update my lazy talisman compat
Any clue why this is still paying out with nothing? The card text is perfectly fine, so like, we know the for loop itself is set up right.
I wonder when I can figure out the source of this damn crash 😭
where's that damn fourth crash emerald
Can you show the patch? I highly doubt it’s working I completely rewrote that part of the code
it should at least tell you the line number, right?
no it's working fine
Pls show patch
what was your error again?
[manifest]
version = "1.0.0"
dump_lua = true
priority = 0
[[patches]]
[patches.pattern]
target = "functions/state_events.lua"
pattern = "for i=1, #G.play.cards do"
position = "after"
payload = """
for k, v in ipairs(Splashkeytable) do
if next(SMODS.find_card(v)) then
scoring_hand[i] = G.play.cards[i]
break
end
end
"""
match_indent = true
i put all my jokers with splash effect in splashkeytable
and it checks if you have them, and if you do, every played card is scored
Is that pattern actually unique?
i'm pretty sure it is
That is incredible
indeed it is
It's probably some advanced stuff that's hard to make of but knock yourself out. tl;dr I made some patches to the code that finalizes scoring for one of my backs and it's causing this. Worked fine before I started adapting for Talisman
yup i dont understand shit
exactly lmao
Easy solution, don’t work around talisman
main.lua:1781: main.lua:1567: bad argument #1 to 'floor' (number expected, got boolean)
Stack Traceback
Man I wish
i found a different crash when testing my second patch
loc_vars is run when you hover the card, you should have that same logic in a calculate function probably
edit: or in the use function
context.individual is still having context.other_card == nil
put
both contexts
not just one
Screenshot your code for me
calculate = function(self, card, context)
local stg = card.ability.extra
if context.joker_main then
return {
chips = stg.chips,
mult = stg.mult,
card = card
}
end
if context.cardarea == G.play and context.individual then
if SMODS.has_enhancement(context.other_card, 'm_bonus') then
stg.chips = stg.chips + stg.dchips
return {
message = 'Bonus!',
colour = G.C.CHIPS,
card = card
}
end
if SMODS.has_enhancement(context.other_card, 'm_mult') then
stg.mult = stg.mult + stg.dmult
return {
message = 'Mult!',
colour = G.C.MULT,
card = card
}
end end end```
That’s not a screenshot
both my patches work
fine
And what’s the crash?
well, that wasn't how we were meant to insert it. ;P
context.other_card is nil in SMODS.has_enhancement
set local cardsCounted = 0 along with payout
oh! have to re-clarify the local variable, got it. ;P
Try making a local reference to it first
if you're using debugplus there's send_debug_message and also print
and balatro has a helper function built in for printing tables
print(tprint(table))
happens
whats the context for discard effects?
i should probably go to definition for context
thats not easy
anyone have a context list
there is one at the wiki
lmao im failing
#5xb core
-# #roblox upgrade tree references be like
should i use pre discard or on discard to retrigger discard effects
probably discard
OK
This doesn't seem to have worked. Can I have some help, or is retrigger on this step unfeasable?
holy crap it works
well done
My goofy ahh cards are scoring properly
yippee
nevermind I lied, they do not currently work in straights. But everything else seems correct
lmao f
wait there was a value i remember seeing
that made aces work in straights
Full house works
that seems to be the reason why smods doesn't have multi rank cards so it makes sense
I'll need to see if I can find a way to fix it
there are some ideas here https://github.com/Steamodded/smods/issues/449
because this is registering as a pair
but anything requiring flushes, n of a kind, or full house registers correctly
it's literally just straights it seems
I'll have to see how it checks for straights
TL;DR straights require a rewritten algorithm and we don’t have a cemented solution for that.
🇫
I don't actually touch the hand evaluation function at all
And committing just a version that works for everything else but straights is an incomplete feature.
remove straights from the game
and aces dont forget aces
🖤 🩶 🤍 💜
If I can fix this one issue though, the feature will be complete
you say that as if it's a simple issue to solve 🤣
slight lie. I need to patch all the rank-based joker functions but that should be as simple as adding a check if card has this enhancement
it's the main reason this feature isn't in smods yet
I mean, getting it to this point wasn't too bad
yeah you've done the easy bit
multiple ranks for a card
Unfortunately the most I’ve done related to calculating if a hand is a straight is via math equations, not any actual algorithm (which is required here).
god forbid
slightly bigger lie. None of the cards are scoring 💀
perhaps I was a little too optimistic
is it possible to get the current score parts
i want to make a joker that increases mult based on chips
plasma deck is crying
I've had some ideas about how I could try to iterate on my last proposal that doesn't work with quantum ranks and state it as a bipartite matching problem
that the balance one?
yes
I'm having an oral exam on graph theory in like 6 weeks time, so maybe this is good practice. that particular course doesn't give a whole lot of fucks about algorithms though so meh
what i mean is if the joker gives mult based on chips and plasma makes chips equal to mult, your joker would be absolutely broken in this deck
sorry
idk sorry
yeah
darned
you can access the current mult and chips as globals
mult and hand_chips respectively
I'm wondering if my straight problem might be a consequence of any changes smods makes to get_straight()
current get_straight in smods also has its issues btw, it just doesn't show with standard ranks
I assume that’s addressed by the get_straight PR?
the other thing that happened is I'm suffering the consequences of making this a O(13^n) algorithm... because it stutters when I select four of them in the same hand
I think I'm gonna need something better than this naive approach
There’s a bunch of examples in the issue you can look at.
I'll take a look
speaking of that the summoning algorithm for my mod is not the best performant so I need to sit down and think about it someday
I feel like I would disappoint my uni professors
In theory, I could bypass this because with 4 of them, the only possible hands you can make are 4oaK, 5oaK, or Flush 5 depending on what the cards are. So I can simplify the check to say if there are at least 4 of them in a hand, only check the cases where they all take the same value. However, that would have to be at the cost of not being guaranteed to be compatible with certain custom hands
actually it's even simpler. Because they will all take the same value, all I need to know is if there are 4 or 5 cards in the played hand, and if it is a flush
bom bom bom, badum badum
jimbo walked up to the lemonade stand
and he said to the man, running the stand
"hey!" bom bom bom
Holy aces
card.ability.extra.x_mult = card.ability.extra.x_mult + card.ability.extra.x_mult_gain
return {
message = localize{type='variable',key='a_xmult',vars={card.ability.extra.x_mult}}
}
end```
I've got a joker that scales off of Eris and the scaling works, but I can't figure out how to make it trigger a message when the mult increases. anybody know why it's not working?
can I use #SMODS.find_card('key') > 0 as a means of just checking if the player has a joker without needing to know where it is or anything about it?
yes
When the card fronts are put on the base card or enhancement art, is there a way to mask parts of them? To make them match transparency of the card base, if relevant? Without me writing a function to include sprite masks, I guess
using next(SMODS.find_card('key')) is a bit more efficient, but doesn't matter too much . either works
how do you hook a function that runs attached to stuff
like Back:apply_to_run() i can't get it to hook the way I hook any other function
wdym
how you hook a function doesn't depend on how it runs
it doesn't make sense to me
has anyone made this joker yet? seems so obvious. made this one rather quickly
it doesn't recognise that the function i wanna hook is a function if I do it the normal way
sugar?
local old = Card.get_id
function Card:get_id()
return old(self)
end
iii see
syntactical sugar, two things that are computationally the exact same
i've thought of making it but never did cuz i thought it was too similar to blackboard
its literally the inversion of blackboard
ortalab
jojokers has that but for the played hand instead of held in hand
ah
I suspect that every suit or rank specific joker has every modded counterpart
ive just had a cool idea for ajoker
that sprite is almost exactly the same as the ortalab one too 🤣
negative +mult and *mult
negative +mult is -mult
damn
Negative Xmult is an instant loss, basically. Though I guess Blueprint'd solve that
not really
you can put the joker leftmost and offset it with +mult xmult
negative xmult is only super detrimental if your xmult comes from playing cards
-2 X 3 is still a negative number, just as 3 X -2 is
10 mult X -2 = -20 mult
-20 mult +30 mult = 10 mult
and you're right back where you were
Yeah, the joker does -25 mult then x3 mult
It's kinda like a risk reward
-25 might be a bit much
nah i'd keep it as -25
sans?
bro even game and watch pixels are too hard 😭 https://awoo.download/__XLivI1n.png
Fr*nch for without
go check
Is there any way to check in-game or will I have to code surf
code surfing is faster anyway
not for me,,,, for the people,,,, for the world ✨
I have had another interesting joker idea
Should I have sent that here? Probably not this isn't hashtag general
isnt game and watch like, not pixel art?
wrong number of arguments to insert?
table.insert{ace_high, hand[i].base.id}
() not {}
I genuinely don't know how I managed to do that
it's the only insert statement I did that for too...
how do I get the type of booster pack in context.open_booster
time for round 2 of bare-knuckle boxing a simple concept for a deck 
you can check G.STATE or literally check the name of the booster it seems
context.card should work
ideally I wanna use the method that will encompass custom packs too
like dx tarots' deluxe packs
of the same type ofc
All vanilla packs are technically custom packs
SMODS takes ownership of them to use SMODS.Booster methods so if it works for modded packs it will work for vanilla.
will the state check still work
assuming it's like if G.STATE == G.STATES.TAROT.PACK
Don't think so
damn
fair enough
You could but you can't click it
crud
Actually yeah it would according to the image
Because the tooltip ceases to exist when you hover away
:[
So if you tried to click on the button your mouse wouldn't be hovering over the Joker
That's why you have a second mouse plugged in
You can add a button like a Sell or Use button
that doesn't change anything
marie has two mouses for her two hands
Two mices, one to hover over the Jimbo, one to click his box
having two mice plugged in doesn't give you two mice on screen
not with that attitude
maybe you need to right-click them with cheese so they breed like in Minecraft
You have to acquaint them first, they're skittish creatures. But they're social so if you keep them both plugged in and check on them every once in a while, it should be fine
it crashed on me here
you wanna know what my idea was? something akin to the gambling machine in roblox not jjt upgrade tree, where you can select different risk-reward bets that will run when the joker is scored
it's TAROT_PACK
oh
i make that mistake a lot
woohoo it worked
now what's the value that says how many cards are in a booster pack
seems to also be here
so this is a deck I have
it's supposed to destroy one card from the played hand, then make 1 random card appear in your hand
it does this, but it leaves a ghost card behind, an empty slot that can be drawn as part of the deck into your hand, unselectable and taking up hand size
local destroyed_cards = {}
if context.after and context.destroy_card then
return{
remove = true,
}
elseif context.after then
destroyed_cards[#destroyed_cards+1] = pseudorandom_element(context.full_hand, pseudoseed('random_destroy'))
G.E_MANAGER:add_event(Event({
trigger = 'after',
delay = 0.1,
func = function()
for i=#destroyed_cards, 1, -1 do
local card = destroyed_cards[i]
if SMODS.shatters(card) then
card:shatter()
else
card:start_dissolve(nil, i ~= #destroyed_cards)
end
end
end
}))
G.E_MANAGER:add_event(Event({
trigger = 'after',
delay = 0.7,
func = function()
local cards = {}
for i=1, 1 do
cards[i] = true
local _suit, _rank = nil, nil
_rank = pseudorandom_element({'2', '3', '4', '5', '6', '7', '8', '9', 'T','J', 'Q', 'K','A'}, pseudoseed('incantation_create'))
_suit = pseudorandom_element({'S','H','D','C'}, pseudoseed('incantation_create'))
_suit = _suit or 'S'; _rank = _rank or 'A'
local cen_pool = {}
for k, v in pairs(G.P_CENTER_POOLS["Enhanced"]) do
if v.key ~= 'm_stone' then
cen_pool[#cen_pool+1] = v
end
end
create_playing_card({front = G.P_CARDS[_suit..'_'.._rank], center = pseudorandom_element(cen_pool, pseudoseed('spe_card'))}, G.hand, nil, i ~= 1, {G.C.SECONDARY_SET.Spectral})
end
playing_card_joker_effects(cards)
return true end
}))
end
end```
plz help I dont know whats wrong
the states method doesn't work for dx packs
my recommendation is to avoid destroying cards manually
the game/Steamodded can handle it for some timings
and how else can I go about destroying cards
use the destroying context
context.destroying_card ?
yes
okay
so the only case of that I can find is Sixth Sense and I can find 0 hint as to how it actually destroys the card, that code just talks about creating the spectral
read the wiki
Keep searching for it's effect label
check for context.destroy_card == [card you want to destroy], return {remove = true}
so I want my code to use this?
return{
remove = true,
}```
for the proper destroying part?
not context.after
context.destroy_card is passed in the destroying phase (which is not before, after, individual, or any of the other things)
so then just
return{
remove = true,
}```
is 10 jokers enough to publish a v1 
no not the destroyed_cards[#...] bit
<@&1133519078540185692>
you need to flag the card you want to destroy in an earlier context and then look for it in destroying
🔫
you seem to have a list of cards or something there i don't even get what you're doing with it
your code will destroy the last card in that list
(wherever it is)
but also based on your earlier code it's a local so it won't even contain anything by the time we get around to destroying cards
you'd have to persist it in card.ability or something
you probably want something like this
if context.before then
local card_to_destroy = pseudorandom_element(context.full_hand, 'seedherepls')
card_to_destroy.destroy_me_pls = true
end
if context.destroying_card and context.destroying_card.destroy_me_pls then
return {remove = true}
end```
^ although note that context.destroying_card only works for cards in play
which is fine in this case
yeah just possibly confusing cause i told him destroy_card lol
it'll actually perform marginally better as it won't check all the cards in hand too
well positive, its not making a ghost card
negative, its suddenly saying hand.chips is a table value, so I get to start hunting for that problem
is context.end_of_round and context.blind_boss a valid way to check when a boss blind is defeated
So I wrote a new extremely straightforward function to detect a straight that I've tested and am pretty sure works
local ace_high = {}
local ace_low = {}
local all_hands = {}
length = next(SMODS.find_card('j_four_fingers')) and 4 or 5
can_skip = next(SMODS.find_card('j_shortcut')) and 2 or 1
if #hand >= length then
for i=1, #hand, 1 do
table.insert(ace_high, hand[i].base.id)
end
table.sort(ace_high)
if ace_high[#ace_high] == 14 then
for i=1, #hand, 1 do
if hand[i].base.id == 14 then
table.insert(ace_low, 1)
else
table.insert(ace_low, hand[i].base.id)
end
end
end
table.sort(ace_low)
table.insert(all_hands, ace_high)
if ace_low then table.insert(all_hands, ace_low) end
for i=1, #all_hands, 1 do
local strt_len = 1
for j=1, #hand-1, 1 do
if (all_hands[i][j] == all_hands[i][j+1] or all_hands[i][j+1] - all_hands[i][j] > can_skip) then
strt_len = math.max(1, strt_len)
else
strt_len = strt_len + 1
end
if strt_len >= length then return true end
end
end
end
return false
end```
i have tried it and the code isn't running so I think it is a context issue?
woops wrong way around
Rocket should have the same check if you look for it
any idea why G.GAME.win_ante = G.GAME.win_ante - 1 would be setting the win ante to -1 instead of reducing it by 1
figured out i needed to enable a smod feature to use repetitions, finally works now
you dont need to enable anything?
you just use context.repetition instead of context.individual
he means retriggers
no
not for jokers
what do you mean not for jokers
-- enable additional SMODS contexts that can be CPU intensive
retrigger_joker = true,
}
he's retriggering jokers, not playing cards
oh
My custom function correctly identifies this as a straight, whichever one the game uses does not...
that's not a straight with those cards though
this is precisely why i wouldn't ever go near a card "having any rank"
what's alphabet soup ace
it counts as any rank
i assume it's a wild card for rank
which with four fingers and shortcut, this is a straight
I mean, I don't think the base game was built to handle multi ranks
sounds like an enhancement issue over a balatro straight detecting issue
So yeah it wouldn't detect it
Oh my goodness I figured out what was causing my crash 😭
The main discussion in the GitHub issues thread discussing multi ranks was Straight detection
where's a Straight-dar when you need one
i'm certain balatro's hand detector considers that card as its base rank
I wrote a wrapper around the base evaluate_poker_hand() to iterate through all possible values of the wild rank, and return the highest scoring hand
that took WAY too long
That doesn't sound efficient
I'm going to try patching this function over the smods get_straight() and see what happens
not at all, it's O(13^n)
it also isn't particularly relevant for the smods implementation of quantum ranks
well it's a pair so it's not like that
you have to do it if you want to maintain compatibility with custom hands
That's a separate issue
but yeah that sounds like a smoking CPU if you play a hand of 5 wild ranks
just to get to the conclusion that it's probably a flush five
It's not like there's only one algorithm that's custom-hand-compatible
are you hooking into the original function?
i disagree, only "try to make every permutation of this a hand" is compatible with "a custom hand can be literally anything"
it slows down if you play 4 of them, but with base game hands if you're playing 4 of them it can only possibly return 4oaK, 5oaK, or Flush 5
what was it
it needs to evaluate properly though still
would it not be like wild card calculations tho
does anyone know why my blinds keep showing up as "ERROR"? I haven't actually coded their behavior yet so I'm not sure what's wrong with it
a Straight detection algorithm only needs to detect Straights. Other hands have their own detection algorithms
for effects that look for poker hands being contained
And to detect a Straight you just need to traverse a graph
no no no no no no
I had suggested DFS
you can optimize the straight detection but you have no idea what other custom hands do from unknown mods that you're not testing with, so your only option is to pass in every permutation and see if any of them spit out a yes
if I play 5 wild rank cards, it should trigger every effect that is "poker hand contains X"
Still wrong AFAIK
if you play 5 wild rank cards it should go to five of a kind every time
Again each mod implementing a new hand would need to implement a way to detect that hand
also balatro collects every single hand included in what you played in context, so you have some legwork to do there
this tells me very little
i hate the effect
oh i see
that's genuinely unwinnable a lot of the time
Should it?
I don't know if it should trigger Full House and Two Pair
why not?
and also shouldn't contain a flush if it isn't one
They're the same rank
imo it should because there's a valid combination of ranks that forms these hands
a natural 4 of a kind doesn't trigger 2 pair
For proof, let the first card be a rank. The other cards are also that rank.
Therefore they're the same rank.
I think this is just up to personal choice of implementation at this point
counterargument, cards are considered one rank at a time
SMODS does not implement multi-ranks, therefore each mod is independently developed with the assumption of you get 1-5 specific ranks and they have suits as queryable properties. Most mods aren't prepared for your "wild rank" card. To use that algorithm, you have to pass in every permutation of the hand, substituting each wild rank card with each possibility.
That's irrelevant because I was talking about implementing multi ranks into SMODS
That's irrelevant, because I wasn't. 🙂
Then you're not talking to me
My thought on multirank cards is that while they can serve as any value card, they should only take one value when making a scoring hand
easiest and cleanest way to do it
Countercounterargument: define is_same_rank then define is_different_rank = function(cards) return not is_same_rank(cards) end
that being said if you play 4 or 5, they should prioritise the highest hand, so playing 5 always results in 5 of a kind
🤔
but equally, if you play 5 wild rank cards, they should trigger every joker that says "Do X when a Y is scored"
the effect isn't final yet, though really I don't think it's that much more unfair than a lot of other blinds i've seen
this was in fact the implementation I was going for
🤔
-# this is satire don't get me involved in your bickering please
this was also what I had in mind
balatro collects every hand that you played
in context
how would that interact with multirank
do you collect every possible resolution
the problem is that with an unmodified deck, you CANNOT win the blind
like it is physically impossible
But really, to me it makes sense that two cards can't be the same and different ranks simultaneously, by definition
that is my issue with it
Just like a card can't be all and none suits simultaneously
so if they trigger every joker effect, then the poker hands table should have all the lower hands too
in SMODS
for reference i have not thought about a possible implementation of "same ranks"
and therefore a 5oak would also be like a full house
yea, that's why it can't appear for the first few antes, that way you won't tend to get it while your deck is unmodified
I was going to have it count with every rank-based joker, like sixth sense, cloud 9, baron, shoot the moon, etc. But those joker checks should be as simple as adding an or has multirank enhancement to their conditions
what, so you can pull a spectral pack and get aura or pull an editioned card that plays a good hand that fits into your build from a standard pack
I mean at that point it might as well be patched to has_rank()
I still think the cards are the same rank, so by Balatro's definition aren't a Full House
3;3;3;(3,4);4 i would expect to both contain a 4oak and a full house, for example. i'm not sure how that plays into how these are checked with get_X_same
yea pretty much
or other ways to do it through mods
this argument boils down to whether or not cards lock in to a specific rank after the best possible hand has been determined
maybe it could be that non-editioned cards are debuffed but that would punish hands that have more than one and less than 5 editions
similarly a 3;3;3;(3,4);(3,4) is also both a full house and a 5oak depending on what the multirank cards are deemed to score as
they 100% shouldn't lock in
so you're telling me i have to go out of my way to prepare in advance for 1 specific blind with items that are only available in large content mods, the lack of which makes the blind unwinnable and extremely unfair and still luck based on if you win or find an editioned playing card even if you go out of your way to do it?
that's just bad game design
but if they don't lock in, poker_hands should have the full house and the 4/5oak
No, I think it boils down to the definition of "same" and "different" ranks
that would be inconsistent with how it works for suits
I will have to take a look at that function, but if smods changes all rank-based jokers to use that function instead of their normal checks then yeah I could literally just have it return true if I have this enhancement. or if this is a base function I haven't come across yet
Because I do agree multi rank cards should trigger every relevant rank Joker
But Full House and Two Pair specifically ask for different ranks
and the cards wouldn't have different hands
that would be so so so much better
hold on, does balatro specifically define full house as "three cards of the same rank, and two of a different same rank"?
that makes sense and ugh
i hate this because going by that you're absolutely right
thunk has talked about Two Pair not being contained in Four of a Kind
a wild rank card is the same rank as all other cards
@wintry solar actually this supports my opinion
every time I get the memory allocation failed error, I forget how to fix it
with separate interpretations of what rank is used, this hand both contains a full house and a 4oak
I need to just self-pin the answer
ok i'm annoyed because definition-wise, Victin is absolutely right, but it still doesn't intuitively make sense not to have a full house in the above example hands
memory allocation of the fix in your brain failed
but these cards aren't the same rank, they can function as any rank
what about for example a multi-rank hand of (1,2);(1,2);(2,3);(2,3)
doesnt that contain 4oak and two pair
lol true
And because they can function as the same rank, the intersection of the ranks they share is nonempty, and therefore they're the same rank, and not different ranks
we can't really base the logic off thunk's definitions of cards that have one rank only
The funny big number deck can finally go the distance omg 
Maybe not, but I think both interpretations seem logically consistent
why do you have to score that much on only ante 5?
basing it off of the logic of poker_hands my expectation for a general implementation of multirank cards is that every possible permutation is included in poker_hands
oh god
Like I said, funny big number deck
at this point, it's just at the discretion of whoever's deciding to implement it
Also, from the perspective of efficient hand calculation, iterating over all possible combinations seems like the least efficient approach, if that's a concern for Straight detection
But if it's gonna be in Steamodded, I think it's important for the community to discuss
this is true
I'm talking from a fundamental design point of view rather than how efficient an algorithm would be
efficiency is secondary, make it make sense first
But both make sense
At the end of the day I'm just trying to do it on my own for fun because I thought it was a cool concept, I hadn't known there wasn't already an implementation for it
ggnore
there's always the nuclear option
implement both, make behaviour toggleable on the wild rank enchant by a flag
¯_(ツ)_/¯
and my custom straight detection algorithm definitely isn't the most efficient, but it seems to work for what I need it to do for my own mod
I can't convince aure to implement multi rank without straight detection
do you think they're gonna implement two algorithms
honestly?
i think they're gonna implement 0
but it's an interesting thought experiment
I'm a supporter of implementing multi ranks without Straight detection
Because it's better than nothing
and multiple mods want to play with the concept
and people are inventing their own
so you just make playing them incompatible with straights?
Straights would keep looking at the base rank
ah ok
straights looking at the base rank is actually awful, see the above example
I feel like it would be more fun if they completely ignored base rank, but that could work
I can't even properly tell what the base rank is
That's an issue with the Enhancement design
the answer for me is that its base rank doesn't actually matter, because I wasn't going to let it score chip
plus it is very unintuitive from a player perspective
not with the concept
I agree it's unintuitive but the mods implementing it are incompatible with each other
i'd argue the draw of wild ranks is to make straights somewhat less unappetizing
I think making them compatible with each other is more appealing than the mods not working together
Also
it's less work for modders
i do actually have some time on my hands coming up, so chances are I'll figure something out
what is "smods lsp definitions"?
Still, at the very least I think the definition of "same" and "different" ranks should be opened up to the community
steamodded language server protocol definitions
so its autocomplete stuff for vscode?
more or less
how do I start using it?
checkout the PR's branch and refer to its description for how to use it
So I've changed the code to use that version of the context.destroying_card
local destroyed_cards = {}
local murder_card = {}
if context.after then
murder_card = pseudorandom_element(context.full_hand, pseudoseed('random_destroy'))
murder_card.destroyThisCard = true
destroyed_cards[#destroyed_cards+1] = murder_card
G.E_MANAGER:add_event(Event({
trigger = 'after',
delay = 0.1,
func = function()
for i=#destroyed_cards, 1, -1 do
if SMODS.shatters(murder_card) then
murder_card:shatter()
else
murder_card:start_dissolve(nil, i ~= #destroyed_cards)
end
end
return true
end
}))
G.E_MANAGER:add_event(Event({
trigger = 'after',
delay = 0.7,
func = function()
local cards = {}
for i=1, 1 do
cards[i] = true
local _suit, _rank = nil, nil
_rank = pseudorandom_element({'2', '3', '4', '5', '6', '7', '8', '9', 'T','J', 'Q', 'K','A'}, pseudoseed('incantation_create'))
_suit = pseudorandom_element({'S','H','D','C'}, pseudoseed('incantation_create'))
_suit = _suit or 'S'; _rank = _rank or 'A'
local cen_pool = {}
for k, v in pairs(G.P_CENTER_POOLS["Enhanced"]) do
if v.key ~= 'm_stone' then
cen_pool[#cen_pool+1] = v
end
end
create_playing_card({front = G.P_CARDS[_suit..'_'.._rank], center = pseudorandom_element(cen_pool, pseudoseed('spe_card'))}, G.hand, nil, i ~= 1, {G.C.SECONDARY_SET.Spectral})
end
playing_card_joker_effects(cards)
return true end
}))
end
if context.destroying_card and context.destroying_card.destroyThisCard then
print("destroy")
return{
remove = true,
}
end
end```
it's still making ghost cards though, and when I remove the function about destroying the card then it just leaves the cards as they are and it's only adding to the deck
trigger = 'after',
delay = 0.1,
func = function()
for i=#destroyed_cards, 1, -1 do
if SMODS.shatters(murder_card) then
murder_card:shatter()
else
murder_card:start_dissolve(nil, i ~= #destroyed_cards)
end
end
return true
end
}))```
you don't need this
what context can i use for a trigger like space joker? The source code just has it listed in else after all the other context checks so...
you're also completely mistiming your events
I tried cutting that and it just doesnt touch the played cards at all
check the wiki
context.after is after context.destroying_card
i think fundamentally, the system used for finding same ranks needs to change for a quantum rank implementation. I was thinking at a base level, there would be a function that returns a list of combinations by amount of same-rank cards, the rank and cards used
i did
so there's no way to destroy the card after the hand is scored?
figure out the murder card in before
it is after the hand is scored
context.destroying_card does just that
That's what I'm saying though. You can define "same" and "different" ranks to be mutually exclusive or not. Personally, I think it makes more sense if they're mutually exclusive. Other people may disagree.
Now that I think about it, maybe my one card that looks for different suits isn't working along that philosophy 
3 3 3 (3,4) (3,4)
in my eyes this should both scale pants and proc the family
yeah I'd need to change the logic in case all "all suit" cards are the same base suit
and I think if they count for the Family they can't count for Pants
if I apply your logic to suits, a wild flush isn't a spectrum-?
Yes
that seems unintuitive
Only if Spectrum is higher ranked than Flush
why does this text show as error?
Spectrum is assymptotically easier than Flush as the number of suits tend to infinity*
should be loc_txt
So it makes sense that Flush trumps Spectrum
okay, take a bunco spectrum
thank you!
but i don't see how it matters
I agree that those both should trigger in that case
I don't think a particular implementation matters
hands containing a hand doesn't depend on the hand being higher on the list
I dunno about this whole same vs. different thing, I mean Seeing Double could make a strong argument against it; "contains a club card and any other suit", works with a wild card and a club but they're the "same suit"
if I have a 4oak with two extra same-rank cards, that is scored as a 4oak but it still contains a two pair
From my perspective, if you look at two cards, their either have the same rank or they don't
In a multi rank world
You take the set of ranks from each card and look at the intersection
It's either empty or not
It can't be both
that means having the same rank is not transitive
(2,3) (3,4) (4,2)
The alternative is allowing two cards to both be the same and different ranks
Neither is intuitive IMO
these cards are pairwise the same rank by your interpretation
of course
yet they're not all the same rank
But you also understand this
(3,4) (3,4) are the same and different ranks simultaneously
yes, this is also true of suits
actually this is totally intuitive to me
take a club and 4 wild cards
I guess if vanilla suits work with that logic it's sufficient to convince me SMODS can take the same approach for ranks
a wild rank card is both the same rank and different rank as another wild rank card, but also not not the same rank and not not different rank
where in the game files can i find the music?
they score as a flush, meaning they are considered the same suit for this purpose
But philosophically I'm still unconvinced either alternative is more consistent
seeing double considers any of the wild cards a non-club as well, so they're different suits as well
a hand of 5 wild cards is a flush that triggers Seeing Double in vanilla
baliame mentioned it above
why are you acting like I can't read
wait nvm i found it
no i'm just tired and slow
that was probably aimed at me /shrug
both of you
idk I kinda just put it together in my head and got excited
ye but you had said it and I had reacted to it
oh i just glossed over this lmao
mb
if you repeat it to me, and especially if you and aure do it it sounds disrespectful
I can offer you nothing but my word that it's not intended that way lol
same
'tis fine
such is the way of written communication in a fast moving environment
I was just annoyed in the moment
i'm just loud and obnoxious ¯_(ツ)_/¯
Again I think both approaches are self-consistent and thus can be intuitive
Mathematically, I'd see which is more useful. But this is a practical matter
also just keep in mind I agree with following the Seeing Double standard for ranks
I think intuitively for a card to trigger multiple joker effects based on it's quantum ranks, it can't "lock in" to only 1 rank during scoring
I think the way to look at is vanilla never considered the possibility of wild ranks but specifically implementing it in the way where is_same_rank() is true if the intersection of covered ranks is non-empty and is_different_rank() is true if the intersection of covered rank is not equal to the full set of ranks of either card would be the closest way to act like vanilla wild suits
and then efficiency is secondary
It's not about locking a rank
It's just set theory
and/or algebra
I defined it with set theory but the concept of equality (or equivalence classes) has things either be equal or not
I dunno man, set theory and algebra is fundamentally not an intuitive way to approach a game that has a self-imposed limit of 4 lines of text per card to minimize having to think about it
I mean you don't need to go that deep
I just did to justify it mathematically
Other people agreed with me and I dunno if they looked that deep
local destroyed_cards = {}
local murder_card = {}
if context.before then
murder_card = pseudorandom_element(context.full_hand, pseudoseed('random_destroy'))
murder_card.destroyThisCard = true
[...] (create new card code, working perfectly)
end
if context.destroying_card and context.destroying_card.destroyThisCard then
print("destroy")
return{
remove = true,
}
end
end```
So I've set the context to before but the destroying_card thing refuses to proc no matter what combination of context.destroying_card, murder_card, and destroyThisCard I use
oh I bet this is a jank ass old smods build issue
I re-upped my build like 2 days ago
What's the best mod to test compatibility with, Cryptid?
no
ideally dimserenes full modpack
cryptid is probably the least compatible mod out there
that's the point
not true
cryptid focuses on cross-mod content/compatibility quite a bit
we need stickers to reply to questions like "read the wiki please please please pl" and "old ahh jank smods" and "oops you're right it's undocumented"
it does but it's so easy to step on eachothers toes until we have a stable version lol
that's a good one too

oh it's picking from an unscored card if it's not working
(if that's intended you can turn on SMODS.optional_features.cardareas.unscored = true )
it's not working on Straights
it 100% works on straights
I wish you were right
whatever you're running is working on straights but mine is not
I literally copied your code into my joker
and added a print to show what card it chose
(how do I record balatro)
windows?
where is your effect coming from?
calculate = function(self, back, context) is it a cardback?
I do Win + Alt + R to record applications
never noticed prtsc can record ngl
it can?
anyway here's it not working on a straight
it's a cardback
lemme take a look
yeah theres a little picture vs video toggle thing
it would appear that decks are never passed the destroying contexts
WAIT I MAY HAVE FOUND IT
oh
nvm
i thought i had solved some big riddle when i realized that the "make new card" thing return true-d and maybe that was just shutting down the whole thing
so like, decks can't destroy cards?
I physically cannot make this deck?
you can always patch in the functionality
for some reason, despite identifying the correct scoring hand, none of the cards score when played... So I will have to look into this
at least I know it only happens when my hand contains one of these cards
why must everything I do become an ordeal 😔
okay how do I start going about doing that
nah it's just been missed on the smods end
give me 5 minutes
5 minutes was a wild over estimation
it works now
yup
eremel the goat
🐐
you really are a 🧙
how can i check if a card is a certin enhancement
SMODS.has_enhancement(card, key)
is there a way to make a print of a table not give the "+x more values" and actually give me the full thing
I'm new to modding in Balatro and need some help with this Joker. I'm trying to make it retrigger all lucky cards, but retrigger twice if their lucky effect triggers. I've tried various different ways but nothing seems to work. This is the last thing I tried. It seems to only retrigger them once.
you're checking for context.individual inside context.repetition
function BALIATRO.tprint(tbl, indent, max_depth)
if not max_depth then max_depth = 1 end
print(BALIATRO.tprint_i(tbl, indent, max_depth))
end
function BALIATRO.tprint_i (tbl, indent, max_depth)
if not tbl then return '<nil>' end
if max_depth <= 0 then return tostring(tbl) .. " <too deep>" end
if not indent then indent = 0 end
local toprint = string.rep(" ", indent) .. "{\r\n"
indent = indent + 2
for k, v in pairs(tbl) do
toprint = toprint .. string.rep(" ", indent)
if (type(k) == "number") then
toprint = toprint .. "[" .. k .. "] = "
elseif (type(k) == "string") then
toprint = toprint .. k .. "= "
end
if (type(v) == "number") then
toprint = toprint .. v .. ",\r\n"
elseif (type(v) == "string") then
toprint = toprint .. "\"" .. v .. "\",\r\n"
elseif (type(v) == "table") then
local ret = BALIATRO.tprint_i(v, indent + 2, max_depth - 1)
if ret then
toprint = toprint .. ret .. ",\r\n"
else
toprint = toprint .. '<nil?>,\r\n'
end
else
toprint = toprint .. "\"" .. tostring(v) .. "\",\r\n"
end
end
toprint = toprint .. string.rep(" ", indent-2) .. "}"
return toprint
end```
that is a combination of context values that does not occur
I use this when I need deep detail
there's already a tprint
I've tried without it and it still only always triggers them once.
xd
but I reserve my right to feel offended
btw despite the fact that this meme was created because of an undocumented function in smods, it's still not documented xdd.
Anyone wanna take a guess before I commit it to the wiki?
something something force a game-over
is there a version of press_play that triggers on discard instead?
I had to patch that in
I'm really praying this is like a mutability issue when I'm iterating through the possible rank values, or something
but I did not look in the docs so I might be about to die
actually that's not even true i literally patched it in because it wasn't in the docs lol
i've been looking but I can't find it, then again i still don't know if there's actually a list of everything you can do in the docs
cause if so I don't know where it is
the list of everything you can do can be easily found with ctrl+shift+f "function"
first row complete
where do I do that search?
everywhere
there's a discard_cards_from_highlighted function, maybe that works
i love baliatro
the alternative is, maybe there's another function I need to patch
it does not
how did you do that? i cant seem to find an alternative
[[patches]]
[patches.pattern]
target = "functions/state_events.lua"
pattern = "-- TARGET: effects after cards destroyed in discard"
position = 'after'
payload = """if G.GAME.blind and G.GAME.blind.in_blind and G.GAME.blind.config and G.GAME.blind.config.blind and G.GAME.blind.config.blind.after_discard and type(G.GAME.blind.config.blind.after_discard) == 'function' then
G.GAME.blind.config.blind:after_discard()
end
"""
match_indent = true```
which works fine with hands costing discards
hey any specific location I'm supposed to do that or can I just slap it like, at the start of calculate
I mean you can just put it in your main outside any function and it'll just be enabled
does this work anywhere in main then?
okay so for some godforsaken reason when i use a familiar card context.cards is a boolean value instead of a table of added cards. how do i idiot proof my code so the game doesn't crash when that happens
@wintry solar is that actually a thing or
it is interesting that there doesnt seem to be a way to just copy what the serpent does
well, yeah, no "officially supported" way
you can still just override G.FUNCS.draw_from_deck_to_hand or patch it
I don't think there's ever a context.cards
well when i add a card from a standard pack this works perfectly fine
SMODS.calculate_context({playing_card_added = true, cards = cards})
end```
yea i'm in that context
I am blind
and now the Deck of Theseus can set sail! thank you so much @wintry solar and @humble girder (and the others who've put up with my nonsense, those are just the 2 rn)
the thing is, it's weird, even in vanilla:
I don't know what was the idea behind this but yeah that isn't a particularly useful context bit
I guess just check that the value is a table first
nah sometimes you do get the cards added
ALL other sources pass one true for each card created
standard packs did seem to be working tho
That should probably always be passing the cards since true doesn't tell you the card type.
why the actual fuck is it coded like this
well isn't that a load of vanilla jank
This is def LocAtlHunk code
boutta patch the context so it's not stupid and useless when using specific spectral cards
ah i see why vanilla does it
not the easiest thing to patch
cards are created in an event but playing_card_joker_effects is called outside the event
effectively, before the cards are even instantiated
ballsack
not actually sure why it has to be that way tbh
am i gonna have to hook into the card creation func
this was meant to be an quick and easy joker to make 😭
don't hook create_card, you'll surprise literally every other mod
yeah looks like marble and certificate are outside the event
I'll sort this out on the smods end, dw about it
thx
Card destruction also works like this
Sometimes it passes the card
Well I think it was inconsistent
Even though Canio exists
Maybe I’m confusing with adding cards @_@
I should go to sleep
#1209564621644505158 message hey you literally modified the wiki after I said this
it doesn't seem to like this, am I doing the lua wrong or did I mess up the toml?
ah the screenshots are slightly mismatched but it throws the same error regardless of whether or not there's the preamble to the function
does anyone wanna check if the release version of my mod works for them 😭
i can check it later tonight, what do you mean by work
like it boots and doesn't instantly and akwardly crash or smth, thank you
would you not be able to figure that out yourself? or is it to test if it's somehow different for other people's systems
i suffer from severe "works on my machine" syndrome so, yeah that.
why? what is this error never seen before
i might be wrong but I think you only need to define "after_discard" on your object, not "G.GAME.whatever"
did you remove the ()
but maybe I need to add a different thing in front
hm
I think so but i should check again
yea it still breaks
you're also missing a comma
oh well that would explain it
oh wow it really was that easy
I for some reason didn't think there needed to be one there
the error message looked suspiciously "missing a comma" but i wrote it off as some other formatting issue
a browser screen? or does this actually play
actually plays
oh then thats very cool
yeah
ok so i genuinely need an example of a joker using generate_ui
because i dont know what the hell i am doing at this point
😭
is that the mobile version?
ballsack
i mean i can give you the one in my mod but it's kinda messy
i used love.js and had to change a ton of shit in both the game and love.js
if you tell me what you want to do i could cook up an example
nah desktop
nah it should be fine honestly, i dont even know where to start with this 😭
screensharing balatro, recreating balatro, or embedding balatro?
i just added the icons to the menu
the game plays but it still doesnt seem to do anything
hmm, can you basically tell me how to make the description, name and allat rendered?
oh wow thats neat
please dont murder me localthunk im not releasing it
man i both love and hate uis, they seem so convenient if you understand how it works but it looks like freaking black magic
hes offline, so hes definitely rapidly approaching your location
u r cooked 💔
😔
oh yeah the only difference is that rng isnt identical to real balatro
so like seeds and stuff arent the same between normal and web
oh i was writing something but i realized i dont understand your question lol
it doesn't render at all?
oh no no, but like currently, theres an empty white box
and i kinda dont know how to add stuff to it rn
so if i understand the basics of how it works, it should be nice
if you want to edit the main description and not the info tooltip then you check desc_nodes == full_UI_table.main
desc_nodes.name contains the UI table with the name
desc_nodes itself contains the description
you want to add stuff to desc_nodes by using localize
oh i see
i feel like the easiest way to start is to call SMODS.Center.generate_ui inside your generate_ui function and printing what it gives you
and then see how you want to alter that rather than making it all from scratch
mmm i will do that and see what i can work with, thanks :D
i will be more specific, not what generate_ui returns (because it doesn't return lol) but what desc_nodes looks like after calling it)
oh so do i just do print(desc_nodes) and take a look at it
(i feel like it would just be nothing)
yeah its probably going to be a bit of UI nonsense but you can work out the parts
it will be a table with the name and description tables
Question, For deckskins do I need to separate each set of cards into different image?
delphox spotted
i've not done deck skins but i dont see why it wouldnt work like any other atlas
trying to port from deckskinslite
ok i just did this and desc_nodes doesn't do what I said lol
it's full_UI_table
how would i add a card into balatro along with modifying it?
SMODS.Joker
huh
wait, what kind of card are we talking about here
basically this adds hello to the end of the box
alright thanks
if you are confused where to start
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
-----------------------------------------------------...
its a bit outdated, but it should tell you the basics ❤️
very nice, if i want to add into the name then id need to do desc_nodes["name"] instead, i suppose?
yes
if desc_nodes == full_UI_table.main then desc_nodes.name is going to be a UI table
if it's not main (an info tooltip) then I think it's just a simple string
wait is pixel art smoothing absolutly needed for it to work properly?
why are none of these guides pinned?!
Do you not have google? Obviously a bad take, i will drink more coffee in silence
because im currently using aseprite and i dont know how to make it double the size without fucking it over
when you export, isnt there a setting to export it x2 size?
im using trial so idk
Thats not the point
does this work with the trial?
idk
aw man
It would make it a lot easier to just have guides like this pinned so modders can just come here and get what they need
instead of doing 20 minutes of searching and trying to ask more experienced modders for help only to be ignored
for a single issue
yeah so uh
i tried changing desc_nodes.name to both a string and an ui table
doesnt seem to change anything
it doesnt work with the trial
oof
i hate being broke T-T
what's the code
definitely fucking up something but here, this is what i tried for the string
Sorry for my snippiness. I agree that the pins should contain more helpful docs, though they should probably be to community-managed wikis rather than old YouTube vids that will become less relevant over time
does anyone have the link to paint. net website?
as for the part where i changed it to an ui table, i did the exact same with the desc_nodes above, was i supposed to do it like that
isnt that
already in the name
try changing full_UI_table.name
oh nevermind, i remembered it wrong
You should definitely just use google. But it's www.getpaint.net
no because when i tried it , it was a dead domain
here
aight hol on
thanks guys
this is how I add the card types to the name
i love acheron
💔
