#💻・modding-dev
1 messages · Page 195 of 1
got it
had to make a lovely patch
Yeah but I can’t imagine the general populace are working on a PR version of smods 🤣
What were you trying to do?
why are stickers so tiny >_<
how do I redeem a voucher from a joker
You make the card and then use redeem on it iirc
i tried looking at the code for like housing choice and idk if its just the way betmma codes
i cant understand it
😭"
is there a template shader somewhere to start off with as a starting ground for all other shaders?
all of the resources are cool and all but they are not helpful at all if you have no idea wtf you're doing like me LMAO
oh i guess i see one
ok works
Oops! The game crashed:
functions/common_events.lua:2486: attempt to compare nil with number
Additional Context:
Balatro Version: 1.0.1o-FULL
Modded Version: 1.0.0~ALPHA-1424a-STEAMODDED
LÖVE Version: 11.5.0
Lovely Version: 0.7.1
Platform: Windows
Steamodded Mods:
1: Betmma Voucher Pack by Betmma, nicholassam6425 [ID: BetmmaVoucherPack]
2: Handy by SleepyG11 [ID: Handy, Version: 1.3.2, Uses Lovely]
3: 5 legendary challenges by Betmma [ID: legendaryChallenges]
4: Betmma Vouchers by Betmma [ID: BetmmaVouchers, Priority: -1, Version: 3.0.1(20250129)]
5: Neato Jokers by Neato [ID: Neato_Jokers, Version: 1.0.3, Uses Lovely]
6: Sonic the Hedgehog Deck by SeagullMoe [ID: sonic_deckall, Version: 1.0.0]
7: Better Vouchers This Run UI by Betmma [ID: BetterVouchersThisRunUI]
8: JokerDisplay by nh6574 [ID: JokerDisplay, Priority: -100000, Version: 1.8.2]
Lovely Mods:
1: betmma_mods
2: Saturn-alpha-0.2.1-A
Stack Traceback
(3) Lua global 'calculate_reroll_cost' at file 'functions/common_events.lua:2486'
Local variables:
skip_increment = boolean: true
(*temporary) = nil
(*temporary) = number: 0
(*temporary) = table: 0x3e3a82c8 {click_offset:table: 0x3e2dbb80, children:table: 0x3dea0688, ambient_tilt:0.2 (more...)}
just stop comparing nils with numbers people jeez
(*temporary) = string: "attempt to compare nil with number"
(4) Lua field 'func' at file 'functions/state_events.lua:258'
(5) Lua method 'handle' at file 'engine/event.lua:99'
Local variables:
self = table: 0x3e7a6a48 {start_timer:true, timer:TOTAL, blockable:true, trigger:immediate, func:function: 0x3df3e2a8 (more...)}
_results = table: 0x3e42c4e8 {blocking:true, pause_skip:false, time_done:false, completed:false}
(6) Lua method 'update' at file 'engine/event.lua:182'
Local variables:
self = table: 0x3df48a68 {queue_last_processed:67.933333333331, queues:table: 0x3df48a90, queue_dt:0.016666666666667 (more...)}
dt = number: 0.00395352
forced = nil
(for generator) = C function: next
(for state) = table: 0x3df48a90 {unlock:table: 0x3deeb428, other:table: 0x3deeb4c8, tutorial:table: 0x3deeb478 (more...)}
(for control) = number: nan
k = string: "base"
v = table: 0x3deeb450 {1:table: 0x3e3360c8, 2:table: 0x3e7a6a48, 3:table: 0x3decd040, 4:table: 0x3ebf27a0 (more...)}
blocked = boolean: false
i = number: 2
results = table: 0x3e42c4e8 {blocking:true, pause_skip:false, time_done:false, completed:false}
(7) Lua upvalue 'gameUpdateRef' at file 'game.lua:2566'
Local variables:
self = table: 0x3dce49a8 {F_GUIDE:false, F_CRASH_REPORTS:false, F_QUIT_BUTTON:true, HUD_tags:table: 0x3e856c30 (more...)}
dt = number: 0.00395352
http_resp = nil
(8) Lua upvalue 'gameUpdateRef' at Steamodded file 'src/ui.lua:84'
Local variables:
self = table: 0x3dce49a8 {F_GUIDE:false, F_CRASH_REPORTS:false, F_QUIT_BUTTON:true, HUD_tags:table: 0x3e856c30 (more...)}
dt = number: 0.00395352
(9) Lua method 'update' at line 22 of chunk '"11585"]'
Local variables:
self = table: 0x3dce49a8 {F_GUIDE:false, F_CRASH_REPORTS:false, F_QUIT_BUTTON:true, HUD_tags:table: 0x3e856c30 (more...)}
dt = number: 0.00395352
(10) Lua upvalue 'love_update_ref' at file 'main.lua:992'
Local variables:
dt = number: 0.00395352
(11) Lua field 'update' at file 'main.lua:3129'
Local variables:
dt = number: 0.00395352
(12) Lua function '?' at file 'main.lua:931' (best guess)
(13) global C function 'xpcall'
(14) LÖVE function at file 'boot.lua:377' (best guess)
Local variables:
func = Lua function '?' (defined at line 902 of chunk main.lua)
inerror = boolean: true
deferErrhand = Lua function '(LÖVE Function)' (defined at line 348 of chunk [love "boot.lua"])
earlyinit = Lua function '(LÖVE Function)' (defined at line 355 of chunk [love "boot.lua"])
MR PRESIDENT GET DOWN
Needing some help with this please-
I clicked the button for the next round and it showed me... That-
first, turn off every mod that isn't the one that's crashing
How do I know which one isn't crashing?
(sorry for the delay, uh, things happened) uhhh, how exactly does main_end work? like, how do we call it in the actual card text, let alone make it so it won't show up if the config setting is disabled?
turn half of them off, if it's still crashing (the same way) then none of the mods you just turned off are the problem
I turned half of them off and the game crashed the second I clicked play LMAO
progress
what if I went and actually finilized the meta tags pr
please save me from this hell
i would never say no to debugplus debugging harder
but I don't want to touch the hundreds of tags to make sure they make sense
How far off is it?
(if it could also work for enhancements that'd be swell)
but there were some weird choices I needed to redo
the plan was that it would eventually work for all object types
I should finish this so I can use it in JamMod
Gappie draws every card
sometimes modding this game is just "hey i can do this thing" 
You're evil
Is there a way to gain vouchers through collection atm?
It would be a nice feature
Same for tags
3 will put a voucher in the shop (if your in the shop)
also 3 and c should work in tags
Hmm I think I tried before
You can hover it and type eval dp.hovered:redeem() too
This might crash calc though I am unsure
dp.hovered my beloved
okay, this is a stupid as hell question. how exactly do we refer to the config? this crashes on line 18, so there's no doubt that it's an issue with that if statement.
kid named that eval script i wrote to apply yhe shader to all cards in collection
Does that live update?
no
cause it reopens the gui
I was thinking adding more jokers to the preview with like 4 set and the 4 random
maybe I should add some way to set what the watched joker is
uhhh the command wasn't really made for that
especially since I don't have a good way to include spaces in options
I need to make a better command parser sometime
still stuck on this. any idea what we're missing here??
can we see the config file_
Victin's Collection ALPHA has started!
The initial release for my main mod is open.
https://github.com/VictinZero/balatro-victins-collection/releases/tag/v0.0.0-alpha.0.0
First public release, still in ALPHA, for testing purposes.
This mod currently provides 15 Jokers and 11 Blinds.
At least 10 more Jokers have been implemented, fully or partially, and/or are being ...
-# now to create a #1209506514763522108 thread
["setting_1"] = false,
}```
we've been wanting to rename setting_1 internally but given it just straight up isn't working right now that'll have to wait
i wonder if in context.game_over , i can make it so that instead of just skipping to the next round as if you won, i can instead give the player extra hands to work with 🤔
-# oh, i see them now. damm i am stupid
No
You’d have to do some funky work in context.after I think
if context.joker_main and G.GAME.current_round.hands_left == 0
here
There is some tweaking to be done, but here is the base conditional.
You can change context.joker_main to whatever the fuck you need.
following that up, i wonder if it would be possible to make a consumable that can only be used in that specific scenario (kinda like a passive thing, you can't use it normally but if the conditions are met it'll use itself up)
we've removed the brackets yet now it's complaining about the lack of a "then" that should be there, unless for some reason the "if" statement just isn't counting it??? we're still not even sure if we're calling the config file correctly honestly.
can_use = function(self, card)
if G.GAME.current_round.hands_left == 0 then -- not tested. You probably want to set this to 1 instead of 0 because I don't think you can use consumables while stuff is taking place.
return true
else
return false
end
@old bane
If your hands are at 0 the round kinda just ended, so you can't use it
== instead of =.
also return G.GAME.current_round.hands_left == 0
You have a bad habit of putting commas everywhere, also you have an extra end
So use 1 instead of 0
https://github.com/WilsontheWolf/JamMod/blob/master/lovely.toml#L56-L68 this may be useful
Maybe add a 3x3 R at the end of “Joke” to keep the convention of hiding “Joker” in every joker card
i mean i already did that
if jimbo wore pants would he wear them like this or like this
Hello, I know this is a bit of a broad question, but how does the info_queue thing work? Like, how do you add stuff to it? And how do you make said stuff?
Since Jimbo is just a floating head, it would wear them over its head, as if a cap of sorts, we could say. It would also fit its character because I mean, 'Joker'.
the hubris to think that jimbo's true form could ever be known
please maybe someone?
i would say insane ball knowledge but like its probably really well known anyway just not mentioned as much anymore
it's kinda direct continuation of the previous thing i drew
ah
LMAO
I'm sorry
this is so peak
It is forbidden to be debuffed and do nothing
i added a seal just for that
card gets debuffed forever
lol
3 times the charm. Surely someone knows how it works.
(the shit that adds any edition's text to cards and shit)
Where tf are you finding those?
thats a fox
this is an excruciatingly stupid question. discovered part of the issue was that the config page itself we set up last night was broken. how do we... how do we make it actually modify the config file?
like, the actual answer seems obvious, it's an issue with ref_table, but getting there is a whole different story and trying to work our way backwards with talisman as a reference point has us clarifying stuff like local lovely = require("lovely") that not only just spawn even more issues, but we're fairly confident those are by no means necessary.
SMODS.current_mod is only avaible during your mod loading
you can save the object in a table or reference it by ID with SMODS.Mods.Id
oh, we were actually fairly close it turns out!
the only thing now is that the actual text formatting uh, doesn't appear in the resulting strings. how do we resolve that?
https://github.com/Steamodded/smods/wiki/Localization#loc_vars use a different key
hey gang - is there a good way to use pseudorandom to simply generate a random number between X and Y? i'm aware that it generates a random number between 0 and 1, which is fantastic for probabilities, but what i'm wanting to do is have it randomly generate a number to pull from a value in an array, which isn't necessarily probability
or is there another function that could be used for this sort of implementation that integrates with the game's RNG
pseudorandom_element is exactly what you want
oh fantastic, thank you! based on context i'm seeing in smods code i'm assuming the format is something like array/pool size and seed val
question; if we want to use this key multiple times, would it be wise to put it elsewhere or do we really just mention it on every card we want to use it on?
you give it the table
say i threw the name of an array i created in "pool," would that be compatible
how do i declare a new consumable type in both the collection and the labels localization entries
so for example, pseudorandom_element(FiveStarChars, pseudoseed('intertwined')) would generate a value from 1 to 2?
it would generate "keqing" or "placeholder"
OOOO actually that makes my life a lot easier
that is incredibly intuitive - thank you!
are you adding pity mechanics to balatro 😭
coming from only having known c++ i am continually shocked by how intuitive lua stuff is 😭
LOL no, this is to determine which 5-star you get from selling an intertwined fate
pseudorandom and its derivatives are thunk's code
so no 50/50 either
or like a framework he's used
I think it's origianl thunk code
so to make things less complex and redundant, i'm making it to where Intertwined Fate jokers show up in the shop as Rare and always give you a random 5-Star, but Acquaint Fate jokers (Uncommon) give you a 4-Star
no pity, no actual limited stuff, very much unlike the actual game
reimplementing genshin's gacha mechanics to the letter in balatro would be extremely unfun for the player, so i'm not doing that xP
great news i dont think the new update breaks anything (so far) 🎉
Hold on someone give Jimbo a crowbar
All it did besides adding new collabs is reworking the credits UI
pretty fancy now actually
the humble wheel:
too strong?
probably tbh
would X0.25 be better?
i'd recommend like x.25 yeah
Cool name
absolutely insane joker
and art
yeah i agree, i love that
lovely art
still don't quite know what it means to use a different key for this... how do we call it, anyways?
ok guys if that's a legendary then what the hell do i do with eat pants
depends
what is it
i mean, this seems fine tbh since it it destroys all played cards
scored
oh right
i wanted to make it destroy all played cards but I'm stupid
i mean still. either way. that is insane
it broke my heart
no celeste 💔
how could they 😩
celeste, hat in time, inscryption
yes
inscryption cards could go insane
i asked maddy and she barely acknowledged it so i think ill just die
also that effect seems like better than Legendary maybe 🤔
don't want it to be legendary 😭
quite honestly i'm just devastated that bugsnax was assigned to hearts. now me and my buddy have to choose between isaac and bugsnax
thank you guys ily (platonically) (balatronically)
balatronically 😭
eat pants being legendary is funny but i don't like it 😭
yeah. I saw that and it felt weird.
The game needs some sort of way to let you pick multiple collabs, and then randomly pick one per suit at the start of each run
this is how I feel about clubs lol
i feel like there's a better way than
[[patches]]
[patches.pattern]
target = '''=[SMODS _ "src/utils.lua"]'''
pattern = ''' card_eval_status_text(card, 'debuff')'''
position = "after"
payload = '''
SMODS.score_card(card, context)
'''
match_indent = true
honestly just being able to use any collab on any suit would be fantastic
calculating the card even if it's debuffed but only if the joker wants to
how do i have the joker return a message after gold cards score?
i mean
pay out
not score
still looking for a solution to this. tried a loc_vars key. it... it didn't go well, though there's also an 100% chance we implemented it wrong.
I tried to make a random Deck Skin option and IIRC it kinda worked
there's a mod that is aiming for this, but the artist has a backlog
I think for the origin you could do a main_end, and to get the right localization entry you need to localize it
this is huge actually
i am looking forward to it owo
doing it like this rn
but it returns the message before the effect actually happens
how do we display a main_end in card text and how do we change what it says? the examples we found haven't exactly been clear...
here's the full diff if anyone cares
hello chat, i have an issue, im trying to make this joker retrigger played cards when played hand contains a flush, but its instead triggering on every poker hand including flush, like high card and pair, whats wrong with the code?
if context.repetition and context.cardarea == G.play and context.poker_hands['Flush'] then --if played hand contains a flush
return {
repetitions = card.ability.extra.repetitions, --retrigger cards
message = localize('k_again_ex'),
card = card
}
end --im not actually 100% sure this works its a bit buggy
end```
we are absolutely not calling main_end correctly nor do we know how exactly to set it. what're we doing wrong here?
Generally, I do something like this:
loc_vars = function(self, info_queue, card)
return {
main_end = _generate_main_end(card)
}
end
and the function code is defined per Joker (or maybe imported from a file if it's shared)
here's an example:
(technically that video is outdated with the code)
Here's an updated video
the idea is that the text will change based on if config.showOrigin == true, either displaying a string specified by the loc_txt if it's true or just hiding it altogether if it's false. this text has two formats; {c:inactive,s:0.8} if true, and if false, {s:0.0}. we got tantalizingly close with a non-main_end, variable-based approach, but it couldn't display the text formatting correctly.
basically, this, but the {formatting} actually. Works.
so with the config, if it's set not to show you can just return nil
if you do want to show it, then you return the actual text as a node
Your context.poker_hands['Flush'] needs to be put inside a next()
I can send you a generate_main_end as an example
main_end seems overkill for this when you can just have two separate localization entries
I think accessing the second localization inside main_end is easier than changing which one the Joker uses
you end up with two localization entries either way
fair enough
would appreciate that, as it's... it's not going hot here. no idea how exactly _generate_main_end is meant to work.
ah thanks lad
how would i make a consumable that can only be used while inside a blind?
using can_use and checking if player is in a blind
so like
can_use = function(self, card)
if G.game.blind then return true
else return false
end
end
if G.game.blind is the proper way to check if the player is in a blind, then yes
oh wait it's G.GAME.blind.in_blind
G.GAME.blind.in_blind
blind in blind
still need help with this. have shuffled around basically every possible permutation of main_end, generate_main_end, and _generate_main_end,, with and without main end = main end at the end of the actual text, and gotten either immediate crashes or nothing displaying at all for a main_end.
would G.GAME.blind.in_blind return a boolean? cause then i can just do
can_use = function(self, card)
return G.GAME.blind.in_blind
end
Where were you defining the strings for this second key you were trying to use here?
since then we've moved this over to a proper localization file, but the end result is more or less the same
at this point, we were clarifying it in the loc_vars of the card code itself, though
do shaders support additional textures
wow that question ended up being so far less specific than I wanted it to be
It's more complicated because it updates the main_end based on currently selected cards. What you really need is the final return, plus some text inside, so you should be able to just refer to the first call of add_node and any local variables used in it
not familar with shaders but this may help https://love2d.org/wiki/Shader
You can't have a loc_vars in your localization file. I'd be looking at something like this for the joker's loc_vars
loc_vars = function(self, info_queue, card)
local t = { vars = { blah } }
if config.showOrigin then t.key = self.key..'_source' end
return t
end
and a setup as follows in your loc file
j_stla_paulJoker = {
name = 'Paul',
text = {
'{C:chips}whatever else',
'Paul.',
}
},
j_stla_paulJoker_source = {
name = 'Paul',
text = {
'{C:chips}whatever else',
'Paul.',
'{C:inactive,s:0.8}Origin: Paul',
}
},
I think you need to pass them as an additional variable
I haven't tried it but I want to sometime
I was hoping there was any smods support for it
I definitely don't want to go back to raw opengl
what are you trying to do?
if you have an additional texture, you can draw the shader on it separately i guess?
nah, I need a noise texture
it's hard to tell what you need when you're being so specific
preferably one I'm not generating with math
yeah I dunno what's wrong with me rn I can't words like at all
basically, making an edition, need a pre-baked noise texture to be available in the shader
I don't shader, so i don't think my input would be of use
welp, it's 10:46 PM i'm sure i have it in me to learn low level LÖVE GL stuff
I think the support is passing an additional variable
that's not really going to help alone, that can be used to pass the texture id but it still has to be loaded
I think the idea is e.g. you have a Joker texture, and an edition that uses a mask to do something
I mean you load it just like you load the original texture
yes that's precisely what i'm going to figure out as soon as my attention span wanders over from discord to vscode
oh boy
Any shader example loads the original texture
I think the only difference is that it's an extra variable
i'm looking at love
Don't look at love look at hatred
i don't think there's support for loading two textures into one shader pass
There is you just pass multiple textures to the shader
listen
show me an example
and i'll believe you
until then i'm believing the documentation
deal, probably
how does a glass card draw its shader
there isn't a specific shader for glass cards
hey if im using neato jokers as a reference is that ok?? like if i'm looking at their code and trying to create my own jokers by learning from it
then how does it get drawn, i know it doesn't draw the shadow but is there anything else to it?
just the base dissolve shader on the sprite
nothing else is special about glass cards being drawn, their sprite just has some transparency
It's fine to use as a reference and learn how it does things, as long as you're not just copying code from it. You should understand what everything that you type into your code is doing
yeah thats what im attempting to do lol
That's... That's called learning...
I asked last night but chat was kinda dead.
What order does context.before vs. boss blind debuffs happen? If I want something to happen before the debuff, is context.before enough
I was going to try and link to a Joker pack released under an open license that you could just copy from (with credit), but I actually can't find any
Like... That's literally how plane geometry is taught. You look at things, figure out how they work, then take what you've learned from how some things work to figure out how other things work. Proofs, theorems, corollaries, it's all going based on what someone else has done, and learning from it.
As stated, don't just copy things and claim it as your own, but you can analyze how something is built to figure out how to build other things. That's architecture. That's development. That's learning.
Hell, that's how the legal world works. Precedent and all that.
is there a way to make copies of the same joker have different values?
Cryptid Monster joker does that
Every Monster you get increases the xMult of future copies of Monster.
so what I did was basically create an Image object based on Atlas, ripping out the things we don't need from an atlas:
BALIATRO.Images = {}
BALIATRO.Image = SMODS.GameObject:extend {
obj_table = BALIATRO.Images,
obj_buffer = {},
required_params = {
'key',
'path',
},
set = 'Image',
register = function(self)
if self.registered then
sendWarnMessage(('Detected duplicate register call on object %s'):format(self.key), self.set)
return
end
self.name = self.key
BALIATRO.Image.super.register(self)
end,
inject = function(self)
local file_path = self.path
self.full_path = (self.mod and self.mod.path or SMODS.path) ..
'assets/images/' .. file_path
local file_data = assert(NFS.newFileData(self.full_path),
('Failed to collect file data for Image %s'):format(self.key))
self.image_data = assert(love.image.newImageData(file_data),
('Failed to initialize image data for Image %s'):format(self.key))
self.image = love.graphics.newImage(self.image_data,
{ mipmaps = true, dpiscale = G.SETTINGS.GRAPHICS.texture_scaling })
local mipmap_level = SMODS.config.graphics_mipmap_level_options[SMODS.config.graphics_mipmap_level]
if not self.disable_mipmap and mipmap_level and mipmap_level > 0 then
self.image:setMipmapFilter('linear', mipmap_level)
end
end,
}
Now self.image of an image object holds a lovely image. According to the forum post this can be sent as a uniform sampler2D
BALIATRO.Image {
key = 'test_noise',
path = 'test_noise.png'
}
SMODS.Shader {
key = 'test',
path = 'test.fs',
send_vars = function(sprite, card)
return {noiseTex = BALIATRO.Images['test_noise'].image}
end,
}```
And it should be then accessible through an uniform/extern in the shader:
extern Image noiseTex;
Probably a mechanic you can use as a basis.
this is as far as I got, and then I stopped because in the meantime I found a suitable mathematical noise algorithm
but as far as I understand everything this should work in theory
🎶 nothing can go wrong 🎵
"Oops, the game crashed!"
OH, NO, IT ALL WENT WRONG
I'm making something that halves a current blind's chips; it works but I can't seem to update the chip count in the blind
you have to update the number and rerender it
look at what happens when Blind:disable() hits The Wall
lemme check that
I assume the way that works is there's a variable stored in the save file itself, which is then used as the default xMult value for generated Monsters
Another thing you can look at is Braille Joker from either BBBalatro or Cheesy Jokers (can't remember which), which reduces blind size by 25%
No worries, I got it working :) But thank you
No problem
Why not use an Atlas?
i'm trying to make it so that an enhanced card sprite will shrink down to the size of a regular card, but instead i just have comically large cards >_<
yeah i already have something like that at work, what i need is for 2 or more copies of the same joker to have different values there. Right now if i spawn multiple copies they all have the same value
How can I manipulate a sprites rotation, position, scale, etc.? (and its layers)
Trying to have a card's description change based on suit. It's saying there's an error at line 533 indexing a nil value, but G.GAME.current_round.dizzy_card.suit shouldn't be a nil value since it's called in calculate. I also provided my localization file. Any idea what spits this error?
Screenshot pls
Not sure what the suit error is, but #1#, #2#, #3#.. etc. refer to the arguments provided to localize(), and you don't appear to be providing any
can someone explain wtf thunk meant by this
if not first or first then
I think you have to add the parameter to loc_vars and fix your localise arguments:
return {
vars = {
G.GAME.probabilities.normal, self.abilities.extra, G.C.SUITS[G.GAME.current_round.dizzy_card.suit], localize(G.GAME.current_round.dizzy_card.suit, 'suits_singular')
}
}`
and then in your `en-us`:
```lua
`name = "Dizzy Joker",
text = "This joker has a {C:green}#1# in #2#{} chance",
" to create a {C:spectral}Spectral{} card whenever",
"A card of {V:3}#4#{} suit is scored",
"Suit changes every round.",
Does current_round.x_card.suit have to be put in local vars or extra? I tried making sure the joker worked by default (without the localize files), and I'm getting an index nil value error around this line if (context.other_card:is_suit(G.GAME.current_round.dizzy_card.suit)) and #G.consumeables.cards + G.GAME.consumeable_buffer < G.consumeables.config.card_limit and (pseudorandom('dizzy') < G.GAME.probabilities.normal/card.ability.extra) then for the current round suit
Neither, i think that will need to be a member of the card directly if you want to access it like that
But if you add it to extra then you can access it with G.GAME.current_round.dizzy_card.extra.suit easily
so add current round suit to the extra line?
how would i use tobig() with G.GAME.chips? is it just
tobig(G.GAME.chips)? i'm running issues with talisman and a mr.bones-like joker
To be more specific, add it as a named member of the extra table:
config = { extra = {odds = 4, suit = 'Spades' } }
Then access it when you're resetting or changing it with G.GAME.current_round.dizzy_card.extra.suit
This will also require you to change the loc_vars function again to use extra.odds instead of just extra
I'll see if this works
well it's to_big not tobig. but otherwise yes
depends on what you're using it for, too
most of what you need to use to_big for are comparisons
Here's the code from "config" to as far as discord would let me I'm still getting a nil value around the line I sent before (the line containing if context.othercard:is_suit)
loc_vars = function(self, info_queue, card)
return { vars = {
G.GAME.probabilities.normal,
self.abilities.extra.odds,
localize(G.GAME.current_round.dizzy_card.suit, 'suits_singular'),
colours = { G.C.SUITS[G.GAME.current_round.dizzy_card.suit] }
}
}
end,
calculate = function(self, card, context)
if context.individual and context.cardarea == G.play then
if (context.other_card:is_suit(G.GAME.current_round.dizzy_card.suit)) and #G.consumeables.cards + G.GAME.consumeable_buffer < G.consumeables.config.card_limit and (pseudorandom('dizzy') < G.GAME.probabilities.normal/card.ability.extra) then
G.GAME.consumeable_buffer = G.GAME.consumeable_buffer + 1
return {
extra = {focus = self, message = localize('k_plus_spectral'), func = function()
G.E_MANAGER:add_event(Event({
trigger = 'before',
delay = 0.0,
func = (function()
local card = create_card('Spectral',G.consumeables, nil, nil, nil, nil, nil, 'dizzy')
card:add_to_deck()
G.consumeables:emplace(card)
G.GAME.consumeable_buffer = 0
return true
end)}))
end},
colour = G.C.SECONDARY_SET.Spectral,
card = card
}
end
end
end
}
local igo = Game.init_game_object
function Game:init_game_object()
local ret = igo(self)
ret.current_round.dizzy_card = { suit = 'Spades' }
return ret
end```
oh would it work for arithmetic as well
i just need it for division
How do I get the type of hand being played - I don't want the displayed hand name (because I want to make sure my joker still recognizes Royal Flush as a Straight Flush).
Namely, I want to find all the hand types available within the current hand, but then discard the one already recognized by the game
iirc that works as-is
Ah i see the full picture now. You've got a mixture of card.suit and card.extra.suit now, you want to pick just one. The suit-based vanilla jokers like Ancient just use card.suit, so might be better to stick with that and ignore what I said about adding it to the extra table
do I keep the ability = extra = odds = 4?
Might as well, using named parameters makes things a little clearer imo
I know less about this side of things, but i think there are some circumstances in which context.other_card isn't defined, so maybe just do a nil check before accessing it
How can I manipulate a sprites rotation, position, scale, etc.? (and its layers)
A nil reference error on that line could be a number of things, try breaking it up to make it more clear what's actually failing
Checking if invalid_case then return end might be better practice and easier to read, instead of nesting if statements
hey, what does bmm_storage.db do? the balatro mod manager says to delete it, but i don't want to delete any save data
That's BMM's mod database, it's not used by anything other than BMM
It'll be regenerated if you click "Reindex mods" in BMM's settings menu, though be warned this will also delete any manually-installed mods
oke, dat's fine with me - just wanted to make sure no save data will be deleted
how would I detect if the player is using OmegaNum for scoring? Would it be as simple as checking if chips or mult is a table instead of a number?
is there a way i can install the mod installer into my proton prefix for balatro? on linux
BMM doesn't currently work on Linux, no
Looks like you might be able to do Talisman.config_file.break_infinity == "omeganum"
just taking a peek at talisman's src
what happens if i install it to the proton prefix?
how can i make 2 or more copies of the same joker have different random values at the same time, without one overriding the others?
or actually eh, maybe i should just take yer word for it lol
Oh, i missed the mention of Proton. It might work, though BMM depends on a few relatively new technologies that might not be present in or compatible with Linux + Proton
Worth a try though
hmm, i think i'll try it next time i delete all the modded stuff via a game update or file verification lol
this may mess up if the user changes the type of infinity but doesn't restart
I think a lot of things break in that scenario
I did my best to pick apart the pasta that was the line being a bunch of nested if statements and separate them. not getting a nil error on the suits now (thanks to just keeping everything the same from the modded castle2 joker), but we've hit a dreaded "arithmetic on field odds (nil value)"
loc_vars = function(self, info_queue, card)
return { vars = {
(G.GAME.probabilities.normal or 1),
card.ability.extra.odds
} }
end,
calculate = function(self, card, context)
if context.individual and context.cardarea == G.play then
if (context.other_card:is_suit(G.GAME.current_round.castle2_card.suit)) and #G.consumeables.cards + G.GAME.consumeable_buffer < G.consumeables.config.card_limit then
if (pseudorandom('dizzy') < G.GAME.probabilities.normal/card.ability.extra.odds) then
G.GAME.consumeable_buffer = G.GAME.consumeable_buffer + 1
return {
extra = {focus = self, message = localize('k_plus_spectral'), func = function()
G.E_MANAGER:add_event(Event({
trigger = 'before',
delay = 0.0,
func = (function()
local card = create_card('Spectral',G.consumeables, nil, nil, nil, nil, nil, 'dizzy')
card:add_to_deck()
G.consumeables:emplace(card)
G.GAME.consumeable_buffer = 0
return true
end)}))
end},
colour = G.C.SECONDARY_SET.Spectral,
card = card
}
end
end
end
end
}
local igo = Game.init_game_object
function Game:init_game_object()
local ret = igo(self)
ret.current_round.castle2_card = { suit = 'Spades' }
return ret
end```
Got no clue if anything new can be gleamed from this layout. I appreciate your time and patience with me here, but this is stumping us
formatting borked. I think I will die
We're gonna try this again tomorrow. Appreciate the help everyone
Which line is throwing the error? Are you assigning to odds anywhere outside of this, or trying to access odds before the card is initialized?
Feel free to ping whenever you come back
Apprieciate this. thank you
I'm currently trying to create a joker that will trigger an effect for the first card of suit, what would the best way to do this be?
first card of suit?
*each suit
first card for each suit scored
yes
i think you could have a list like
suits = {
[“Spades”] = false,
[“Clubs”] = false,
etc.
and then set each to true as you find one of each suit
maybe you could use chad logic and check for unique suit
ik I need to do something like that, I just need to figure out how to properly set the list then reset it at the start of each hand
just have it be a local variable inside your context.individual
not individual
more like context.before or something
that way it gets reset when a new hand is plued
plyed
(im on phone if you cant tell lmao)
I tried using context.before but it just crashes when I play a hand
should this also work for vanilla jokers or only modded jokers?
i presume the localization is displaying odds correctly, im not good with lua so this is a shot in the dark but the game seems to think card in line 521 is not, in fact, the card in question, since otherwise odds wouldnt be nil, maybe try using self instead? im not sure why that would fix it but its worth a shot
alright
and if it doesnt fix it maybe at least itll give you a new crash log that will be more useful
ok im going absolute bonkers, can someone plz help me out. i just want to get access to the loc_vars from any object, even vanilla ones, does anyone have ideas?
loc vars is funny because sometimes you just can't access it
loc_vars is funny becasue its not actually a function for vanilla cards
just part of one
im hanging out in a vc in the cryptid discord can someone come explain to me whats actually going on?
like i have been banging my head violently for way too long and i need help
So in the Balatro source code, cards aren't structured the same way as in steamodded, so a vanilla "card" doesn't have any loc_vars that are easily accessible. What are you trying to accomplish?
if you want to join into the Cryptid discord i can explain, but basically i want to make a deck tracker so that viewers on twitch can mouse over cards on stream to get the description and name of a joker
okay so i ran a quick test by making a joker with the same ability copy pasted and it seems like it's an issue of it being an edition
i don't believe editions can trigger the same calculation methods as jokers
it looks like i might have to convert my idea into a joker sticker for full functionality
trying to patch something over pokermon, but it doesn't seem to work - what may i be missing?
actually it looks like that makes it even less functional, due to limitations of loc_var
Hello hi howdy! I tried looking online for modding guides, however i could only find ways to install mods, not make your own mods. I was planning on doing a joker/sprite reskin- how would I start doing that?
reskins should use #1300851004186820690 ngl
Thank you! :3
I wonder if I can have functions in config.extra?
Functions are just ✨ fancy✨ variables
And dynamic typing means you can put anything anywhere. Whether it makes sense to is another matter
Hi there, is there any documentation on formatting text with smods?
cant find it on the github wiki (maybe im just blind :3)
You mean like all the keyword you can use in {C:[keyword]}?
yep
oh thanks, found it in the game files
And, if you know how to hook a function in lua, you can make your own keywords with your own custom colours.
sick sick
could we have a non-breaking target to prevent the 1 in 4 of glass from occurring? it's a popular modded effect but patching it seems unfeasible since no matter what you do, it's most likely breaking similar patches
something simple, just like
local willdestroy = true
-- TARGET: prevent glass break
if willdestroy then destroyed = true end
I might try and tackle this, it's often asked. Briefly:
- Pre-defined colour:
{C:<colour>} - Movement:
{E:1}is floating text,{E:2}is bumping text - Hover tooltip:
{T:<key>}, where<key>is an element inG.P_CENTERSorG.P_TAGS - Text background colour:
{X:<colour>}(usually used as{X:mult,C:white}) - Custom colour:
{V:<number>}, where<number>is an index of a colour passed to the textlocalise()throughargs.colours - Text scale:
{s:<number>} - Reset formatting:
{}
(Replied to wrong comment)
Wait, I don't need to ,C:white after X:mult this whole time!?
they are combinable
adding both makes the text white the background mult
also each new set of brackets resets all previous ones
OK, I thought it meant {X:mult} would give red-back-white-text without the need to specify ,C:white
also for V: you can pass the colours to loc_ars
this should giv e red back black text iirc
Yeah I figured.
I just thought there were shortcut. Turns out it's a wrong direction.
whoa thanks
I'm in the mood to write documentation, I'll scribble up a proper list with examples and post it... somewhere. Mod Wiki probably
you can pr onto the smods wiki
It's all vanilla functionality so the Steamodded wiki might not be the most relevant place for it
I'd forgotten about the UI doc, that was super helpful for me. Alright, cool, will do
Looks like theres a spot for text formatting https://github.com/Steamodded/smods/wiki/Localization#text-formatting
do you know how to write Wikipedia style stuff
i am going to try to document my own mod on my wiki
Documentation? What's that?
chat what are we thinking
I mean it doesn't even need to be formal
if scored cards sum is ≥ 15 x1.5 mult per scored cards and retrigger the card effect for a legendary
Is it 1 in 50 chance to hit the jackpot of $777,
or 1 in 50 chance to hit the jackpot with $777?
It was originally 1 in 50 chance to hit the jackpot and gain $777.
of is more gramatically correct
How would I give a single joker a custom back sprite?
Somehow my 1x and 2x folders from my mod just... deleted their contents???
I had a backup and also saved it to my git but still
thats new
question; we're trying to make a blueprint-esque joker, and trying to add our
local t = { vars = { VARIABLES_HERE } }
if config.showOrigin then t.key = self.key..'_source' end
return t
end,```
thing to it, so we can properly add the description depending on the config setting. uh. how the HECK do we do that with this thing?
hmm. this doesn't crash, but it doesn't return t properly.
good idea
6
6
2
no
Can you animate a deck (as in the graphic of the preview card)
I've been trying to get the sprite sheet to work but when I tell the deck that I wanna use a sprite sheet as animation instead of just the first frame the game crashes
decks dont support animated textures
you'll have to use a static image and manually move the position in the objects center
oh that would've saved me some headache and a good hour of banging my head against the wall rip
if i want to randomly pick a character out of the pool, what would i need to do here?
wouldnt it just be pseudorandom_element(CharList[4])
it seems optional?
if you want just slap in like pseudoseed('poop pant') as the second argument
for... random!
why does it just proc again after the score is finalized??? did i do something wrong with contexts
for context here is the calculate function
calculate = function(self,card,context)
if context.joker_main then
if card.ability.extra.triggered == true then
if pseudorandom("cerebella", 1, 500) == 1 then
print("cat (it doesn't exist yet)")
end
-- and whatever else happens if its triggered it has to do with other jokers that arent implemented yet
end
end
if context.individual and context.cardarea == G.hand then
if context.other_card:is_suit('Diamonds') then
card.ability.extra.triggered = true
if context.other_card.debuff then
return {
card = card,
message = localize('k_debuffed'),
colour = G.C.RED
}
else
return {
card = card,
x_mult = card.ability.extra.xmult
}
end
end
end
if context.after and context.cardarea == G.play or context.end_of_round then
card.ability.extra.triggered = false
end
end
context.individual and context.cardarea == G.hand happens in two situations,
one of which is where steel cards trigger and gold cards the other.
how do i differentiate between the two situations is there a third context
Add and not context.end_of_round for steel card timing,
or and context.end_of_round for gold card timing.
ok that fixed it ty, didnt think to use contexts in a more logical way rather than just mindlessly throwing it in my code for the situation lmao
This might help you more.
https://github.com/Steamodded/smods/wiki/calculate_functions
sorry to nag, still looking for help with this one? :o
use a comma instead of and?
Lua basically treats return foo and bar or nil as if foo == <truthy value> then return bar else return nil (ish)
So t will never be the returned value (the table with main_end will instead) and the or nil will always be ignored.
tried a comma, that didn't work... what do?
since, literally everything works but the main_end for whatever reason. (for reference, there's supposed to be a "compatibile/incomaptible" after the origin tag from the lang.)
Add main_end into t and return just t
okay, well. that worked disturbingly effortlessly.
we're kinda scared how cuberry somehow has been the card that has been the easiest to implement thus far given his. uh. Meta-reptutation, in the source material. wouldn't surprise us if there's bugs we're yet to find, to be fair... ;P
https://github.com/Steamodded/smods/wiki/Localization#loc_vars Prob cause that's how there's supposed to work 
(I don't have nitro I can't add the "read the docs" sticker)
ah, that would be the bug. he's supposed to trigger 3 times (one trigger and 2 retriggers, to be precise) for every time the joker activates, which doesn't work quite right for jokers that can, themselves, retrigger. (yes, we are acutely aware of how this could scale with some jokers and that's entirely the point given his rarity is this mod's equivalent to legendary ;P)
we assume our retrigger code is going to have to be a bit more sophisticated than it currently is, huh.
for reference, this is the current code (most of which is shamelessly pried from cryptid's old blueprint joker)
I'm trying to make a Joker that adds a randomised Stone card to the deck when chips > mult after scoring. The code below is mostly taken from the Marble Joker code in the lovely dump, and almost works, except that a floating copy of the Stone card stays on screen and does not go away. What below is causing that?
calculate = function(self, card, context)
if context.cardarea == G.jokers and context.after then
if hand_chips > mult then
G.E_MANAGER:add_event(Event({
func = function()
local front = pseudorandom_element(G.P_CARDS, pseudoseed('earthquake' .. G.SEED))
G.playing_card = (G.playing_card and G.playing_card + 1) or 1
local card = Card(G.play.T.x + G.play.T.w/2, G.play.T.y, G.CARD_W, G.CARD_H, front, G.P_CENTERS.m_stone, {playing_card = G.playing_card})
card:start_materialize({G.C.SECONDARY_SET.Enhanced})
--G.play:emplace(card)
table.insert(G.playing_cards, card)
return true
end}))
G.E_MANAGER:add_event(Event({
func = function()
G.deck.config.card_limit = G.deck.config.card_limit + 1
return true
end}))
draw_card(G.play,G.deck, 90,'up', nil)
return {
message = "Rumble!"
}
end
end
end
weird. even removing the retrigger section, he only seems to retrigger on the first 2 cards before just. stopping.
you commented out the emplace line, which actually puts the card in hand
which should probably be G.hand, since you don't want to emplace to G.play
But if marble joker has it that way then maybe it works and I'm just crazy
I should clarify I want the card to go into my deck, not my hand
G.deck
but also make sure you set the card's area since you are doing this the hard way instead of using SMODS.create_playing_card
oh the card gets drawn from G.play to G.deck
SMODS doesn't have create_playing_card, it's just from vanilla iirc
ok ill delete half my oddities then
Okay, the code works perfectly fine (the card does get added to the deck etc.) BUT there's a floating stone card that just kinda appears and is chilling around. It's a result of the Marble Joker making a Stone card appear in the middle of the screen at the start of round
oh hm apparently i am using a base game function
me commenting out the --G.play:emplace(card) made it so I no longer got softlocked, but there's something else I need to get rid of (I think) that would prevent the floating card from appearing
you arent emplacing the card into G.play for it to be drawn from G.play to G.deck
The floating card is a result of not emplacing the card
Okay, it's fixed, thanks y'all! Here's what the fixed code looks like (I made some additional fixes):
calculate = function(self, card, context)
if context.cardarea == G.jokers and context.after then
if hand_chips > mult then
G.E_MANAGER:add_event(Event({
func = function()
local front = pseudorandom_element(G.P_CARDS, pseudoseed('earthquake' .. G.SEED))
G.playing_card = (G.playing_card and G.playing_card + 1) or 1
local card = Card(G.play.T.x + G.play.T.w/2, G.play.T.y, G.CARD_W, G.CARD_H, front, G.P_CENTERS.m_stone, {playing_card = G.playing_card})
card:start_materialize({G.C.SECONDARY_SET.Enhanced})
G.deck:emplace(card)
table.insert(G.playing_cards, card)
return true
end}))
G.E_MANAGER:add_event(Event({
func = function()
G.deck.config.card_limit = G.deck.config.card_limit + 1
return true
end}))
return {
message = "Rumble!"
}
end
end
end
okay, no, that's weird. the amount of times it actually triggers seems to be correlated with the number of jokers you have??? which. that should not be happening. we're probably gonna sleep on this one...
After calling calculate_joker you need to set blueprint and blueprint_card to nil
You will want to keep a reference to blueprint_card in a different variable to use afterwards
after line 166, right?
correct
This is how vanilla blueprint does it
Except in your case self should be card
okay, that's... odd. it's still only retriggering twice if there's 2 jokers, and only retriggering additional times the more jokers there are. even tried to copy-paste the vanilla blueprint code, and it did the same thing?
which implies either an error in the for loop for other_joker, or somewhere in the update function
What is your code now
You didn't even set blueprint and blueprint_card to nil
Additionally you might want to add and not context.no_blueprint like vanilla blueprint does
okay, there we go. now it works as we'd expect it to. the only thing would be adding the "it triggers 3 times" thing, but it's almost 3am, so that sounds a lot like tomorrow us' problem. ;P
💀
what
y'know the ERROR of Spades
which despite being a "spade" is clearly a heart
great card that just crashes the game and also does not count for flush fives
is it tho?
tried to create a random heart or spade card when blind is selected
so far i have done not that
its a card at least
chat whats worse an ERROR of spades that is a heart and crashes the game when played or a card with absolutely nothing on it that crashes the game when played
error of spades is f u n n y
true
is there a way to create a card that is specifically a suit?
because what im doing is absolutely not working lmao
Random rank?
yes i think
yeah just like a random hearts card or spades card
got mimics in this before i got cards doing what they're supposed to do lol
y'all are NOT 2's of hearts
have a blank steel card lol
could be an interesting challenge run tho
your deck is getting filled with cards that crash the game if played
just put jokers in the area
i mean wouldn't exactly be the same cause they stop being there and dont go back into the deck no?
Do you guys agree this sounds like a legendary?
if you level up high card a bunch, this could go insane
true
indeed
sounds a little to low, maybe multiply chips & mult by two?
X2 mult for every unique Poker Hand included in what is played
there's 10 unique pairs in a 5oak
I mean, I'd probably lower the rarity before changing the effect
isn't it 20? 5x4?
Nah, it'd be 5 choose 2 - which amounts to 10
ahh
5x4 means that Card 5 - Card 1 is a different pair from Card 1 - Card 5
evil deck idea: "Additive Deck"
chips and mult are added together rather than multiplied
5oak would get 5oak + 4oak + 3oak + pair + high card
It's not 5 high + 10 pairs + 10 3oak + 5 4oak + 5oak
At least, that's not the impression I got from the way my brother suggested the joker
Still can't believe every hand can contain HC even when 4oak not containing 2p.
flush five = flush five, flush, five of a kind, four of a kind, three of a kind, pair, high card
which hand type gets the most sub-hands? Is it flush five?
Dear god it's so fucking peak
so is Jimbo still usable when he's been devoured?
is this the vore you were referring to yesterday
maybe
One of my Jokers has the function to change its suit for every round ended, but it's currently very vanilla, what changes can I make to support modded suits overall
SMODS.Suits
Yeah I know it exists but how do I specifically use it
Do I replace all instance of suits with it
Oh wait
Found something I maybe could use
Yeah you can do that for suits too
did you add mult_mod after that instance of the joker was created
also does it actually exist also mult should be {C:mult} and not include the Mult text and there should be a space
effects that give money say "earn $#"
"X on start of blind" should be "When {C:attention}Blind{} is selected, X"
flashing warning!!!!
what in the actual name of jimbo
Why do they spin 🤣
Why the dogy got helicopter mode!!!
I want to do particle effects on a Sticker, but does it support on_load, on_apply and on_remove like editions do?
on_apply = function(card)
card.children.particles = Particles(1, 1, 0, 0, {
timer = 0.03,
scale = 0.3,
speed = 1.2,
behind = true,
lifespan = 2,
attach = card,
colours = {G.C.PURPLE, lighten(G.C.PURPLE, 0.15), darken(G.C.PURPLE, 0.2)},
fill = true
})
end,
on_load = function(card)
card.children.particles = Particles(1, 1, 0, 0, {
timer = 0.03,
scale = 0.3,
speed = 1.2,
behind = true,
lifespan = 2,
attach = card,
colours = {G.C.PURPLE, lighten(G.C.PURPLE, 0.15), darken(G.C.PURPLE, 0.2)},
fill = true
})
end,
on_remove = function(card)
card.children.particles:remove()
end,
For instance this is how I do particles on Editions, but as far as I know, Stickers only has draw
https://github.com/Steamodded/examples/blob/master/Mods/ExampleJokersMod/ModdedVanilla.lua#L163 Merry Andy 2 from the examples has what you need
that's significantly harder to do
whatever im ready... there is no turning back
Check Cryptid's source, I know they have vouchers + a joker that increases card selection size, that might be similar to what you want
If I want to debug hand calculation, is the best way of doing so just making a custom deck with the 5 starting cards I want, or is there a utility that lets me just customize the cards directly?
what is the name of the voucher
you can use debug plus to change everything about your cards
I want to say "Grabber", but don't quote me on that
fractal fingers is a Joker
oh yez
yeah i am not ready
scrapping that rn
Sticky Hands
iirc
Grabber is the +1 hand in vanilla
thanks anyway
thanks!
how do I directly modify the chips and mult outside of a message proc
because this is displaying the message but not adding the chips or mult
don't use manual card_eval_status_text, it's not needed with better calc
you can just return the table you're defining as x here
or construct a return table if you need multiple messages
it basically supports you returning something like a linked list
the returned table can have an extra table which is evaluated as if it was also returned
local tail = {}
local head = tail
local function append_extra(t)
head.extra = t
head = head.extra
end
append_extra({ message = 'blah' })
-- etc.
return tail
my recursive version is funnier and probably strictly worse
okay, so now it updates the chips and mult correctly...but then after evaluating, it goes back to the default chips and mult for the hand
line 61 will have the and false removed, just testing the loop itself detected proper hands
i tried to repurpose the abstract joker code to give $1 dollar per joker owned and idk what the hell happened, anyone able to tell me what i fucked up
You're not resetting dollar bonus
yeah thats what i thought
Add a card.ability.extra.dollar_bonus = 0 before the loop
damn lol thank you
guhhhh this lovely patch doesn't seem to do anything to the joker with that specific name and when I set it to "true" it just throws up weird errors. i think i need to either give up on this silly easter egg or sleep on it for 500 years
oh you can't modify score in context.before
oh
I don't think we've added a context that allows base score to be modified in the way that blinds like the flint do
that being said, hand_chips should already be populated in context.before shouldn't it?
and whatever the mult global is
it's a hack but works in theory /shrug
hand_chips and mult globals.
is there any way i can make these two values be random for each instance of the joker?
randomize them in set_ability
inside or outside loc_vars?
i tried messing with set_ability yesterday but it gave me weird results like overwriting previous instances of the joker
were you interacting with self
you shouldn't interact with self in a SMODS Center unless you know exactly what you're doing
yeah i was using self
also loc_vars gets a card, not a center :v
yeah i just changed that to center just to see if it changed anything
set_ability = function(self, card, initial, delay_sprites)
local MIN_VALUE = 0
local MAX_VALUE = 10
local SEED = 'your_joker_id_here'
card.ability.extra.RandomOddsJ = math.floor(MIN_VALUE + pseudorandom(SEED) * (MAX_VALUE + 1))
card.ability.extra.RandomOddsT = math.floor(MIN_VALUE + pseudorandom(SEED) * (MAX_VALUE + 1))
end,
wait r u the suits guy
yes
I'm currently trying to create a joker that will trigger an effect for the first card each of suit scored, what would the best way to do this be? I understand I need to use some sort of list but not exactly sure how.
this joker triggers on the first instance of each suit being scored
it can get a bit annoying with blueprint compatibility
if you want to do first card of each suit you'll also have to wrap your head around how you want to handle wild cards
thanks man
I was gonna mention how Flower Pot (the vanilla joker, not the stats-display mod) handles wild cards, but I realized that FP only works because it looks at the whole hand at once, whereas your idea instead looks at the hand in sequential order
if you care about the first instance of each suit being scored you can get away with figuring it out as you go in scoring
if you care about the first card with each suit scored you do kinda do something like FP
I was thinking of adding one tbh
and figure out what's what in context.before
would anyone be able to look at this and help me, trying to make it so the joker gains chips when jack of spades is played but it does not increase? have tried both of these to no success
Context before doesn’t have an other_card
where is your i coming from
Flower Pot first looks for all non-wild cards, determines which suits are accounted for, and then uses wilds to fill in the blanks (one suit per wild).
I guess what you could do is, use context.before to sort out which cards get scored, and give them a flag, use context.individual to proc the joker's effect on any cards with the flag, and then use context.joker_main to remove all the flags (so that you don't have things from previous hands getting scored again.
yep, thank you
that's much more complex than I thought, how much of the code is dedicated to dealing with wild cards?
card.ability.extra.wilds = card.ability.extra.wilds + 1
scores = true
elseif```
not much, blueprint is the bottleneck
have to separately keep track of what each different card has already done in one scoring pass so that it works properly with blueprint/brainstorm
How does yours deal with wildcards? Does it always trigger for wilds or only fill in the gap or trigger for wilds but has a limit of 4 total triggers?
I need to generalize it out to be able to handle any amounts of suits, but basically yes
if you think about it, a wild trigger is a wild trigger, it doesn't ever need to figure out what suit that actually was for the purpose of this joker
it will always trigger for a wild as long as it has triggered less than the total amount of suits in the game
ah ok, is that for if you have custom suits?
well, yeah, if it wasn't hardcoded as 4 I'd just check the number of suits in SMODS.Suits
awesome, do you mind if I mostly copy your code?
the following message is braptro approved
Ong
my facade of lua knowledge has been damaged
why is there a braptro cult now
Cause it being made by two incompetent individuals and someone who understands lua
*barely
not even joking this is the first time i've properly used lua so I'm kinda learning as I go
i am also incompetent
But are you brap enough
And part of the blala joker poker
the development resumes tomorrow
I mean if you are good at lua i we’ll send you a public copy tomorrow and ask you to make a simple joker possibly if @inland shard is fine with that
wat
oh is this a job interview
hi welcome im the manager what do you bring to the team

Lmao
I've been working on my own mod for the past month

Braptro?
Tell me more
eat pants
Wtf man
Me having 3+ mods in development: 💀
Great Scott!!!!
Your cooked
I know
and this one too
maxwell one was drawn by someone else
fire
do you like it
yeah
Can i show my shit off
yes
well yeah we all are already
i keep sending this but this is what i did in my mod
Wow 😭
Might make that an expansion in the near future if you want
what expansion 😭
Expansion are small samples of ideas
ah right
The braptro has one call mythic
I'm sorry i just am not that competent at what i do lmao
Which adds more rarity and challenges increasing the difficulty
Thats fine at least what you are make is really cool and i hope it goes well
Im going to sleep to gn
gn
sound scared me lowk
this is sick tho
(i hate chemistry laboratory class)
How often are destroy effects really a negative? I've been using them as a partial negative, but I'm considering whether I should refactor some destroy effects into permanent debuffs
Because cards that break are really just deck thinning, after all
yeah being able to control card deletion is insanely powerful
debuffing is however fucking brutal since a majority of scoring jokers are based on rank and suit, so there would need to be an insane effect for the debuffing to be a good balance for the joker imo
X2 mult per card played 😭
Hmm, yeah, control's a big part of it. Maybe I should leave the cards that randomly destroy a card to do just that.
I'll keep debuffing to a specific archetype. The fact that there are more methods of destroying cards does mean it's a bit less of a downside as well, after all
I think it's incredibly rare that destroying cards is a negative
Now pair it with this.
I'll change my jump scare effect to a debuff 🤔 that should make it a bit more of an actual trade-off
it does not matter if you play
high card with 4 hands
ok i think this is just too good
I actually like that alot it allows for some earlier flush five builds
sure go ahead
Awesome, thanks
coo
ok fixed
nvm where the FUCK do i
how do i destroy cards in the entire hand 😭
is G.play not it
Hi all! Im so sorry for the big post I don't mean to yap, i've just come across a werid problem.
I Was wondering if maybe I could get some insight when it comes to Balatro and Sound mods?
I was hoping to replace the BGM with some other choice selections. But what I came to find is that the audio for the mp3's wouldn't loop or be cut abruptly.
So far, from what I can tell the game has a max duration setting somewhere in the code? And all songs that the game uses are played at the same time, at the exact same length. So a song finishes (the shortest appearing to be 34 seconds) that is when the whole ost "loops"
Right now, I can only think of resolving this by cutting the ost into 34 seconds and making speed up and slow down variations of each. Im fine with that, but I was wondering if im just overlooking the setting that defines the cutoff for when a song might loop?
step 1: use oggs not mp3
there is no max duration for songs, they just loop when they finish
So is my problem for the abrupt audio cuts just the filetype?
or rather the failed loops?
and the ingame music is 4 minutes long
unsure, but please use oggs and not mp3s regardless
calculate = function(self, card, context)
if context.joker_main then
return {
xmult = card.ability.Xmult
}
end
if context.individual and context.cardarea == G.play and #G.play.cards == math.floor(card.ability.card_target) then
card.ability.Xmult = card.ability.Xmult + card.ability.extra
return {
message = 'Upgraded!',
colour = G.C.MULT,
card = card
}
end
if context.after and context.cardarea == G.play then
if context.destroy_card.ability.aiko_about_to_be_destroyed then
for i,k in ipairs(context.full_hand) do
k.ability.aiko_about_to_be_destroyed = true
k:start_dissolve({ G.C.BLACK }, nil, 4, false);
k:juice_up(0.1)
end
end
end
if context.destroy_card and context.cardarea == G.play and not context.blueprint and not context.destroy_card.ability.eternal then
if context.destroy_card.ability.aiko_about_to_be_destroyed then
return { remove = true }
end
end
end,
i will cry
Sorry, I know that was probably what I should use, but i was just in the stages of intial testing (rhythm games are that way to)
god
I guess, does anyone mod the balatro ost? I did search reddit but didn't see anyone mentioning touching that aspect. so I may just be in uncharted waters
there's a mod in the example mods folder
and several alt music mods in #1209506514763522108
and afaik only osu really uses mp3, most rgs running on unity and the like recommend oggs
ehhh ive seen high quality mp3s used
Ah, thanks sorry, its my first time trying to mod the game and it doesn't look like those threads are super active...but ill ask in there and hopefully someone bite. thanks again!
Forgive the early morning code yap, but I'm trying to have a joker do the following: if card held in hand is a number card, give +10 chips. if card held in hand is a number card and win a 1/3 chance, give 1 dollar
The chips part of this works, but not the dollars portion. Any help would be appreciated (I gotta try and split this up)
huh it won't let me copy paste
which part of the dollars doesnt work
if context.individual and context.cardarea == G.hand then
if context.other_card:get_id() <= 10 then
if context.other_card.debuff then
return {
message = localize('k_debuffed'),
colour = G.C.RED,
card = card,
}
else
return {
chips = card.ability.extra.chips,
card = card
}
end
if (pseudorandom('decree') < G.GAME.probabilities.normal/card.ability.extra.odds) then
if context.other_card.debuff then
return {
message = localize('k_debuffed'),
colour = G.C.RED,
card = card,
}
else
G.GAME.dollar_buffer = (G.GAME.dollar_buffer or 0) + card.ability.extra.dollars
G.E_MANAGER:add_event(Event({func = (function() G.GAME.dollar_buffer = 0; return true end)}))
return {
dollars = card.ability.extra.dollars,
card = card
}
end
end
end
end
end
})```
okay that works let me format it
just return dollars don't need dollar buffer
like this?
if context.other_card.debuff then
return {
message = localize('k_debuffed'),
colour = G.C.RED,
card = card,
}
else
return {
dollars = card.ability.extra.dollars,
card = card
}
end
end
end
end
end
})```
care to give me one sec to isolate between the return and odds real quick?
huh?
In my infinite wisdom, I actually had yet to check which part of the function wasn't working between those two
Thank you so much!
okay so it isn't returning the dollars value
should this not destroy the entire hand scored or not
uhh what are you doing exactly
if played hand contains 4 cards destroy the entire hand
yea
i'm sure that won't cause weird shit to happen all the time
just wanted to make sure they are extra dead so they don't become ghost cards
if i want to check if a sold card is a negative joker what do i add to the
if context.selling_card then
or is there a mod that checks if a joker specificaly is sold that i can check out?
if context.selling_card and context.card.ability.set == "Joker" and context.card.edition and context.card.edition.negative then
Is there a way to hide the Use button for a consumable?
hook to card:highlight
thanks so much
if i do this this happens
don't start dissolving or juice
returning { remove = true } already will trigger the destruction of the card
alr
I'm trying to add a Negative playing card tooltip to a joker, but for some reason it isn't showing up when I use e_negative_playing_card. It works perfectly with any other edition. Is there a different key for the negative playing card?
loc_vars = function(self, info_queue, card)
info_queue[#info_queue+1] = G.P_CENTERS.e_negative_playing_card
return {
vars = { card.ability.max_highlighted }
}
end```
is there something like this but instead of checking if its sold, check if its destroyed?
nope
shiet
enable SMODS.optional_features.cardareas.unscored and also check for context.cardarea == 'unscored'
is dissolve the only function that destroys the cards or are there any else?
oh great then thanks
do i enable this inside the main mod function
i never had to do this before
SMODS.optional_features.cardareas.unscored = true literally at the top of your main.lua or something
heads up, i missed an s
how can i get the name of the joker if its in a card
maybe i phrased it wrong
so like for example
or its id
its whatever just important its unique
you usually use .config.center.key for that
oh thanks
bump
also, how how do you add seal tooltips to Jokers? Like editions have G.P_CENTERS.e_edition and enhancements have G.P_CENTERS.m_enhancement
Yoo
I have like 95 questions.
- How do I use the config with UI
- How do I create/modify UI
- How do I create something similar to the Nope text from the wheel of fortune
- How do i check what cards the player has in the full deck
one of my concept jokers makes scaling jokers scale twice as quickly (so for example green joker would gain +2 mult per hand, wee joker would gain 16, etc) is this possible + how would i implement this like do i have to specify and modify every vanilla scaling joker
That would be crazy, and broken
lol
But i cant say anything. My jokers are broken(er?)
yeah so uh currently
it still acknowledges that a card is in the new card area
but it doesnt seem to render anything
odd
Joker?
nope, its an entirely new card area
the six elements, earth, fire, stars, more earth, mario and glass
its where the joker card area is
when you press the Show Gacha Results button, an UI comes down which takes its position
totally not inspired by joyousspring whatsoever
But why
UI is not easy 😭
- you'd rather die than do this
- you'd rather watch your whole family die than do this
return {colour = G.C.PURPLE, message = localize("k_nope")}(probably, if not this then something along these lines)- G.playing_cards
which uhhhh gachas ❤️
but basically currently, it only gives you a joker of the highest rarity rn
wtf kafka e2
uhh, how do i do the ui die thingos
which is why im adding the "Show Gacha Results" card area
it will add all the 5 jokers you rolled from the ticket there
then you can choose to add them to your joker area or not
the next time you use the ticket, everything in the gacha results area is wiped
that is if i can even see the card in the card area
odd
here's the PSA Slab Joker fully finished with the random card grading values for each copy of the joker
bro got blueprint AND brainstorm, damn.
I’M CRYINT
its a random rare joker after all
you should make a shader that adds a random smudge in some part of it that is barely noticeable like 1x2 pixels and then make it a psa 6 because of it
if I'm wanting to check that a card has NO enhancement, is checking something like other_card.ability == false possible, or do I have to manually input other_card.ability.name ~= "enhancement name"?
[card].config.center ~= G.P_CENTERS.c_base is how I do it
the oa6 synergy here is crazy
Thanks
though you may want to use SMODS.has_enhancement, which I think can also check for base
Depending on whether you wanna check for whether the card is enhanced, or is going to count for an enhancement
Just check if it's enhanced. The desired effect is unenhanced 2-5's gain extra chips
SMODS supports quantum enhancements which is for jokers that have effects like (all 7s count as Lucky cards). Just checking G.P_CENTERS.c_base will not count those enhancements. has_enhancement will. Though if you're not using quantum enhancements, it's really only something to think about when it comes to mod compatibility
Can you tell me more about those quantum enhancements?
I'm wanting to think about it now just in case, because several mods add unique enhancements. But stuff like "all x cards count as y enhancement" isn't what I'm looking to debuff necessarily. I'd only want to do something like if you had paradolia to turn cards gold through midas mask, detect that they are in fact golden and don't trigger the joker
So i didn't get answers for the UI part.
Does anyone maybe know
might help u
u can also check previews
I would, except that's all I really know about them so far. I haven't implemented them yet
yeah uh real quick, is this the correct check for 0.6% rng stuff?
randomRNG is pseudorandom
6 as in 6 or 0.006(percent)?
its 6
theres /1000 at the end to effectively cut it down to 0.6%
(at least, thats what i think it should do)
im honestly hitting that 0.6% a lot, not sure if im messing up something or its genuinely just my luck 😭
i printed all the numbers to make sure, it is indeed 0.006 after the /1000
maybe its specialpity?
theres 2 conditions
it seems tomorrow enough. how can we make it so a blueprint procs its effect three times instead of just once? tried to have 3 calls of other_joker_ret on line 169, which. did not work. ;P
about 18 uses of the item
so its definitely not the problem, i also printed SpecialPity whenever i used the item too lol
it does change everytime i use the item
hm
wata games moment
but maybe it is my luck, thats the hard part about debugging rng stuff
ok it's barely related
i just realized what psa slab is
thats how it works in almanac ig
it divides 1 by a probability
1/36, 1/50, 1/1000
and it also does < instead of <=
Love the effect and description haha
