#💻・modding-dev
1 messages · Page 631 of 1
ah wonderful, thank you
this is probably a stupid question with some less elegant ways to implement it,
but what would be a concise way to check if the player's joker slots contain at least 1 instance of a joker with a certain key
next(SMODS.find_card('j_modprefix_key'))
how do i get if a card is debuffed?
if card.debuff
ty
how do i make something happen before all joker calculates? also, would a hook or patch be more fitting
i wanna add a juice_card_until thingy to my mechanic but i don't wanna add it manually into each joker i use said mechanic with
(my upgrade mechanic thing)
wait
ik a mod i could reference that's done something like this
nvmd i just looked at that code and that doesn't help
actually after would work more
okay i think it's eval_card
No way another british person
what am i doing wrong 😭
local ec_ref = eval_card
function eval_card(card, context)
local ec = ec_ref(card, context)
return ec
end```
yeah 😔
I love how thats the instant reaction
local oldevalcard = eval_card
function eval_card(card, context)
local g, post = oldevalcard(card, context)
return g, post
end
ty
so i did this to make a certain joker always spawn as negative, but it also does it in the collection. how do i make it not do that in the collection?
set_ability = function(self, card, initial, delay_sprites)
card:set_edition("e_negative", true)
end,
if card.area and not card.area.config.collection then
Actually that might not work if set_ability is called before the card is placed in an area
that seems to break it
doesn't become negative at all
okay, now it's making the card shake a not-so-normal amount
local oldevalcard = eval_card
function eval_card(card, context)
local g, post = oldevalcard(card, context)
if (card.config.center.slime_upgrade and card.config.center.slime_upgrade:can_use(card)) then
juice_card_until(card, function() return not G.RESET_JIGGLES end, true)
end
return g, post
end```
That happens when juice_card_until is called multiple times
Yes, you're calling juice_card_until for every time the card is calculated for any context.
how should i correctly handle this then
Pick a context for when you want it to happen
how do i make this work everytime i load my mod with this code it says attempted to load a nil value
you're using the load file function wrong, but you don't need to manually load image files anyway
you do need to have the images separated into a 1x and 2x version in the assets folder, but the game will automatically load everything properly placed in the assets folder
I do have a 1x and 2x version in my assets folder the reason there is a load fuction is because im trying to make these appear when im doing crossmod with allinjest
just put the AltTexture in the check for the mod
what might be preventing a soul card from creating my custom legendary joker
SMODS.Joker { --Archibald
key = "archibald",
loc_txt = {
name = "Archibald",
text = {
"Disables the price scaling of",
"{C:green}Rerolls{} in the shop",
"{C:inactive}(Minimum cost of {C:money}$1{C:inactive}){}"
},
unlock={
"{E:1,s:1.3}?????"
}
},
rarity = 4,
cost = 20,
blueprint_compat = false,
pos = { x = 5, y = 0 },
soul_pos = { x = 5, y = 1 },
atlas = "jimbos",
unlocked = false
}
local enlightenment_comedian_oldcalculatererollcost = calculate_reroll_cost
function calculate_reroll_cost(skip_increment)
if (next(SMODS.find_card('j_en_comedian_archibald')) and G.GAME.current_round.reroll_cost > 0) then
return enlightenment_comedian_oldcalculatererollcost(true)
end
return enlightenment_comedian_oldcalculatererollcost(skip_increment)
end
so i changed it and removed the stuff above but now it crashes when i open the texture menu and still none of the spectrals have their textures changed unless you meant something else?
Is there anything I can look to help me show dynamic text on a joker using loc_vars?
context.cardarea == 'unscored'
how do i destroy em
context.destroy_card and context.cardarea == 'unscored' and context.other_card, return {remove = true}
may i interest you in
calculate = function(self, card, context)
if context.destroy_card and context.cardarea == "unscored" and not context.blueprint then
return {
remove = true
}
end
end
and tbh i find 100% destroy rate to be a bit OP
50% seems much more realistic
its for a temporary joker
oh neat
it only works for 3 hands then it dies
yeah mine is for this:
neat
would anyone know why this doesn't work?
unlocked = false,
locked_loc_vars = function(self, info_queue, card)
return { vars = { 18 } }
end,
check_for_unlock = function(self, args)
return args.type == 'discover_amount' and #G.P_CENTER_POOLS.Spectral <= args.spectral_count
end
crashes when i try to load the game, despite it following the same format as vanilla rewritten does for the cartomancer unlock
Log?
i'm gonna try using the achievement criteria since that's an overlap here
check_for_unlock = function(self, args)
return G.DISCOVER_TALLIES.spectrals.tally/G.DISCOVER_TALLIES.spectrals.of >= 1
end```
so this works fine but crashes upon profile reset, attempt to index field DISCOVER_TALLIES
how do i increase hands and discards for the current blind
ease_hands_played and ease_discard
k
fixed it, was missing args.type == 'discover_amount'
how would i do something when a joker is being destroyed i.e. by madness/dagger?
for k, v in pairs(G.P_CENTERS) do
if v.set == 'Joker' then
if (not v.mod) then
G.GAME.banned_keys[k] = true
end
end
end
how would i change this deck apply into only accepting jokers from a single mod, as it accepts all modded jokers right now.
should do if v.original_mod and v.original_mod.id == "modid" then
how do i detect if you've bought a spectral card in a G.E_MANAGER:add_event?
How would I get the current position of the card in it's own calculate?
this?
for i = 1, #G.jokers.cards do
if G.jokers.cards[i] == card then
my_pos = i
break
end
end```
ty this worked well
the original joker still shows up for some reason though
Is there anything out there that implements like, a divide mult or divide chips score modifier???
xmult = 1/(dividing amount)
You know what that makes sense
But how does that show up on the joker description?
Does it provide a divide symbol or do I have to format it myself
you gotta format it yourself
something like {C:white,X:mult}/#1# {}Mult or something
Yes Ive dont that before, Used that today for armor piercing rounds
Making a risk of rain 2 full content mod
-# bump
I am not the one to help lols
how do you play a sound without delay when you're also changing the mult/chips
hi how do you play the juice up anim on the blind score
How would the code look like if I want the joker to activate on any hand except a specific one
like something like "Gains +10 mult if a played poker hand is not a straight"
look at ortalab
if context.before and context.scoring_name ~= 'Straight'
thank you 🙏 🙏 🙏
original joker as in
? he's the fallback whenever the game tries to generate a joker with an empty pool (e.g. it decides to generate a Rare but your mod has no Rares)
the only way to block Jimbo entirely is to ensure some other joker can always appear
How do I get the loc_txt of a joker?
G.localization.descriptions[set][key]
tyyyyyy
calculate = function(self, card, context)
if context.initial_scoring_step then
local old_mult = mult * 2
local old_chips = hand_chips *2
mult = mod_mult(old_mult)
hand_chips = mod_chips(old_chips)
return {
message = "Doubled!",
pitch = 1,
}
end
end,
i have this that doubles the base chips and mult, how do i make it keep doubling as the round goes on (and make it reset at the end of round)
(so a lvl 1 high cards becomes 2 - 10 4 -20 8 -40 etc)
Why aren't you doing Xmult_mod = 2 and Xchip_mod = 2?
uh
i copied this from another joker i have and it works so i didnt really ask questions tbh
You would put a variable in the config and increase it every hand, and do Xmult_mod = 2^variable and Xchip_mod = 2^variable and you would reset it at context.end_of_round and context.main_eval
mult = mult * Xmult_mod,
hand_chips = chips * Xchip_mod,
mult works, chips gives an error on chips
return {
Xmult_mod = 2^variable,
Xchip_mod = 2^variable,
}
when i use Xmult_mod/Xchip_it doesnt do anything at all
Code?
config = {
extra = {
multiplier = 0
}
},
loc_vars = function(self, info_queue, center)
return { vars = { center.ability.extra.multiplier } }
end,
calculate = function(self, card, context)
if context.before then
card.ability.extra.multiplier =
card.ability.extra.multiplier + 1
end
if context.end_of_round or context.main_eval then
card.ability.extra.multiplier = 0
end
if context.initial_scoring_step then
local multiplier = card.ability.extra.multiplier
return {
card = card,
Xmult_mod = 2 ^ multiplier,
Xchip_mod = 2 ^ multiplier,
message = "Doubled",
colour = G.C.MULT
}
end
end
``` this doesnt do anything
if context.initial_scoring_step then
local multiplier = card.ability.extra.multiplier
local Xmult_mod = 2 ^ multiplier
local Xchip_mod = 2 ^ multiplier
return {
card = card,
mult = mult * Xmult_mod,
hand_chips = Xchip_mod,
message = "Doubled",
colour = G.C.MULT
}
end
however this one doubles the mult (idk the correct way for chips so that one doesnt) but the multiplier stack doesnt work (everything same as above)
Hello, I have an issue with a shaders that i've created for an edition. i don't understand why i have that beacause i don't have a mouse_screen_pos variable in my shader.
are you trying to make it exponential?
would this work (btz_gokus is an objectType)
yes
no
What about writing down every member of that object type?
does your object type have only jokers
Yes
then i get this
ok now i've fix my issue but i have an other problem, i can send my codes if it's helpful
You need to use the galactic variable in your shader or delete it iirc
I think your variable may need to be named galactic rather than galaxy
you can loop through G.jokers.cards and check if joker.config.center.pools.btz_gokus
...just check if the given center actually has pools.
forgot about that, so if (joker.config.center.pools or {}).btz_gokus
how do you disable the standard message of adding mult or chips?
How would I keep track of jokers that have been sold or destroyed
not like the number, I mean I wish to keep track of the 2 most recent jokers/consumables/cards that have been sold/destroyed
is there a variable of how much cards got sold this run?
how can i get if a card has been discovered or not?
like as in a for loop
for i = 1, G.jokers.cards do
how do I do soul layer
ohh i see ty
soul_pos = { x = x, y = y },
how come sound = 'key' and play_sound() result in completely different volumes
is the mod "balatro goes kino"compatible with cryptid?
former seems to be much quieter than the latter
found it
Is there a way to make it so when playing a hand, instead of using a poker hand's score, to add up the chips and mult of each poker hand that composes the played hand?
For example when playing a Full House, instead of doing 40X4, it uses the chips and mult of High Card, Pair, Two Pairs, Three of a Kind and Full House making it 5+10+20+30+40 X 1+2+2+3+4.
Obviously a mod that would do such a thing should change the poker hand's scoring values to balance things.
Hi, I'm trying to make a mod to procedurally generate Jokers, here's what I got so far (textures are mostly for testing purposes for now)
However I'm running into a few issues
One being the annoying little border that gets added to the outline of different Sprites
And the other is the tilt not being properly applied from the parent card
oh hell nah we got JokNFT
That is not the intent but unfortunately feels similar
how do i have unmodifiable values in a joker (as in other jokers can't value manipulate them)
often the standard for ignoring value manip is to set immutable to true in the smods.joker initialization
to draw layers on top of cards while keeping the tilt, you can look at how the game draws stickers onto jokers (in card.lua)
basically something like
sprite.role.draw_major = self
sprite:draw_shader('dissolve', nil, nil, nil, self.children.center)
or if you only want certain values the standard for that is an “immutable” table, similar to the “extra” table
thanks lily
Is the dissolve shader the one handling the tilt??
Otherwise I'm also rendering them with the center as the parent
Oh wait draw major
I didn't read that part my bad
yes
balatro is in a 2d engine, the shader pipeline and speicfically the dissolve shader is what simulates the 3d effect
how do i select a random consumable pool
nvm found it G.P_CENTER_POOLS.Consumeables
Thank you so much I spent more than an hour on this
Now I only have the remaining issue of borders around sprites, if anyone knows what causes that?
Could it be due to my png atlas being weird?
Or is it something to do with rendering
I had that issue on a previous mod as well where I created a custom deck skin
Here's another example of the issue
Thank you, I will look into this when I have the time
I see the issue is what I expected
should this part in vanillaRemade have parenthesis for the function?
That is valid Lua syntax, parentheses are optional
Especially for SMODS.create_card since it only takes one parameter of data type table
how do you use variables in jokerforge? and how do you display them visually?
the jokerforge thread in #1209506514763522108 or the jf discord server (linked in that thread somewhere iirc) is probably a better place to ask
i dont see a jokerforge official thread in there, just a bunch of creations made with jokerforge
ah it's because there's a space
https://discord.com/channels/1116389027176787968/1386843228330004611
nvm forgot the space lol
yea lol
what area should this playing card have to go to my deck (Also, I have an emplace function afterwards to send it to my hand. Is this right)?
Area shouldnt be a string literal
You can use SMODS.add_card instead to automatically emplace it somewhere
how do i make a joker that, when sold, replaces all held jokers with their specific counterpart jokers?
if context.selling_self then
for k, v in pairs(G.jokers.cards) do
if counterpartjokertable[v.config.center.key] then
v:set_ability(counterpartjokertable[v.config.center.key])
end
end
end
how should i set up the ability and counterpartjokertable?
how does in_pool work for backs
how would i prevent it from spawning a specific vooucher
How do I pass a given card's table as an argument to SMODS.add_card()?
counterpartjokertable = {
j_joker = 'j_modprefix_jokercounterpartkey'
}
You mean you want to copy a card?
I wanna create a copy of a specified card and add it to deck.
Without drawing it to hand.
copy_card(card)
is there a context for entering a shop
context.starting_shop
yea the calculate functions wiki page is woefully outdated
eremel's working on rewriting it, just give him some time
I've got this code excerpt from something that creates a copy of the player's deck when the Blind is selected, but it doesn't work.
local mirror_card = copy_card(card_to_mirror, nil, nil, G.playing_card)
mirror_card:add_to_deck()
I'm basing it on Ortalab's Rouge Rose.
i want to make reroll surplus not appear when my deck is used and taking ownership of it seems excessive
i tried doing this and it crashed when selling the joker, is there anywhere specific i should put the joker table
@daring fern, can I pass copy_card(card) as an argument to SMODS.add_card()?
And how does SMODS.add_card(card) differ from card:add_to_deck()?
SMODS.add_card creates a card, emplaces it, and adds it to deck, Card:add_to_deck() triggers the card's add to deck effects.
set_blind = function(self)
ease_hands_played(self.config.extra.mod_hands)
local list_of_mirror_cards = {}
for _, card_to_mirror in pairs(G.deck.cards) do
G.playing_card = (G.playing_card and G.playing_card + 1) or 1
local mirror_card = copy_card(card_to_mirror, nil, nil, G.playing_card)
mirror_card.ability.LAPSEMS_world_mirror = true
list_of_mirror_cards[#list_of_mirror_cards + 1] = mirror_card
end
for i = 1, #list_of_mirror_cards do
G.E_MANAGER:add_event(Event({
trigger = 'after', delay = 0.05,
func = function()
SMODS.add_card(list_of_mirror_cards[i]) -- This line produces a crash.
return true
end
}))
end
self.original_deck_size = G.GAME.starting_deck_size
G.GAME.starting_deck_size = #G.playing_cards
G.deck:shuffle('bl_lapsems_final_mirror')
end,
Rewrote it a bit, and it crashed without giving an error message.
set_blind = function(self)
ease_hands_played(self.config.extra.mod_hands)
local list_of_mirror_cards = copy_table(G.deck.cards)
for _, card_to_mirror in pairs(list_of_mirror_cards) do
G.playing_card = (G.playing_card and G.playing_card + 1) or 1
card_to_mirror.ability.LAPSEMS_world_mirror = true
end
for i = 1, #list_of_mirror_cards do
G.E_MANAGER:add_event(Event({
trigger = 'after', delay = 0.05,
func = function()
SMODS.add_card(list_of_mirror_cards[i])
return true
end
}))
end
self.original_deck_size = G.GAME.starting_deck_size
G.GAME.starting_deck_size = #G.playing_cards
G.deck:shuffle('bl_lapsems_final_mirror')
end,
Tried the first batch of code that I posted, and it does produce an error message when it crashes the game.
set_blind = function(self)
ease_hands_played(self.config.extra.mod_hands)
local function better_copy_card(card, new_card, area)
if not card then return nil end
local area = area or (new_card and new_card.area) or card.area or G.jokers
local cardwasindeck = new_card and new_card.added_to_deck or nil
local copy = copy_card(card, new_card)
if new_card and cardwasindeck then copy:remove_from_deck() end
if card.playing_card then
G.playing_card = (G.playing_card and G.playing_card + 1) or 1
copy.playing_card = G.playing_card
G.deck.config.card_limit = G.deck.config.card_limit + 1
table.insert(G.playing_cards, copy)
end
if (new_card and cardwasindeck) or not new_card then copy:add_to_deck() end
if not new_card then area:emplace(copy) end
return copy
end
for k, v in pairs(G.deck.cards) do
better_copy_card(v).ability.LAPSEMS_world_mirror = true
end
end
Replaced my set_blind() with this code, but the game froze.
Also, this is what Rouge Rouse uses:
local card = SMODS.add_card({set = 'Base', area = G.deck, suit = suit, rank = rank})
card.ability.rouge_rose = true
currently trying to discard certain ranks after hand is played, in a fashion similar to The Hook. I've narrowed it down to this specifically crashing it when it runs but the error log isn't giving me anything to go off of, just that the highlighted bit crashes
says attempt to index local 'card', a nil value
You can't put cards in other cards.
what does that mean in this context? i was under the assumption that one of these is a list of cards (queue) and one of them is just the array of highlighted cards
tried refactoring it to be more like the vremade hook and still same issue,
more of the code if it helps
it might be struggling to give selected card the value?
local passed = false
for k, v in pairs(G.hand.cards) do
if v:get_id() == 5 then
passed = true
v.area:add_to_highlighted(v, true)
play_sound('card1')
end
end
if passed then G.FUNCS.discard_cards_from_highlighted(nil, true) end
oh shit that worked great! thanks
just spent like ages troubleshooting cause localthunk misspelled hierophant in the code
If I had a penny for every generated Jokers-like mod I'd have 3 😅 (I'm one of them) Can't wait to see how you'll handle yours
SDM_0 THE LEGEND
I have some ideas, but the graphical part is so fun that I might focus on that first for a bit
I'm a sucker for procgen
Wha
im going to try rotating a joker again
but where is the rotate
everyone just said smods.drawstep last time i asked and there's nothing in the documentation for angle
OKay so two things :
Is there a way to change the spawn rate of a voucher or a consumable if you own a joker ?
I know i could use something like if next(SMODS.find_card("j_splash")) then to check if a joker is present but i wouldn't know about the rest
there's not a built in function to my awareness
you may have to patch something in somewhere
what if i try to play with the weight ?
something among the lines of
get_weight = function(self)
if next(SMODS.find_card("j_splash")) then
return (0* self.weight) + 1
else
return 0 * self.weight
end
end,
is get_weight valid on vouchers and consumeables? the documentation is really unclear on this
yeah i don't think it is
only modifiers, vouchers, and rarities (but not cards within the rarity)
seems like a feature worth adding someday
well, weight is about spawn rates right ?
maybe it could work out ?
i guess right now the only really easiest way would be to do a special pool for it ?
oh wait
in pools
what if i try to play with the weight ?
something among the lines of
in_pool = function(self, args)
if next(SMODS.find_card("j_splash")) then
return true
else
return false
end
end,
the card couldn't spawn unless the joker is present
i guess it's worth to see if it does work in that logic
though
HOLY FUCK IT ACTUALLY WORKS
I made a quick test with planet cards, in that case there's only pluto since it's the default card and i've all planet cards in my consumable slots
and here i've added splash from the code and now the consumable spawns
I mean i guess ?
I just wanted to make so my consumable wouldn't appear unless a very specific joker is in the joker slots
you can make an infinite money chain if you thin pool down to emporor and hermit
oh yeah,
i mean my goal here was just to see if my code to make a consumable appear only if a joker is present to work or not
that would be a good seeded run
cool
it's probably the easiest lines of code too so i didn't actually expected it to work
i'm gonna do this now lol
i want infinite money
i might be the first too...
sure
fellow linux players what was the balatro mod directory again
as payment ill give PeggleSetup.exe
/home/(your username)/.steam/debian-installation/steamapps/compatdata/2379780/pfx/drive_c/users/steamuser/AppData/Roaming/Balatro
yk theres a way easier way to say /home/(your username) right
Perhaps
also its ~/.local/share/Steam/steamapps/compatdata/2379780/pfx/drive_c/users/steamuser/AppData/Roaming/Balatro/Mods/ for me
~
yeah that's the same, just username and the mods folder, should have added that
i'd like to create an unlock for an enhancement. if im not mistaken, enhancements can't do unlocks, right? I remember trying at one point, at least...
i could pretty easily do some trickery and swap the sprite and description using loc_vars, which would make it look like it was locked. but how would I do an unlock popup like using unlock_card? would I have to make the UI myself, or is there something else I can do?
I've asked this before, but maybe someone else will know what to do. I'm trying to overwrite the base game's blank card sprite so it is either accepting a new sprite in its place or make it invisible (whatever is easier). I can provide an example/photo to explain what I mean if anyone is confused. I already the new sprite made, but I don't know how to overwrite the sprite.
Using Malverk?
how would i make a joker return a different string when its in the collection
i have a randomized hand type when you encounter it but i want it to explain that in the collection
i can think of a way: use the card area of the joker to determine it. in loc_vars, if the area is in your jokers or in the shop slots, return one key for your loc which displays the hand. otherwise return a different key that displays an explanation
i dont know of one off the top of my head
i imagine its somewhere in the code but
its def in the code yeah
kk sweet
maybe G.shop_jokers or smth
im trying to just find the one thats for the dictionary
cus it' dbe a lot safer to use an if statement to check that
edge cases
i'm digging through the code rn and i see G.your_collection
which is being indexed so i assume its a table of some kind that contains cardareas
G.DISCOVER_TALLIES.jokers
it seems like it iterates through this?
which is weird
but im thinking that might just be some sort of tallying method
thats just implemented weird
possibly
Maybe I should implement alternate collection loc vars like blinds have 🤔
thats what i was thinking yeah
maybe...
ive run into this situation more than once (but usually i just work around it instead of implementing anything into the joker to actually do it)
i tried to implement it with the collection loc_vars shit
but it didnt work and i was like
'huh. thats weird'.
lol
it seems like checking if card.area == G.your_collection doesn't do anything
which is weird since it definetly is handled as an area
ohhhhh lmao
u could just iterate and test them all
and thatd do it
i assume its one cardarea per row
could also jus look through the joker one
but then
that'd not work with the 'mods'' part of the mod would it
hmmm
where it show sall the additions
sweet
Why does the Card with this Edition never trigger? Or more specific, how do I only make it trigger when it is it's turn?
You should be able to do card.area.config.collection iirc
fair
now i want to make it only show in inactive color if its in the collection
time for evil code
real quick how do i make the text wiggle inside a joker text box
i thought it was v:1 but that isnt workin
i think it's E:1
get a role in id:customize
Where in the code does mult and chips get multiplied together
i think functions/state_events.lua when SMODS.calculate_round_score is called
but not 100% sure
I already have a role
but I have not selected which.
although I do have an ipad which I ahve not installed balatro on it.
civil chatter doesn't give reaction permissions i guess
you need to pick one of those there
how do I check if you ended a round using your most played hand?
oh yea ive been meaning to ask
@tired kestrel how do you change the balatro background into a different shader again? ive been wanting to do that for my end except the background would change based on a bunch of parameters (like how high you score, or if a certain shop is active)
I think you have to basically make splash.fs or background.fs and basically add in the shader you wanted. But you also have to make sure to sue the mix and set it to 1 so that way while the oriignal background is used without error you can basically set the other one to the max to show the background you desire.
The last part is basically having to configure about how you want it to make it. cause if you do't do modifications you'll get like the basic one since right now the brightness is handled by the splash.
I don't know if there'd be a proper tutorial
or if I would ever make a proper tutorial as it seems advanced to do but you have to like learn about how shaders work
luckly enough there is this example that someone replaced a backgrounds: https://github.com/Kekulism/Vortex
alright. though even though I only did this for my Drac-amet mod but there's a bit more to go through. But yeah I wish you luck and maybe hopefully there is a better way to replace the vortex in more easier way
if context.end_of_round and (G.GAME.last_hand_played == G.GAME.current_round.most_played_poker_hand) then
--
end
for some reason it does the effect as many times as there are cards that remain in the hand at the end of the round
and... now it does nothing...
yea end_of_round runs for context.individual too
add and not context.individual and it should be fixed i think?
yea it's not doing anything
-- Dragoon
SMODS.Joker{
key = "dragoon",
atlas = "comedy_mythical_jokers",
pos = { x = 1, y = 0 },
soul_pos = { x = 2, y = 0 },
unlocked = false,
blueprint_compat = true,
rarity = "comedy_mythical",
cost = 30,
config = { extra = { exp_mult_gain = Comedy_checkDifficulty(0.05, 0.1), exp_mult = 1 } },
loc_vars = function(self, info_queue, card)
return { vars = { card.ability.extra.exp_mult_gain, card.ability.extra.exp_mult, colours = {ComedyColors.EMULT} } }
end,
calculate = function(self, card, context)
if context.end_of_round and (G.GAME.last_hand_played == G.GAME.current_round.most_played_poker_hand) and not context.individual then
card.ability.extra.exp_mult = card.ability.extra.exp_mult + card.ability.extra.exp_mult_gain
return { message = localize { type = 'variable', key = 'a_exp_mult', vars = { card.ability.extra.exp_mult } } }
end
if context.joker_main then
return {
exp_mult = card.ability.extra.exp_mult
}
end
end
}
here's the code if it can help with fixing anything
try context.main_eval instead of not context.individual
also fyi that most played hand value resets per ante iirc
since its only used for the ox
all other things use a for loop to find the most played hand
how would I calculate the most played hand that way?
that seems wrong
wait okay there was a syntax error in the file and that apparently just made the file not load rather than error directly
thats odd
bump
don't assert, just let it crash
then you'll have an actual crash log
or remove the message from assert
fyi, SMODS.load_file returns two things
both the file as a function and an error if one arises
ahh okay, ty both
so you probably want to be doing
local file_data, err = SMODS.load_file(path..".lua")
if err then error(err) end
yeah that's better than what I said
cryptid also does this for its recursive loader
fwiw the wiki only mentions a single return value for load_file, i suppose that should be amended sometime
(though it then mentions love.filesystem.load, which would probably yield the correct return results if i had checked that)
trying to make it so that if you have 3 very specific jokers, it combines them into a fourth very specific joker
-- in a function that essentially hooks into Game:update(dt)
-- checking if you have all dragoon parts
local dragoonParts = {"j_comedy_dragoon_claw", "j_comedy_dragoon_body", "j_comedy_dragoon_wings"}
if Comedy_containsAll(Comedy_checkForJokers(dragoonParts), dragoonParts) then
Comedy_fuse_jokers(Comedy_checkForJokers(dragoonParts), create_card(nil, G.jokers, nil, nil, nil, nil, "j_comedy_dragoon", 'mythical_obtain'))
check_for_unlock { type = 'comedy_spawn_mythical' }
end
-- in utils.lua
-- util function to check if you have a specific list of jokers equipped
-- will be used especially for stuff like Mythical Joker creation
function Comedy_checkForJokers(jokers)
local output = {}
if type(jokers) ~= 'table' then return {} end
if not G.jokers or not G.jokers.cards then return {} end
for _, joker in ipairs(jokers) do
if joker and joker.config and joker.config.center and joker.config.center.key then
local id = joker.config.center.key
if next(find_joker(id)) then table.insert(output, joker) end
end
end
return output
end
-- destroys a joker
function Comedy_destroy_joker(joker)
if joker then
G.E_MANAGER:add_event(Event({
trigger = 'after',
delay = 0.15,
func = function()
if joker and not joker.removed then
joker:start_dissolve({ G.C.SPECTRAL, G.C.WHITE })
joker:remove_from_deck(true)
G.E_MANAGER:add_event(Event({
trigger = 'after',
delay = 0.5,
func = function()
if joker and joker.area then joker.area:remove_card(joker) end
return true
end
}))
end
return true
end
}))
end
delay(0.6)
end
-- will destroy the input jokers, and create the output joker
function Comedy_fuse_jokers(input, output)
-- makes sure both args actually exist
if not input then return end
if not output then return end
if type(input) ~= 'table' then return end
for _, joker in ipairs(input) do
Comedy_destroy_joker(joker)
end
if output then
SMODS.add_card(output)
end
end
-- checks two tables
function Comedy_containsAll(t1, t2)
local set = {}
for _, v in ipairs(t1) do set[v] = true end
for _, v in ipairs(t2) do
if not set[v] then return false end
end
return true
end
It doesn't do anything when I have all of the necessary jokers. Is there any place that I might have messed up
-# doing this in a game:upadate hook because if I had each individual piece check in the add_to_deck, it might accidentially create 3 copies of the thing
Comedy_checkForJokers seems to be expecting a table of cards, yet youre giving it a table of strings
it doesnt really do anything anyway
ohh
you definitely should change that function
i was about to ask
since currently all its doing is checking the keys of inputted cards and then checking if theres a joker with that key
which will be the case 99% of the time
also the function also seems a bit redundant, for the fusion check you could just do if next(SMODS.find_card("j_comedy_dragoon_claw")) and next(SMODS.find_card("j_comedy_dragoon_body")) and next(SMODS.find_card("j_comedy_dragoon_wings"))
and for the fusion you can then do it like this instead
SMODS.destroy_cards(SMODS.find_card("j_comedy_dragoon_claw"))
SMODS.destroy_cards(SMODS.find_card("j_comedy_dragoon_body"))
SMODS.destroy_cards(SMODS.find_card("j_comedy_dragoon_wings"))
SMODS.add_card{ key = "j_comedy_dragoon", area = G.jokers }
-- will check if you have the requirements to fuse the jokers
-- mainly for mythical shards
function Comedy_fusionCheck(parts)
if (not parts) or (type(parts) ~= 'table') then return false end
local canFuse = true
for _, part in parts do
if not SMODS.find_card(part) then
canFuse = false
end
end
return canFuse
end
-- checking if you have all dragoon parts
local dragoonParts = {"j_comedy_dragoon_claw", "j_comedy_dragoon_body", "j_comedy_dragoon_wings"}
if Comedy_fusionCheck(dragoonParts) then
Comedy_fuse_jokers(dragoonParts, "j_comedy_dragoon")
check_for_unlock { type = 'comedy_spawn_mythical' }
end
I went and did this now, and the second I booted up the game, I got a crash
oops
i sometimes forget that I'm not coding in python or java
so...
I got it to work
but now when you have the 3 things, it will keep making an endless supply of the Dragoon until after the dragoon parts have been destroyed, but only because it caused an error
did you forget to return true in the event
I did now, but for some reason it creates the thing even if the jokers don't exist
so even if you didn't have the 3 pieces, it will just keep creating an endless amount
SMODS.find_card at least returns an empty table
so you need a next
you should also probably set a flag while the fusing is happening so it doesnt keep happening until the destruction animation finishes
where would I put that flag?
probably in the update hook when fusion starts
and then check if the flag isnt set before doing fusion
you could also just handle fusing in the add_to_deck for each of the jokers actually
so you dont have to worry about it running repeatedly
yea but wouldn't that just make it create 3 copies?
no
which context. should I use for before cards are scored but after the contained poker hands are put into context.poker_hands?
add to deck only runs when you obtain the card
which would only do something on the last part you obtain
context.before?
Doesn't work since I'm using SMODS.current_mod.calculate()
if you're trying to add chips/mult, use context.initial_scoring_step instead
but otherwise yea context.before should be perfectly fine
When using it, context.before is before a score reset step
yea that's intended behavior
use context.initial_scoring_step if you need to modify any scoring
that is not related to the specific calculate yueah
(resetting the score after context.before allows e.g. the level up from space joker to actually apply to the played hand)
Thanks, I've tried initial_scoring_step but apparently context.poker_hands is not populated
or I'm doing something wrong
show your code
I want to add mult for every poker hand contained, I'm currently testing it before making it a joker
function SMODS.current_mod.calculate(self, context)
if context.initial_scoring_step then
local nbph = 1
if context.poker_hands then
for _, h in ipairs(context.poker_hands) do
nbph = nbph + 1
end
end
return {mult= 10*nbph}
end
end
context.poker_hands isn't an integer indexed table
so ipairs wont do anything to it
you want something like this
for _, hand in pairs(context.poker_hands) do
if next(hand) then nbph = nbph + 1 end
end
Thanks! But what is the underlying structure of poker_hands?
{
['Pair'] = {{Card, Card}, {Card, Card}, {Card, Card}},
['Three of a Kind'] = {{Card, Card, Card}},
['Flush'] = {},
-- etc
}
I believe that is how a three of a kind would look
There's no ['High Card'] ?
SMODS.Joker{
key = "dragoon_claw",
atlas = "comedy_mythical_jokers",
pos = { x = 0, y = 0 },
soul_pos = { x = 0, y = 0 },
unlocked = true,
blueprint_compat = true,
rarity = "comedy_mythical_shard",
cost = 10,
config = { extra = { xmult = 2 } },
loc_vars = function(self, info_queue, card)
return { vars = { card.ability.extra.xmult, localize('j_comedy_dragoon')['name'] } }
end,
add_to_deck = function(self)
if Comedy_hasJokers({"j_comedy_dragoon_claw", "j_comedy_dragoon_body", "j_comedy_dragoon_wings"}) then
G.E_MANAGER:add_event(Event({
trigger = 'after', delay = 0.5, blocking = false,
func = function()
SMODS.destroy_cards(SMODS.find_card("j_comedy_dragoon_claw"))
SMODS.destroy_cards(SMODS.find_card("j_comedy_dragoon_body"))
SMODS.destroy_cards(SMODS.find_card("j_comedy_dragoon_wings"))
SMODS.add_card{ key = "j_comedy_dragoon", area = G.jokers }
check_for_unlock { type = 'comedy_spawn_mythical' }
return true
end
}))
end
end,
calculate = function(self, card, context)
if context.joker_main then
return {
xmult = card.ability.extra.xmult
}
end
end
}
for some reason even if it does have all 3 when the third one is added to the deck, it will not do anything
How do I make my deck block everything added from different mods (excluding my mod)
apply = function(self, back)
for k,v in pairs(G.P_CENTERS) do
if v.original_mod and v.original_mod.id ~= "your_mod_id" then
G.GAME.banned_keys[k] = true
end
end
end
depending on the timing of things, you might have to put it in an event to make it wait til G.GAME.banned_keys actually exists
what does your hasJokers function look like?
function Comedy_hasJokers(jokers)
if (not jokers) or (type(jokers) ~= 'table') then return false end
local hasJokers = true
for _, joker in ipairs(jokers) do
if not next(SMODS.find_card(joker)) then
hasJokers = false
end
end
return hasJokers
end
I believe the add to deck function is called before the card is added to the area, so this function will never be able to find the card that is calling it
try just checking for the other 2 cards
This blocks consumables and vouchers too right?
yep
the only things not in G.P_CENTERS are blinds and tags, iirc
Thank you!
how would I have it wait until after the jokers are destroyed before creating the fused joker?
create the joker in an event
how do i use "is a pair" as a context (not contains)
context.scoring_hand will be a string containing the name of the hand that it actually counts as
use that in whatever timing you have
context.scoring_name == <poker_hand>
Does anyone know the problem with this
You need to use brackets.
Where?
if context.debuff_card and (context.debuff_card:is_suit('Spades') or context.debuff_card:is_suit('Clubs'))
Thank you
like this?
Yeah, that'd probably do it. I cribbed that excerpt from To Do List.
A question of my own: just what can you pass as an argument to SMODS.add_card()?
wdym?
I ask 'cause I'm creating a Showdown Blind that gives the player a copy of their current deck, which I based on Ortalab's Rouge Rose.
Here's what Rouge Rose uses to assemble the original deck:
local suits = {'S','C','D','H'}
local ranks = {'2','3','4','5','6','7','8','9','10','J','Q','K','A'}
for _, suit in ipairs(suits) do
for _, rank in ipairs(ranks) do
G.E_MANAGER:add_event(Event({
trigger = 'after', delay = 0.05,
func = function()
local card = SMODS.add_card({set = 'Base', area = G.deck, suit = suit, rank = rank})
card.ability.rouge_rose = true
return true
end
}))
end
end
Cryptid, you mean? I was told much the same.
But it doesn't work when I pass a card (in table form) to SMODS.add_card().
Is there anyone here who might?
i dont really know any very good modders
You use copy_card for copying cards, not SMODS.add_card
But can I copy a card with copy_card() then pass it as an argument to SMODS.add_card()? 'Cause I don't know of any other way to add cards to the deck upon blind select, other than how Rouge Rose did it.
is there a way to get the level of a played hand
No, CardArea:emplace(card)
how do I get exponential mult to work without having to use/require Tailsman?
Need I replace CardArea with, say, G.deck?
mult_mod = (-mult)+mult^number
G.GAME.hands['Pair'].level
yes
this ended up happening:
INFO - [G] 2025-12-11 18:56:14 :: ERROR :: StackTrace :: Oops! The game crashed
main.lua:2020: [SMODS ComedyGold "include/utils.lua"]:467: attempt to perform arithmetic on global 'chips' (a nil value)
You and @daring fern, that did correctly clone the deck, but now the cards won't go away! (This was likewise adapted from Rouge Rose.)
disable = function(self)
if not G.GAME.blind.disabled then
LAPSEMS.disable_blind_reset_mult()
self:defeat()
for _, card in pairs(G.hand.cards) do
if card.ability.world_mirror then
card:shatter()
end
end
G.STATE_COMPLETE = false
G:update_draw_to_hand()
end
end,
defeat = function(self)
for _, card in pairs(G.deck.cards) do
if card.ability.world_mirror then
card:shatter()
end
end
for _, card in pairs(G.discard.cards) do
if card.ability.world_mirror then
card:shatter()
end
end
G.GAME.starting_deck_size = self.original_deck_size
end
oh wait that's for the wrong thing. Either way, it ended up subtracting all of the mult first
how do i make a joker spawn mercury
SMODS.add_card({key = 'c_mercury'})
how would I do that for chips?
chip_mod = (-hand_chips)+hand_chips^number
Nevermind, I figured it out: I was looking for card.ability.world_mirror instead of card.ability.LAPSEMS_world_mirror.
how would you have a context for when a steel card is activated
if context.individual and context.cardarea == G.hand and not context.end_of_round and SMODS.has_enhancement(context.other_card, 'm_steel')
don't forget and not context.end_of_round
oh theres no way to outside of just checking if youre holding a steel card?
yea there's no post_trigger for playing cards like there is for jokers
uhh how would you check if its debuffed then
context.other_card.debuff
thx
No, debuffed cards don't score.
guys this joker isnt appearing ingame what did i do wrong
is it in the collection
discovered equals false
make it true unless you want a unlock method
if you want a unlock method make one smh
you're thinking about unlocked
discovered just means it shows as "Undiscovered Joker" until you find it
blundering
I want to set up jokers that have an alternate joker. And then joker where selling it replaces those jokers with the alternate jokers if they are held in your hand. Is this possible?
I tried to set it up and it crashes with "attempt to index 'counterpartjokertable'"
It looks like G.P_CENTER_POOLS doesn't have the table of all possible poker hands. Where are they kept?
G.GAME.hands
SMODS.PokerHands
other answer works too
The other answer froze my game.
though different structure and SMODS.PokerHands gives you the initial hand stats
Code?
eval G.GAME.hands
and allows you to screw with calc
Nevermind, I ran it again and it didn't freeze.
so like it only matters if you need to do something with stats or calc
How do I localize G.GAME.last_hand_played? Running localize(G.GAME.last_hand_played) merely gives "ERROR".
localize(G.GAME.last_hand_played, "poker_hands")
if context.selling_self then
for k, v in pairs(G.jokers.cards) do
if counterpartjokertable[v.config.center.key] then
v:set_ability(counterpartjokertable[v.config.center.key])
end
end
end
Does anyone know how to set this up?
how do i update the text for G.GAME.chips to fit the actual number?
does anyone have the documentation for how remove_card works cause im apparently misusing it
You mean CardArea:remove_card?
Yes, it just removes it from the CardArea.
what would i use for destroying cards then
Card:remove()
hmm, im trying to remove every other card, and im just iterating through cards[i], tho
can i still reference a card by its position in the card area
cards[i]:remove()
cards[i] is a card
you can remove it
ok so remove doesnt take an argument then
so itd be G.jokers.cards[i]:remove() then
yes
alright thank you
I'm having an issue with a joker of mine where when you sell it, it crashes the game for some reason. Below is the error and code
if context.joker_main and not context.blueprint then
if card.ability.retriggers >= 13 then
local eval = function(card) return not card.REMOVED end
juice_card_until(card, eval, true)
end
end
if context.selling_self then
if card.ability.retriggers >= 13 then
SMODS.add_card ( {key = "j_DRY_Sithe_NEO" } )
end
end
Are you calling SMODS.has_enhancement somewhere
yes
calculate = function(self, card, context)
if context.repetition and context.cardarea == G.hand and SMODS.has_enhancement(context.other_card, 'm_steel') and not context.end_of_round then
if SMODS.pseudorandom_probability(card, "Sithe_steel_retrigger", 1, card.ability.odds, "Sithe_steel_retrigger") then
card.ability.retriggers = card.ability.retriggers + 1
return {
repetitions = card.ability.extra.repetitions,
}
end
end
dose anyone know of a modded joker who's effect uses two separate poker hands in it's ability?
What for?
i'm reworking seance to trigger after having played a flush and a straight, but i don't know the loc_vars well enough to make the extra value work
Should be the same as seance? Just with more and different entries
Tho even still, I’m confused. Do you mean a flush and a straight between two different plates hands?
yes
this is my current setup in the loc_vars
config = {extra = {poker_hand = 'Straight Flush'}}
i don't know how i would have split this into straight and flush so i did this instead
Not sure why you’re passing in self.ability.hand_tracker at all
to update the card text
so how would i do it? because i do want to replace the text
you literally just need to remove self.ability.hand_tracker from the return statements
and then in the localization itself, put #1# where you want those strings to go
like that?
yep
local pokerhands = {}
for k,v in pairs(G.GAME.hands) do
if SMODS.is_poker_hand_visible(k) then
pokerhands[#pokerhands + 1] = v
end
end
chosen_hands = {}
while #chosen_hands < card.ability.extra.hands do
hand_to_add = pseudorandom_element(pokerhands, 'c_lapsems_skaro')
if not LAPSEMS.contains(chosen_hands, hand_to_add) then
chosen_hands[#chosen_hands + 1] = hand_to_add
end
end
for i = 1, #chosen_hands do
SMODS.smart_level_up_hand(card, chosen_hands[i], false, card.ability.extra.levels) -- This line produces a crash, "attempt to index a nil value". What do I do?
end
so every time i hover over the card in my collection. the game crashes, ask if you need the full crash log
even after the change
Replace pokerhands[#pokerhands+1] = v with table.insert(pokerhands, k)
Figured that one out a split second before you replied. Thanks anyway, though.
Is that the only place? Cus the crash log says that you're passing nil to has_enhancement somewhere. Also you're not on latest smods release
For that Joker its the only instance of has_enhancement, there are others in the mod but only crashes when I sell the joker and a custom variable is greater than 13. Also am aware on the outdated smods, I just use multiplayer a lot so like to be back that far...plus if I dont have ploblems back there it shouldn't cause any in future, I'd think at least
Maybe you sent the wrong crashlog then? Cus I don't know what else it could be
no, I copied and pasted what it gave me...suppose I'll check the other enhancements though
these parts of my code crash the game when i hover over seance, when removed it defaults back to the original extra value... what would i need to change to fix the crashes?
card.ability.extra.hand_tracker instead
didn't work 😭
Did you start a new run
i'm looking through the collection
is there a crash log
Are you taking ownership of a joker
yeah
can you send the full code
hello
no
loc_vars and the config part itself has been my road block
anyone can help me with this?
move the card.ability.extra.fuerza = 0 above the return, you don't need to return the value, hope that helped :)
Just to show all instances of has_enhancement
1.
if context.repetition and context.cardarea == G.hand and SMODS.has_enhancement(context.other_card, 'm_steel') and not context.end_of_round then
if SMODS.pseudorandom_probability(card, "Sithe_steel_retrigger", 1, card.ability.odds, "Sithe_steel_retrigger") then
card.ability.retriggers = card.ability.retriggers + 1
return {
repetitions = card.ability.extra.repetitions,
}
end
end
2 & 3 (another card).
if SMODS.has_enhancement(context.other_card, 'm_steel') then
for k, v in pairs(G.I.CARD) do if v.config and v.config.center and v.config.center.key == 'm_steel' then v.ability.h_x_mult = 3 end end
end
if context.repetition and context.cardarea == G.hand and SMODS.has_enhancement(context.other_card, 'm_steel') then
return {
repetitions = card.ability.extra.repetitions
}
end
Number 2 doesn't seem to have a context check, unless you didn't copy it fully
im gonna try, but thanks in any case
I'm honestly not sure, I think that it's trying to call the original loc_vars and failing cus the config changed, but I dunno why that'd happen since I haven't used take_ownership very much
is there a way you think i can get around this?
above it is just the calculate function
Then yeah that's wrong, context.other_card only exists in certain contexts, like individual and repetition
Not really, I'd need to test it on my pc but I can't today
well non the less this is progress lol, thank you :)
thats weird then, cause it works on the one prior. I have below
calculate = function(self, card, context)
if context.repetition and context.cardarea == G.hand and SMODS.has_enhancement(context.other_card, 'm_steel') and not context.end_of_round then
if SMODS.pseudorandom_probability(card, "Sithe_steel_retrigger", 1, card.ability.odds, "Sithe_steel_retrigger") then
card.ability.retriggers = card.ability.retriggers + 1
return {
repetitions = card.ability.extra.repetitions,
}
end
end
and
calculate = function(self, card, context)
if SMODS.has_enhancement(context.other_card, 'm_steel') then
for k, v in pairs(G.I.CARD) do if v.config and v.config.center and v.config.center.key == 'm_steel' then v.ability.h_x_mult = 3 end end
end
if context.repetition and context.cardarea == G.hand and SMODS.has_enhancement(context.other_card, 'm_steel') then
return {
repetitions = card.ability.extra.repetitions
}
end
Wdym one prior?
The other two has_enhancement calls check if context.repetition before trying to access context.other_card
ohhhhhhh, okay
so what could I do to make the enhancement call work on the 2nd???
and more importantly, how would 2 jokers affect each other in such a way?? I guess it could be that it transforms into the other
I'd say first just try removing that 2nd part entirely and see if that fixes it. I'm assuming you were testing while having both jokers tbh
Nope, I only had one, but one transforms into the other when selling which may be why it triggered upon selling
Oh. Is the joker you spawn when selling the one with the missing context check
If so yeah that is definitely why
nope, still returns a nil somewhere it says
What'd you change
wait, I'm dumb and forgot to save...hold on lol
I got rid of the bit with doing a check for enchants only, I'll let ya know how it goes
okay, that did solve it
so I gotta figure out how to phrase below so it works
calculate = function(self, card, context)
if SMODS.has_enhancement(context.other_card, 'm_steel') then
for k, v in pairs(G.I.CARD) do if v.config and v.config.center and v.config.center.key == 'm_steel' then v.ability.h_x_mult = 3 end end
end
Do you want to make all steel cards give only 3 mult instead
yes
I have it on an add to deck but only applies to the ones in hand, not all in the deck
I'm super tired and I don't wanna think, so I'm gonna give you the easiest but worst way of doing it. Loop through G.playing_cards in update, check if steel and change
I can also save for tomorrow if you want, I have no deadlines and am just doing this for fun lol
Knowing the problem will help a TON at least
result of my labor, card will at least shake, though no actual value tracking :(
this séance sounds much better than the current one at least
did the mult with suit cards get changed (lustly greedy etc)
can't open GitHub rn
I need help trying to make an unlock based on the edition you bought. appernetly I tried many times to make it work but somehow sometimes it worked even without polychrome or it doesn't work.
I tried lovely patch and functions but none of them worked.
or did work
just not the way I thought it would supposed to be.
i played the game when they gave 4 mult and it was very broken times, but they will cost 4 instead of 5 :)
eary game that is
I mean 15 mult on a flush hearts is cool for checkered players
can anyone help with the unlock funtion or how it work?
i can't help but this might
they pretty much belong to early game because we have onyx agate
yeah fair point
WAIT, rework to give CHIPS
no i forgot about the arrow
oops lol
if they are for early game, i could make them give mult on play instead of score >:)
but that could restrict some builds... this needs to be carefully approached
I tried but for some strange reason it didn't work in my favor somehow and I probably need help with this
Thank you once again, that's exactly what I was looking for, it fixed my issue and my boys are looking sharp
well looks like someone beat me to it
totally not like i gave up my own
local original_get_new_boss = get_new_boss
SMODS.Joker {
key = "jam_sybil",
loc_txt= {
name = 'Jam Sybil',
text = { "",
},},
atlas = 'regaliadeck',
pos = { x = 3, y = 2 },
rarity = 2,
cost = 5,
pools = {["pseudoregamod"] = true},
unlocked = true,
discovered = false,
blueprint_compat = false,
eternal_compat = true,
perishable_compat = true,
--- the function
config = { extra = {}},
loc_vars = function(self, info_queue, card)
return {vars = {}}
end,
function get_new_boss(self)
--if G.GAME.always_spawn_cymbal_boss_memory == true then
if G.GAME.always_spawn_cymbal_boss_memory then
return 'bl_cymbal_boss_memory'
end
return original_get_new_boss(self)
end,
add_to_deck = function(self, card, from_debuff)
G.GAME.always_spawn_cymbal_boss_memory = true
end,
remove_from_deck = function(self, card, from_debuff)
G.GAME.always_spawn_cymbal_boss_memory = false
end,
}
seriously what am i doing wrong, set the local outside but now it's something else
put the function definition outside of the joker declaration
so i should but the entirely get_new_boss function outside ?
is there a way to check what joker calls a function (such as get_id() )
Ah i see, and that's where add_to_deck and remove_from_deck will do the job by executing the code outside it ?
what?
well that would only have the info of the card being checked
oh yeah true
i need to know what joker is calling the function
hm
im considering a few things but if I want this idea to be future proof id need to like
get that info or lovely patch every instance of get_id to also return the joker's info when calling it
setting the function outside will give me something like
local original_get_new_boss = get_new_boss
function get_new_boss(self)
--if G.GAME.always_spawn_cymbal_boss_memory == true then
if G.GAME.always_spawn_cymbal_boss_memory then
return 'bl_cymbal_boss_memory'
end
return original_get_new_boss(self)
end
SMODS.Joker {
key = "jam_sybil",
loc_txt= {
name = 'Jam Sybil',
text = { "",
},},
atlas = 'regaliadeck',
pos = { x = 3, y = 2 },
rarity = 2,
cost = 5,
pools = {["pseudoregamod"] = true},
unlocked = true,
discovered = false,
blueprint_compat = false,
eternal_compat = true,
perishable_compat = true,
--- the function
config = { extra = {}},
loc_vars = function(self, info_queue, card)
return {vars = {}}
end,
add_to_deck = function(self, card, from_debuff)
G.GAME.always_spawn_cymbal_boss_memory = true
end,
remove_from_deck = function(self, card, from_debuff)
G.GAME.always_spawn_cymbal_boss_memory = false
end,
}
with only G.GAME.always_spawn_cymbal_boss_memory being there to activate the said code
no idea but card:get_id might be run a lot of other times from non-joker places
which might be jank
ye
mmh
i want the gimmick of my new project mod to be that you can modify the mentioned ranks of jokers
well i guess i better try, at least it won't run with syntax now it seems
like cloud nine could be changed to another rank for example
cant you just modify the joker's ability.extra?
it would be feasible to manually implement this on my jokers and the vanilla ones, but the feature would be pretty annoying to implement on modded jokers
no
almost every rank checking joker hardcodes the rank checking
everyone does it
it makes value manip so hard
not me
well ur not supposed to change the rank value normally
like what if it becomes 4.5
now your joker is broken
thats why we hardcode those
thats not your joker's fault, though
its fully on the value manip which should be better implemented
it is if you didn't round the value
the value manip can't interpret what value you used for what
its a simple hook anyways
thanks, it does work now, i keep getting the same blind as long as i've the card
anyway i would have no way to modify anything hardcoded which is the majority
so it would be nice to be able to intercept and modify the rank checks
no the function that gets ranks is get_id
i would need jokers to return their info in get_id to be able to do this
which I could patch get_id to allow, but that would still require people to manually add it to their jokers 🤔
that is probably the easiest method
oh yeah fuck i forgot the question
local key, i = nil, 0
while not key do
i=i+1
local name, value = debug.getlocal(2, i)
if not name then break end
if type(value) == 'table' then
if name == 'self' then
if Card.is(value, Card) then
key = value.config.center.key
elseif value.key then
key = value.key
end
elseif name == 'card' then
key = value.config.center.key
end
end
end
```?
what is this
what does it do
It looks for a card in the previously called function's local variables.
but i would need the info of the joker that is calling it
get_id would only have the info for the card being checked wouldn't it
Yes, that's what it should do.
In a Card:get_id() hook.
Is there a way to make a boss blind not appear trough the regular ways ?
If i set the min_requirement to a high value like 999 that should do it no ?
try setting in pool
oh so just a
in_pool = function(self, args)
return false
end,
should work then riight ?
should be
Yes it works thanks
if voucher.args then
insert(tempPool, voucher)
end
end```
idk why the game does this when i try do to it
i have a voucher in the area
It's table.insert not insert
yeaaaaa i changed it last minute but before that was using table.instert
this crash is due to a pseudorandom_element call you have in Empowered.lua
oh I see, you're passing an empty table to it, presumably tempPool
my game crashes whenever i play a high card
and when i turn the volume to 0 it doesnt crash
OMG yes
bruh
i think i changed it to be good but it was giving the exact same error and i thought it was the same thing again
anyways back to my coding of doom and despair
are you the developer of the mod you're using
you just made me realise thats the wrong chat 💔
also one more question is voucher.args correct? or does it do nothing? its supposed to check if the voucher can be scaled for the card not to pick something like magic trick to duplicate but idk if im doing it right
uhm, I don't think so
what do you mean by it can be scaled
like getting more than 1 does something?
yes
if so you'll probably just have to manually make a list of allowed vouchers to duplicate
or the opposite, a list of blacklisted vouchers
yes but that kinda leaves a gap for modded ones
vouchers just don't have that kind of information on them
by making a blacklist you'll allow all modded jokers, and theoretically, you can blacklist vouchers for mods you want to be compatible with
yea youre right ill do it that way
i mean vouchers
o7
I figured out shaders for hue shifting each separate joker part
You'll probably have a different take on the idea, there's a lot of ways to make it work
I only focused on the graphical side and I have yet to figure out exactly how they're gonna generate effects
Hey folks, does anyone know if extern MY_HIGHP_OR_MEDIUMP number time; is updated every frame in mods? I've ported over a Shadertoy shader to Balatro but it seems to be stationary, I use it by itself as well as with sin
Are you working on some sort of joker art randomiser
Joker randomizer period, a set number of random jokers would be added to the pool in a run and get randomized every run
Hey, I want this tag to only trigger when you use a consumable, doing nothing until then (Like how the double tag does nothing until you get another tag)
Anyone know what I'm doing wrong? Rn it doesn't crash, but it also never triggers either
(this is how it looks more scrolled down, if that helps)
HAHAHAH
How do I make it that it says "Applied to all previous stakes" instead of "Applys gold stake"
return {
descriptions = {
Stake = {
stake_vremade_black = {
name = "Black Stake",
text = {
"Shop can have {C:attention}Eternal{} Jokers",
"{C:inactive,s:0.8}(Can't be sold or destroyed)",
"{s:0.8}Applies all previous Stakes",
},
},
Thats from vanilla remade
put that in a en-us.lua file
and have that in a localisation folder
oh. alright. I have not made the localization folder yet but alright
you probably should
also I also should also make a localization of all the stuff I've written into
Tags are very weird in that they do not use traditional contexts. You will probably need to do something like this to make it work (obviously editing to your needs)
For further context (not the game term), the first screenshot is using a mod calculate object, where Maximus in this case is SMODS.current_mod. Mod calculate is a very nice tool to have once you learn it exists. The loop is the most important part, as this is how tags are typically applied. The type will be the context you’ll look for. In your case, probably type = ‘using_consumable’, and you’d probably run this under the traditional if context.using_consumeable then check. The second screenshot is a tag’s apply code where you will check for context.type == ‘using_consumable’
since I'm doing localize is there a way to reuse this?
What would be the best way to translate this to a Joker?
Cos I wanna make a joker that turns into one of four specific jokers when blind is defeated
is there a context that happens after playing cards are scored but before context.joker_main
trying to make a joker that copies abilities of both adjacent jokers... i feel like this code is correct but it does not seem to be working?
the printed effects table looks to indeed be a proper list of effects (jolly joker and regular joker used here for testing)
granted, i'm referencing doodle from all in jest for this code, and last i played a run with it that joker didn't work either i think lol
ah merge_effects is just yielding nil here hm
oh it's supposed to receive a single table listing the effects, rather than passing the effects in as arguments directly
i feel like that is not documented correctly?
is it possible to tell if a card triggered another joker
Hey everyone, sorry to bother you again but I updated my mod with more jonklers and got this wierd crash message
the joker I had was supose to make a negative food joker when blind is selected but the game just crashed
code?
context.post_trigger and context.other_context.other_card?
or context.other_context.card for some contexts
Hey now that the joker atlas for my mod is getting REALLY big, it's starting to slow down load time quite a bit. I don't really feel like checking but if I wanted to optimize for loading time, would it be more efficient to split it into multiple atlases?
Idk why it would, just figured id ask
and the object type?
object type?
the pool
the pool in the joker should not have an extra mod prefix
oh, all the other jokers have that problem cause thats just how joker forge does things and that wasn't caused problems
how do i check for an exact hand (instead of containing a hand)
if context.scoring_name == 'modprefix_key'
ty
help still not working
i tried move the card.ability.extra.fuerza = 0 to diferent part but still no working
You're returning in the second if so the third if is never reached.
Thanks, very grateful
idk how optimizing atlases works exactly but that may or may not help
try it and see i suppose
what would i be doing wrong here
is there an faster way to test things than restarting the game every time i change something
anyone know why pressing the button crashes the game?
-- Shop thingy :)
function create_UIbox_becca()
return create_UIBox_generic_options({
no_back = true,
contents = {
UIBox_button({ button = "elle_rebecca.exit_shop", label = { "Exit" }, minw = 5, colour = G.C.RED })
}
})
end
-- Button Callbacks
G.FUNCS.elle_rebecca = {
-- Leave shop
exit_shop = function(e)
if not e then return end
G.FUNCS.exit_overlay_menu()
end
}```
Log?
how could i check if a joker is the first one of that specific joker in my joker slots? specifically to fix a bug that happens with part of the code if you have two or more of them, i wanna disable that part of the code if it isn't the first instance of that joker
if card == SMODS.find_card(self.key)[1]
hey so how do you directly set the # of chips and mult mid hand
ive tried doing it in a event and not and the next calculation still acts as though it wasnt set
mult_mod = (-mult)+number
this worked perfectly tysm!!
wait arent you supposed to call mod_mult() tho
No.
bbbbut vanillaremade explicitly says to
also whats mult_mod
It changes the mult, without a message.
how is that different to just setting mult directly
also do you return it in the table or
yea it goes in the return table
You shouldn't do it like that.
No, it's chip_mod
oh right
so chip_mod/mult_mod supports negative values but is otherwise identical to mult?
i've given the log, any update?
(and doesnt send a message)
No, they are the same except mult_mod has no message.
mult doesnt support negatives tho
????? its done nothing when i tried before
What line is that in the lovely dump?
No, it's 996 not 966
if context.before and not context.blueprint and
SMODS.pseudorandom_probability(card, "nflame_mcbedrock", 1, card.ability.extra.chance) then
card.ability.extra.chip = card.ability.extra.chip + hand_chips
card.ability.extra.mult = card.ability.extra.mult + mult
return { mult_mod = -mult, chip_mod = -hand_chips, message = localize("k_upgrade_ex") }
end
``` ok so the mult mod seems to do literally nothing, not even change the scoring number
wait is hand_chips and mult set during before?
context.before is to early to be changing the mult and chips.
so how would i change it before the first card is scored
It would be context.initial_scoring_step
Before the played cards score.
The chips and mult do not get reset if you change them.
is that the only diff
Just G.FUNCS.elle_rebecca_exit_shop directly.
yea pretty much
although it's directly after context.before, so e.g. a space joker to the right of something with context.initial_scoring_step would still fire off before the joker you're making
hmmm
so context.before for like anything that doesnt affect this hand
and context.initial_scoring_step for anything that does
yep
Returning is always better.
aaalr
so just return { mult_mod = newmult - mult } basically
where you want to set it to newmult
Yes.
ok cool
i don't understand ui i think
function create_UIbox_becca(card)
return create_UIBox_generic_options({
no_back = true,
contents = {
{n = G.UIT.R, config = {align = "cm", padding = 0.1, nodes = {
{n = G.UIT.T, config = {align = "cm", padding = 0.1, colour = G.C.GOLD, text = card.ability.extra.test}}
}}},
UIBox_button({ button = "elle_rebecca_exit_shop", label = { "Exit" }, minh = 0.6, minw = 10, colour = G.C.FILTER })
}
})
end```
you specified nodes in config which is wrong
np
how do i get a shadow to appear under my node?
{n = G.UIT.R, config = {align="cm", outline=1, padding=.15, r=.1, emboss=.5, hover=true, shadow=true}, nodes = {```
(the one with an outline)
print(count)
print(card.ability.extra.levelreq)
print(count / card.ability.extra.levelreq)
print(math.floor(count / card.ability.extra.levelreq))
print(math.floor(count / card.ability.extra.levelreq) ~= 0)
print('WHAT')
am i going insane or is the math genuinely just not mathing
what do you mean 0 ~= 0
Do you have Talisman installed?
i do
could be that but i dont recall talisman breaking this when i first coded it
ok yeah its talisman
figures, how is it breaking it though
count and levelreq are both always just normal numbers, did they change math.floor somehow?
Yes, but it doesn't affect normal numbers.
actually, count is affected by G.GAME.hands[hand].level its probably that
ok putting a to_big() around count did it
whoops
thanks lad
Could any of you say why this doesn't automatically debuff the rightmost Joker during this Boss Blind? I adapted the code from Bunco's The Wind, which disables the leftmost Joker instead.
local LAPSEMS_original_game_update = Game.update
function Game:update(dt)
--[[
This is utilized by The Blood.
--]]
if G.GAME and G.GAME.blind and G.GAME.blind.name == 'bl_lapsems_blood' and G.jokers and G.jokers.cards then
if not G.GAME.blind.disabled then
for i = 1, #G.jokers.cards do
if i == #G.jokers.cards then
G.jokers.cards[i].ability.bl_lapsems_blood_chosen = true
else
G.jokers.cards[i].ability.bl_lapsems_blood_chosen = nil
end
end
else
for i = 1, #G.jokers.cards do
G.jokers.cards[i].ability.bl_lapsems_blood_chosen = nil
end
end
end
LAPSEMS_original_game_update(self, dt)
end
It's G.GAME.blind.config.blind.key not G.GAME.blind.name
Still ain't debuffing it.
Instead of using G.jokers.cards[i].ability.bl_lapsems_blood_chosen use SMODS.debuff_card
Running SMODS.debuff_card(G.jokers.cards[i], true, self.key) crashed the game.
UPDATE: Replacing self.key with 'bl_lapsems_blood' fixed it!
...But now the debuff persists after the Blind is defeated.
Yes, you need to set the debuff with that source to false in the defeat function of the blind.
I did just that...
disable = function(self)
for _, joker in ipairs(G.jokers.cards) do
joker.ability.bl_lapsems_blood_chosen = nil
SMODS.debuff_card(G.jokers.cards[i], false, 'bl_lapsems_blood')
end
end,
...but the game still crashes.
Replace G.jokers.cards[i] with joker
The game no longer crashes, but the joker doesn't get its debuff removed.
If false ain't truthy, then what is?
flabbergasted
have you ever
coded before???
Yes!
Nevermind your astonishment at my not grasping these things. What do I do instead?
booleans exist
false is one half
true is the other half
in lua, everything but false and nil is truthy (counts as true)
therefor just shove true in there
But I already used SMODS.debuff_card(joker, true, 'bl_lapsems_blood') to apply the debuff to begin with!
Do I swap the true for nil instead?
I did.
try this again but replace _ with i
Didn't work.
Code?
I followed @spiral mural's instruction.
disable = function(self)
for i, joker in ipairs(G.jokers.cards) do
joker.ability.bl_lapsems_blood_chosen = nil
SMODS.debuff_card(G.jokers.cards[i], false, 'bl_lapsems_blood')
end
end,
shove in a print statement (and have debug+ installed) and see if its even running
Do you also have that in defeat?
disable = function(self)
for _, joker in ipairs(G.jokers.cards) do
joker.ability.bl_lapsems_blood_chosen = nil
SMODS.debuff_card(joker, false, 'bl_lapsems_blood')
end
end,
defeat = function(self)
return self:disable()
end,
local LAPSEMS_original_game_update = Game.update
function Game:update(dt)
--[[
This is utilized by The Blood.
--]]
if G.GAME and G.GAME.blind and G.GAME.blind.config.blind.key == 'bl_lapsems_blood' and G.jokers and G.jokers.cards then
if not G.GAME.blind.disabled then
for i = 1, #G.jokers.cards do
if i == #G.jokers.cards then
G.jokers.cards[i].ability.bl_lapsems_blood_chosen = true
SMODS.debuff_card(G.jokers.cards[i], true, 'bl_lapsems_blood')
else
G.jokers.cards[i].ability.bl_lapsems_blood_chosen = nil
SMODS.debuff_card(G.jokers.cards[i], false, 'bl_lapsems_blood')
end
end
end
end
LAPSEMS_original_game_update(self, dt)
end
So, you're stumped?
print(joker.ability.debuff_sources)
Within disable()?
Yes.
Why is the UI aligning like that. Turned on the outline on the ROOT node specifically to see that it's going outside the create_UIBox_generic_options for some reason??
Nvm I just realized that I can do it without create_UIBox_generic_options and it kinda gives me more freedom
I'm using cryptlib for thislua {"{C:attention}All jokers owned, cards in deck, and consumables owned{} gain {C:attention}x#1#{}values"}
how do i affect cards in deck and consumables
Guys I have a question when it comes to like applied stakes, when you try to make the text into like "Applies to all previous stakes"?
other than having to manually do locazlize ports.
It’ll add the applied line for you iirc
I see. though right now I kinda wished there'D be a way for loc_text to replace the applied gold stake to whatever previous stakes you had
but now I have to basically do the whole localization thing in motion espeically having the custom deck_locked things
eremel do you have an idea on how custom deck_locked worked in general?
What do you mean by this?
And no, not off the top of my head - I don’t really lock anything in the stuff I have made because I don’t think it’s ideal design for modded content
I mean like is there a way to replace "Applies Gold Stake" with "Applies to all Previous stakes"?