#💻・modding-dev
1 messages · Page 509 of 1
😭
/j /j
" upg
rade"
and
"ugprade"
not exactly, after an amount of time it changes a random thing negatively, and the time before the next negative thing is shortened. Eventually the timer will shorten to 0 seconds and that ends your run
another tip, fix your dang indentation vro 🥀
hmmm bepis this is not the way I would have gone about making an easing progress bar 🤣 I'd have just made the current version work with easing
ok i formatted
and does that mechanic currently work?
for the most part, yes
-# (only 1 minor issue)
i mean, im leaving the original progress bar code separate from the ease one in case people might want to switch to the former :3
you can actually change all progress_bar to ease_progress_bar and there wont be any issue i think
haya
me when my entire joker says chips, but the dastardly mult_mod wasn't changed:
its in the examples okay
I would just make progress_bar ease by default and have a flag to make it not ease
look at vanillaremade instead PLEASE
do i just replace mult_mod and chips_mod with chips and mult
yeah
kk
wtf are these
mult_mod is for some message or sound fuckery
yeah you can do that easily since the code is there, i was kinda unsure about changing every progress bar to use the easing lol
well right one is just orange stake
stakes
but they arent in game
the right one is orange stake wdym
ik theyre stakes
well considering there are only 4 uses of it and they can't actually change when you can see them I don't think it'll matter 😂
i think progress bars should always have easing anyways
true
Returning to modding after a little hiatus so a bit rusty - I'm making a new deck, how can I modify the spawn rate for certain booster packs?
It's easy to access regular card types in the shop eg G.GAME.tarot_rate but what about specific booster pack types. Is there a way to eradicate certain ones from a run entirely?
probably remove them from G.P_CENTER_POOLS.Booster
now that the tangent about my horrid code is done, may i ask abt the "ROOT node without a parent" thing again?
add the key to G.GAME.banned_keys
why does the scaling effect not happen twice when i have both jokers (it happens once on ante end no matter what, but is supposed to happen twice when you have the sweden joker)
you can only return once i think?
oh, that exists?
yeah its used by challenges
yeah what he said then
thanks I didn't know about banned keys
ok i think i get it
how did it not crash......
it didnt
you can do this??
i think it might just basically ignore the do because theres no for loop
but yeah that shouldnt be there
vscode told me to do it
anything after a return will not run unless the return is in a false if statement
what why 😭
u might want to use SMODS.merge_effects in this case
if context.end_of_round and context.main_eval and context.beat_boss and not context.blueprint then
local ret = {{message = localize("k_upgrade_ex"), colour = G.C.MULT, card = card}}
card.ability.extra.mult = card.ability.extra.mult + card.ability.extra.mult_gain
if next(SMODS.find_card("j_tmod_sweden")) then
card.ability.extra.mult = card.ability.extra.mult + card.ability.extra.mult_gain
ret[#ret+1] = {message = "Teamwork!", colour = G.C.CHIPS, card = card}
end
return SMODS.merge_effects(ret)
end
probably smt like this?
how unexpected: 4 more errors have just shown up
and the one that balatro immediately throws at me makes 0 sense
i can already tell you are missing an end
ok ty
that exists?
ive never heard of context.start
i feel like it is
No.
this has a list of everything that gets put after context. near the top
:3 in my defense theres always a context i dont know out there
its not all documented, which is annoying
yay it all works excellently
*yet, eremel has a job
we would never add a context.start that's remarkably vague as to what it does
i know 😭
find the event you're trying to create a context for and slap a SMODS.calculate_context() in there
<@&1133519078540185692>
🔫
thanks :3
there is already a context for drawing cards
yeah i know thats the one i used
the arguments for calculate are wrong
OH YEAH
it should be self, context, card
im so bad at this sometimes
self card context
i recommend setting up lsp it autocompletes it for you
lsp?
what is it even supposed to autocomplete it never did that for me..
it gives suggestions at the very least
you have to make it autocomplete by pressing tab and stuff
literally all it ever did was be annoying turn my true's into tonumber when i try to press enter
even then, not everything shows up
lsp just ate my RAM and made my autocomplete super slow
if you type calculate = it should give you the option to autocomplete the signature if you set it up correctly
where am i supposed to set that up
it's in the link i just sent 😭
it doesnt autocomplete bro
well your vscode is cursed then
Then is mine rigged too?
where do i even put that i just get sent to this wierd settings page
ok another question: in
update = function(self, card, dt)
what does dt refer to
deltatime
delta time, time since the last frame
ah ok
oops i think i put the wrong one
is the path to smods the path to smods from the location of the luarc file or just the file path from the top directory
it's relative to the luarc yeah
its Preferences: Open Workspace Settings (JSON)
this is what i get for having my mod on my desktop huh
.. gaming 🎉
also where is lovely/dump
in the mods folder
because i did the smods stuff but now its complaining about that there is no global SMODS already
that means the path for smods is probably not correct then
i think it can also be the absolute path
also let it load for a bit because it takes some time to load all the lsp stuff lol
well that wouldve been good to know earlier 😭
im not sure tho i use the relative one because i just code in the mods folder lol
where should print() messages show up?
the lovely console or the game itself if you have debugplus
ok thanks
I swear I had it being in absolute paths
will debugplus override lovely displaying the prints()?
no
ok good
this is my luarc.json
{
"$schema": "https://raw.githubusercontent.com/LuaLS/vscode-lua/master/setting/schema.json",
"workspace.library": ["C:\\.lib/smods", "C:\\.lib/balatro","C:\\.lib/JokerDisplay", "${addons}/love2d/module/library"],
"runtime.version": "LuaJIT",
"runtime.special": {
"love.filesystem.load": "loadfile"
},
"workspace.checkThirdParty": false
}
oh yeah i removed the runtime version because it was complaining a lot for some reason
I have this a code, How would go with adding an extra button but for only one specific joker?
wrap the if condition in if self.config.center.key == "j_modprefix_key" then
also i recommend making the menu function a local or giving it a more unique name
yea, i meant to do that
I should also start with actaully coding a second button to appear
hi, what can i do to stop a vanilla blind from appearing in pool?
i would like only certain blinds to appear if a specific joker is owned
Boss blinds at least
Currently the code just spawns the sell button
G.P_BLINDS.bl_key.in_pool = function(self) return false end?
Thank you!
Also you should probably hook get_new_boss instead.
Ive got this which i know is wrong as a second button isnt appearing but im not sure why
wait its overlapping
1s
bump
chaning the second button to br didnt help so im not sure how to fix it
what would i have to do to alter Judgement to only pull jokers from a specific pool
what i have:
key = 'boxofcats',
set = 'tarot',
atlas = 'carrot',
pos = { x = 0, y = 2 },
use = function(self, card, area, copier)
G.E_MANAGER:add_event(Event({
trigger = 'after',
delay = 0.4,
func = function()
play_sound('timpani')
SMODS.add_card({ set = 'Joker' })
card:juice_up(0.3, 0.5)
return true
end
}))
delay(0.6)
end,
can_use = function(self, card)
return G.jokers and #G.jokers.cards < G.jokers.config.card_limit
end
}```
Change Joker to the pool key.
bump :3c
even when pixel smoothing is off there is still some blurring between pixels, does any know why this is or where it comes from in code?
(found it its G.CANVAS:setFilter('linear', 'linear'))
How would I make a voucher make it so that all Mult Gained is +50% more?
Hook SMODS.calculate_individual_effect
SMODS.calculate_individual_effect = function(effect, scored_card, key, amount, from_edition)
-- ...
if (key == 'mult' or key == 'h_mult' or key == 'mult_mod') and amount then
if effect.card and effect.card ~= scored_card then juice_card(effect.card) end
mult = mod_mult(mult + amount)
update_hand_text({delay = 0}, {chips = hand_chips, mult = mult})
if not effect.remove_default_message then
if from_edition then
card_eval_status_text(scored_card, 'jokers', nil, percent, nil, {message = localize{type = 'variable', key = amount > 0 and 'a_mult' or 'a_mult_minus', vars = {amount}}, mult_mod = amount, colour = G.C.DARK_EDITION, edition = true})
else
if key ~= 'mult_mod' then
if effect.mult_message then
card_eval_status_text(effect.message_card or effect.juice_card or scored_card or effect.card or effect.focus, 'extra', nil, percent, nil, effect.mult_message)
else
card_eval_status_text(effect.message_card or effect.juice_card or scored_card or effect.card or effect.focus, 'mult', amount, percent)
end
end
end
end
return true
end
-- ...
end
I'm having an issue with multiples of my tag. Just one processes fine, but if there are more than one then they all process before the first finishes, which causes booster packs that come from them to open over the top of each other instead of processing one at a time. What am I missing?
apply = function(self, tag, context)
if context.type == 'shop_start' then
local logger = adc_get_logger()
local lock = tag.ID
G.CONTROLLER.locks[lock] = true
tag:yep('+', G.C.ADC.PLUM, function()
local booster = SMODS.add_card( { set = 'Booster', area = G.play })
booster.T.x = G.play.T.x + G.play.T.w / 2 - G.CARD_W * 1.27 / 2
booster.T.y = G.play.T.y + G.play.T.h / 2 - G.CARD_H * 1.27 / 2
booster.T.w = G.CARD_W * 1.27
booster.T.h = G.CARD_H * 1.27
booster.cost = 0
booster.from_tag = true
G.FUNCS.use_card({ config = { ref_table = booster } })
booster:start_materialize()
G.CONTROLLER.locks[lock] = nil
return true
end)
tag.triggered = true
return true
end
end
just a blind guess but maybe try using an event? shrug
-# never tried making a tag before
maybe its because your type is shop_start
the vanilla ones use new_blind_choice
idk if it makes a difference though
Yeah, you don't get these tags from skipping a blind - they come from a joker. So I need them to process in the shop (or at the end of round, but I have the same issue there)
have u tried checking how tags that create booster packs work
you could probably add a check for it to not trigger when a booster pack is already open
They're won at the end of the round. I'd actually RATHER them be opened then, but someone here suggested moving it to shop_start because of this issue. It made no difference though - happens in both places.
yeah then do this
Most of the code there is copied from VanillaRemade Bufoon Tag
I'll give this a try
ic
Thanks
i think that context specifically re-checks after each booster but im not sure
this is either something about the pools (bottom) or catner (top of jokers)
i cant tell
please help me fix
yeah that makes sense
still no worky, same error
ok wait no i just dont listen
ok i did it how you told me to
and yeah still no worky same error
have you even made the rarities yet
wdym
no
check SMODS.Rarity
-# what do you mean no
So when I try to check G.STATE to make sure we're not in a booster pack, it's always G.STATES.SHOP and never G.STATES.SMODS_BOOSTER_OPENED.
Am I checking this wrong, or is there a stack of states that I need to be checking?
if context.type == 'shop_start' and G.STATE ~= G.STATES.SMODS_BOOSTER_OPENED then```
I have a question, is it possible to apply a shader to the whole screen?
i thought i could do that for individual jokers
im just stupid
yes, someone did that before
this might help idk
I'll look into it, thanks
why is this happening now
there's definitely a few things in the pool
(boxofcats is near the end)
its set = "Tarot", not "tarot"
huh, it let me autofill to "tarot"
shrug
😭 why is this function not called
it's clearly right there, yet print failed
if I'm not wrong, this function handles setting usage count and last card used for Fool to work
yet Fool still works, while my modded Fool doesn't
wtf
its set is Divinatio
the condition should check for Tarot, Planet or Divinatio
I even put print right there
but print did not work
can i see the check
this?
the joker catner only procs for once cat joker
(itself)
no more than itself is having any effect for some reason
just a guess but maybe modded sets should have the mod prefix too?
oh yeah its outside that check, hm
they shouldnt
but G.GAME.last_tarot_planet_divinatio remained nil
does it not prinnt with those
print is outside the check
thats not what i asked tho
N' meant whether the print works when you are using a normal tarot card
it might be another mod messing with it
how does Fool even work
if the function doesn't
yet it works
that's driving me nuts
how can i check the final chips of the current hand so basicly check if theres fire
print is not inside any checks wtf WHY IS IT NOT PRINTING
feels like anything but my patch inside the function works
nvm I found the culprit: Incantation
damn
lol
absurd Ig
Incantation completely overrides the function
for quantity parameter to work
does overflow do the same
lmao
why r there two huy
why not? 😄
true
You're trying to do that in a return
Huy is the second most common first name in Vietnam lmao
omg coding in light mode is insane
well when i dont do it in the return this happens
which black color does descriptions use
i hate most dark modes, the only ones i like are discord and twitter
Remove the comma after the 1
you only need commas in tables
not even inconsistent you're just doing it wrong
only needed in tables lol
oh that makes sense
true...
i guess pretty much everything is tables and i just didnt realise
table here, please don't be tableist
i follow chairism
im sorry but take your table out of here.
this guy is tableist everyone jump him
no clue it autocorrected to that
thats the secret
in order to master ui you must learn to rizz tables
i no longer wonder why i suck at this shit!
ok so what should it look like? lol
i mean he ain't wrong ngl
good table organization is like key to making ui
so you don't make a ui gun and shoot your ui self
mhm
or make a ui truck to run over a ui family of ui four
so basically do your suit check in context.before (and only context.before)
it looks organized enough
that's how i do it too
still unreadable as shit but
it's slightly less unreadable
true
this should be correct now
calculate = function(self, card, context)
local other_joker = nil
local has_diamond = false
if context.before then
for _, playing_card in ipairs(G.play.cards) do
if playing_card:is_suit(card.ability.extra.suit) then
has_diamond = true
break
end
end
end
if has_diamond then
-- blueprint copy effect here
end
end
unless i forgot something again
even leaving and coming back to it later didn't help 😭
shouldnt that say if has_diamond == true then
if has_diamond then and if has_diamond == true then are the same logically
ok nvm then
yeah no this still dont work mb gang
oop
nvm i put it in the wrong file
ok it still dont work
key = 'diamond',
loc_txt = {
name = 'Diamond',
text = {
'Copies ability of {C:attention}Joker{} to the right if played hand contains a {C:diamonds}#1#{} suit'
}
},
atlas = 'diamond',
pos = { x = 0, y = 0 },
rarity = 3,
cost = 9,
unlocked = true,
discovered = true,
blueprint_compat = false,
config = { extra = { suit = 'Diamond' }, },
loc_vars = function(self, info_queue, card)
if card.area and card.area == G.jokers then
local other_joker
for i = 1, #G.jokers.cards do
if G.jokers.cards[i] == card then other_joker = G.jokers.cards[i + 1] end
end
local compatible = other_joker and other_joker ~= card and other_joker.config.center.blueprint_compat
main_end = {
{
n = G.UIT.C,
config = { align = "bm", minh = 0.4 },
nodes = {
{
n = G.UIT.C,
config = { ref_table = card, align = "m", colour = compatible and mix_colours(G.C.GREEN, G.C.JOKER_GREY, 0.8) or mix_colours(G.C.RED, G.C.JOKER_GREY, 0.8), r = 0.05, padding = 0.06 },
nodes = {
{ n = G.UIT.T, config = { text = ' ' .. localize('k_' .. (compatible and 'compatible' or 'incompatible')) .. ' ', colour = G.C.UI.TEXT_LIGHT, scale = 0.32 * 0.8 } },
}
}
}
}
}
return { main_end = main_end }
end
return { vars = { localize(card.ability.extra.suit) } }
end,
calculate = function(self, card, context)
local other_joker = nil
local has_diamond = false
if context.before then
for _, playing_card in ipairs(G.play.cards) do
if playing_card:is_suit(card.ability.extra.suit) then
has_diamond = true
break
end
end
end
if has_diamond then
local other_joker = nil
for i = 1, #G.jokers.cards do
if G.jokers.cards[i] == card then other_joker = G.jokers.cards[i + 1] end
end
return SMODS.blueprint_effect(card, other_joker, context)
end
end
}```
is the blueprint effect not working?
after the first for loop put a sendDebugMessage(has_diamond)
ok hold up
see if has_diamond is true
meanwhile, print():
i didnt even get a debug mesage vro 😭
ok then just print it then
do you have debugplus?
it should make seeing the prints a lot easier
false
this seems like it will only copy the effect of jokers during context.before specifically
oh
uhhh
yeah i think it keeps on getting reset
hold on
i think it might be better to put the has_diamond inside config.extra
so that way it doesnt continuously get reset to false
what joker r u using to test the blueprint effect
hm
put a print inside if has_diamond then to see if it ever runs
OH WAIT
your card.ability.extra.suit is Diamond
?
.
well that would explain why has_diamond was false
mhm, but limiting it to context.before wasnt a very good idea anyways
oh my god i just realized why it would limit the copy effects to jokers that use context.before
fml
is there a way to check if the player is in a round?
or do i need to keep track of that manually
There’s definitely a G.STATE for it
ohai astra :D
G.GAME.blind i think?
will give that a go
nope
does anyone know why in the update function, i always get 0 when printing dt?
if i had to guess its probably because the number is very smalll
I’d imagine it gets rounded up yeah
if it gets rounded up it would always be 1
Down*
ok i tried this and it still kept on printing 0 even after a few seconds
is dt really that small?
im running balatro on a smooth 288 fps so 1/288 on average shouldnt be too small for it to print out
after a few seconds the total dt should be equal to those few seconds
do you have any other mods that could be messing with it
no, just debug plus and steamodded
try G.real_dt as well
it worked
Delta time crisis
idk what's up then
you want to use real dt anyway if youre animating something
ok good to know then
Good schmorning chat
hi silly
what??? i made this card so long ago, why is it coming with this now?
it was good i think im halfway through
because you set up the lsp stuff
but what does it even mean with need check nil
Hell yea
I should set up lsp stuff i still haven't
How do you even do that again
how can i display a message like the one The Psychic displays but at any time with a custom message?
im guessing youre using pseudorandom_element, that can return an element or nil if none, so you need to check if the result is nil or the function might crash
maybe you dont actually need to because your logic makes it so it's never nil but vscode doesn't know that
im using pseudorandom element yeah
but the consumable's can_use should make that always have something
i think it should learn balatro then smh
True
oh i missed this
https://github.com/nh6574/VanillaRemade/wiki#3-set-up-the-lua-lsp
Me too lol
hi winter!!
u are lacking love
why dont you fix that
SMODS.Joker {
key = 'RBand',
loc_txt = {
name = 'Runald\'s Band',
text = {
"{C:chips}+#4#{} chips",
}
},
rarity = 1,
atlas = 'RiskOfJokes',
pos = { x = 2, y = 1 },
cost = 5,
eternal_compat = false,
config = { extra = { chips = 0, chip_mod = 4 } },
loc_vars = function(self, info_queue, card)
return { vars = { card.ability.extra.chips, card.ability.extra.chip_mod } }
end,
calculate = function(self, card, context)
if context.final_scoring_step and (hand_chips * mult > G.GAME.blind.chips) and not context.blueprint then
card.ability.extra.chips = card.ability.extra.chips + card.ability.extra.chip_mod
return {
message = localize('k_upgrade_ex'),
colour = G.C.CHIPS
}
end
if context.joker_main then
return {
chips = card.ability.extra.chips
}
end
end,
}
why does it appear as nil?
get rid of the hash tags, works for me normally
"{C:chips}+4{} chips"
is meant to represent the value in loc vars, so #1# is the first value in loc vars and etc
oop
'#'
i forgot discord formatting
im not yellinhg
im sorry
excatly i want it to show chips, since it scales
which value in your loc_vars is your chips, the first one or the second one
yes, but '4' doesnt exist you have up to 2 in loc vars
you have extra chips and chip mod
is the has_no_rank check necessary here?
if context.individual and context.cardarea == G.play and not SMODS.has_no_rank(context.other_card) and context.other_card:get_id() == 8 then
nop
Cards with no rank have a random id
wait does that mean they could coincidentally have an ID of 8? ^u^;
Is there any mod that adds a new collection section to get references?
cardsleeves?
paperback adds paperclips to the "Other" section
hiya! i have a Joker that needs to trigger between discarding cards and drawing new ones
is that context.discard?
yes
it's currently if not context.blueprint and context.hand_drawn and card.ability.extra.active then but that's not right
okay ^u^ thanks!
no problem!
wait that doesn't seem right
why
that applies for all discarded cards — i want it to happen after they're discarded
it'll happen 5 times if i discard 5 cards, eg.
ah pre_discarded
not that either
oh
that's before the cards are discarded
i need a context that's between discarding all the cards and drawing the new ones
I don't think there's a post_discard unfortunately
is there no draw function?
i needed something similar and i kinda squished hand_drawn and discard together
yeah i was going to say if there's a draw check you could just use that along with the discard
hand_drawn is after the cards are drawn
i was using this previously but it's not what i need
drawing_hand is before the hand is drawn
sozzers
<@&1133519078540185692>
ok
use a flag to make sure it only does it once
ooh, will try that
it's not on the wiki ^^;
try this!
smods documentation strikes again (iirc context.initial_scoring_step isn't on the wiki either, which I had to use recently)
aha :D!
thank you please ping reply twin
fuck
is there any improved documentation or is it desolate and dry desert outside of few sources and the main documentation currently
why does the first true just end instantly
I don't think there's anything better, I was neck deep in the lovely dump when I found what I was looking for
shucks!
it's setting itself active, i needed that when it used draw_to_hand
as that happens with plays and when the blind is selected, too
im struggling with getting alternate calculations to happen.
i want my jokers to detect when a specific joker is next to it, and get empowered by it.
not sure if i'm reading up on documentation correctly but from my understanding this would check individual cards that are currently in play right?
or individual would call for specific effects then other card would the check itself
ngl
I should release that brainrot mod
I did it as a joke but It's 🔥
Do you agree edward robinson
who is bro

if code is required, i will supply what i have
here's the code
It's G.jokers.cards[i-1].config.center.key, also is_next_to_wateringcan should be initialized to false after the context check
if im understanding you right, then this should work for checking toward the left?
crashed with the new code
Check if G.jokers.cards[i-1] exists first.
But also the .key is j_modprefix_key format.
why doesn't my code work properly with telescope?
SMODS.Booster:take_ownership_by_kind('Celestial', {
create_card = function(self, card, i)
local _card, spect, scope, _planet
if (G.GAME.used_vouchers.v_telescope) and i == 1 then
local _hand = PSI.mostplayedhand(false)[1]
for _, v in pairs(G.P_CENTER_POOLS.Planet) do
if v.config.hand_type == _hand then
_planet = v.key
end
end
_card = {
set = "Planet",
area = G.pack_cards,
skip_materialize = true,
soulable = true,
key = _planet,
key_append = "pl1"
}
return _card
end
for _, v in ipairs(SMODS.find_card("j_para_garagefork", false)) do
if SMODS.pseudorandom_probability(card, 'para_garagefork', v.ability.extra.numerator,
v.ability.extra.denominator, 'para_garagefork') then
spect = true
break
end
end
if spect then
_card = {
set = "Spectral",
area = G.pack_cards,
skip_materialize = true,
soulable = true,
key_append =
"ar2"
}
elseif not scope then
_card = {
set = "Planet",
area = G.pack_cards,
skip_materialize = true,
soulable = true,
key_append =
"ar1"
}
end
return _card
end,
},
true
)
the most played hand function btw:
PSI.mostplayedhand = function(format)
local _hand, _tally = "Straight Flush", to_big(0) -- ty vanillaremade for giving me all this code
for _, handname in ipairs(G.handlist) do
if SMODS.is_poker_hand_visible(handname) and to_big(G.GAME.hands[handname].played) > to_big(_tally) then
_hand = handname
_tally = to_big(G.GAME.hands[handname].played)
end
end
if format then
return {
_hand,
number_format(to_big(G.GAME.hands[_hand].chips)),
number_format(to_big(G.GAME.hands[_hand].mult)),
number_format(to_big(G.GAME.hands[_hand].l_chips)),
number_format(to_big(G.GAME.hands[_hand].l_mult))
}
else
return {
_hand,
to_big(G.GAME.hands[_hand].chips),
to_big(G.GAME.hands[_hand].mult),
to_big(G.GAME.hands[_hand].l_chips),
to_big(G.GAME.hands[_hand].l_mult)
}
end
end
what's happening here? I've already defined and set xmult_gain but it's saying it's a nil value
you have to show more of your code
i'd assume you're probably using xmult_gain instead of card.ability.extra.xmult_gain though
nope as you can see it's card.ability.extra.xmult_gain
it also gives nil in the joker description even though I have it in the loc_vars field
which line specifically is line 43?
btw card.ability.extra.Xmult_mod doesn't exist in that return
and also that'd just be a number instead of something like "x6.25 mult"
rq actually. are you sure you saved the file
it's the one that adds xmult_gain to xmult
oh shit yeah i forgot to change that
and yeah i did save the file
Did you start a new run
After changing the config
yeah try that
the specific joker you have probably has xmult_gain as nil in its config
instead of the correct 0.5
i was using the same savestate so that might be the case
ill try this
why are you savestating in balatro
bump. issue is it doesn't always spawn the right planet (seen most commonly if the chance is set to 6 in 6 with oops)
so i don't have to get the conditions for the joker to trigger every time i wanna test it
whenever you want to test something new you should start a new run
also do you not have debugplus or something
isn't savestate a feature of debugplus?
anyway it worked the answer was in fact to start a new run
no? i have a slightly outdated version so maybe that's new
it is, you press Z + a number key to save and X + that number to load
well i can tell somethings supposed to happen because my game crashes
for some reason I get a stack overflow error for using the second tarot, could someone help cus I have no clue 🙏🙏 (yes it was too long to fit in the message)
hello again modding-dev chat
im trying to get my joker to display a message during gameplay after certain durations. I have got the counting fine and its all working but im not sure how to get the messages to display.
update = function(self, card, dt)
if not G.SETTINGS.paused and G.GAME.blind and G.GAME.blind.in_blind then
card.ability.extra.time_spent = card.ability.extra.time_spent + dt
card.ability.extra.in_seconds_kinda = card.ability.extra.time_spent / 200 * 58
if card.ability.extra.in_seconds_kinda <= card.ability.extra.S_rank then
return {
message = 'S Rank!'
}
end
if card.ability.extra.in_seconds_kinda == card.ability.extra.A_rank then
return {
message = 'A Rank!'
}
end
if card.ability.extra.in_seconds_kinda == card.ability.extra.B_rank then
return {
message = 'B Rank!'
}
end
...and so on.
im guessing messages dont work in the update section since these dont do anything
i also tried
context.cardarea
yeah update doesnt have a return or context
do SMODS.calculate_effect({message = "message"}, card)
so instead of doing return i do that
yes
is this when you have the last joker?
yes
if so it's because you're missing a strict context check
what happens is that youre checking the globals (which you shouldnt save directly to a global variable, use G.GAME.modprefix_variablename instead) in every context, so when both globals are true it adds the card in the next context called and every context after that
what's worse is that there is a card_added context so every time you are adding a card its creating another one because you don't reset the globals
ohhh ok thanks
N is so smart
How can i check if the played hand contains another hand with blinds?
Not sure what you mean "with blinds" but usually it's next(context.poker_hands["Handname"], check trousers code
Like with a blinds code, they don't have context
Dont they?
They should support calculate iirc
I see mentions of blind calc on the SMODS discord
I thought they didnt as well cause it wasnt documented
By they definitely do
...had they not, I'd probably pull my hair out.
how do I make the message appear before the card is destroyed? simply putting start_dissolve() after the return gives this error
ok so i have
return {
Xmult = card.ability.extra.money + (math.floor(G.GAME.dollars / 5)) * 0.1
}
and i have no idea why the talisman "attempt to compare number with table" happens here
removing the math.floor fixes this tho
You got a to_big func? Not sure where exactly it would be placed, maybe for the division
talisman has a bug with returning bignums i think
oh but if removing math.floor fixes it might be something else
wrapping it all in lenient_bignum fixed it
i love talisman
Is it known if there's a way to, with the recent smods probability stuff, make a probability whose chances can't be modified by Oops and the like? I want to make absolute chances for balance reasons, would just plugging 1 into the numerator field of pseudorandom_probability work?
I think a hardcoded value would work best
you can't do it with pseudorandom_probability unless you hook stuff to prevent the context
you can just do pseudorandom("seed") < 1 / odds
Oh I can still do the old way, good to know
first of all, youre missing a context check on destroying cards
second, doing context.cardarea == G.jokers with context.joker_main is entirely pointless
as for the error, youre just missing an end, so you probably did the syntax wrong
also if youre destroying playing cards, use SMODS.destroy_card
if youre destroying playing cards during scoring use context.destroy_card
the system for destroying cards works fine so i don't really wanna mess with it. unless SMODS.destroy_card lets you put the message to display before card is destroyed?
youre still missing an end somewhere which is causing the crash
no it's destroying after calculations are done, specifically when fire effect is triggered
the code looks like it would destroy during scoring too
somehow it doesn't, no idea how it works it just does
even if not i would add a stricter context check
when does this crash even happen?
right when opening the game
if you meant like this, it did not work
thats because it cant load the file
not anything to do with your joker
do assert() around your file loading line to get a more detailed error
are you sure it's entering any of the conditions
yep
i have returned to bring up my ijiraq issue again, in which it does not mimic yorick properly
this (safe_set_ability function) was made by somecom so id be better off asking him, but it doesnt hurt to ask for help because you never know
do note that the yorick itself works fine, but once it's ijiraq with his effect it does not track, even after discarding 5 hands
printing the table shows that the initial values are indeed stored, but any changes ijiraq would make to them aren't, it isnt tracking the states
yo, rick
do other messages from cards work
yes
dunno then, it should work
what's the syntax for SMODS.destroy_card?
the only argument is either a single card or an array of cards
oh i forgot it has other arguments now
i added that lmao
it's cards, bypass_eternal, immediate
@daring fern (realizing i should link this to him for when he actually is available)
I just suddenly have a seal idea:
"Creates a Spectral card when discarded, but has 1 in 4 chance to be destroyed. (Must have room)"
Has this one been done before?
are bypass_eternal and immediate optional or do I have to fill them in too?
also i can tell what bypass_eternal is but what does immediate do?
Optionals, immediate skips any anim and sound
Whether to do it right away (True) or put it in event queue (False, default value).
it's giving this error, did I do something wrong?
how do i make a joker move to a random position (shuffle itself basically) in the joker slots
ok i got it to work but once it hits the first condition it just spams the message and softlocks :/
if i want to take ownership of a joker to add some functionality but I still want the original functionality to work as it did, do I need to recreate the original functionality in the calculate function? or is there a way to patch new functionality without overriding the old.
the function is SMODS.destroy_cards(), plural
Have you looked into Amber Acorn's code?
shit yeah it works now thx
My guess is to hook the calc function in P_CENTERS and then takeownership.
doesnt that like shuffle all jokers
But it does move jokers around, isn't it?
that poses more questions than it answers, but thats just because I dont know what a P_CENTERS is. ill take a gander, thank you!
why is it saying it expects end to close the if at line 42 when it clearly is there
You could also hook calculate joker and do it the vanilla way w a name check if its just to add and not modify/remove
Wait, sorry, I was wrong.
It should be Card.calculate_joker
Um, I think you shouldn't put anything directly after return.
oh
is there any way to display the message before the card is destroyed?
SMODS.calculate_effect
this crash consistently happens if i start a blind with burglar as my effect
apparently mod is being taken as a table in ease_hands_played?
god i wish i knew how this ability table worked
syntax?
Maybe just turn that shit into a number then idk
i dont
know how
to do that
putting the effect of burglar in him manually just crashes the game anyway
^is there a library that can be installed to give some descriptions of functions?
i mean it saves the values?
is it possible to make a value that can be returned in a calculate table like mult or chips
Yes.
Probably this? (I'm using VS Code)
that is barely what he asked but yeah
i would say "maybe if i add Burglar to this list" but Yorick isnt even working so
get the cat version with 3 downloads
SMODS contains an LSP, not vanilla tho
or MAYBE BECAUSE BURGLAR USES EXTRA AND EXTRA IS A TABLE
can i overwrite that?? i def need extra to be a table
Doesn't vanilla remade have in its wiki a place for vanilla and further lsp
Or am I misremembering
Maybe, I dont actively check on VR updates
And then this
shit that's actually really bad if it's because extra is a table
The downside of this is that the extension's diagnostic system has a lot of opinions against localthunk's codes.
Extra can be both a value and table
your question made me curious, turns out it's as simple as swapping the positions in G.jokers.cards
so then it's not the reason it's crashing?
Im on phone I dont see the crash (too lazy to dl)
i got you
why is this only registering one cat joker if i have it and another cat pool joker
key = 'catner',
loc_txt = {
name = 'catner',
text = {
"Gives {C:money}+$1{}, {C:chips}+20 Chips{}, {C:mult}+15 Mult{}, and {C:mult}+x0.2 Mult{} for each cat joker.",
"{C:inactive}Currently #1# cats.",
"{C:inactive}'i like this cat. its a good cat.'"
}
},
cost = 5,
rarity = 2,
pools = {
["cat_shop"] = true,
["cat"] = true
},
atlas = 'spr',
--pos = { x = , y = ,}
config = { extra = { money = 0, chips = 0, mult = 0, xmult = 1} },
calculate = function(self, card, context)
catcount = 0
for i = 1, #G.jokers.cards do
if G.jokers.cards[i].config.center.pools and G.jokers.cards[i].config.center.pools.cat then
catcount = catcount + 1
end
end
print = catcount
card.ability.extra.money = catcount
card.ability.extra.chips = catcount * 20
card.ability.extra.mult = catcount * 15
card.ability.extra.xmult = (catcount / 5) + 1
if context.joker_main then
return {
money = card.ability.extra.money,
chips = card.ability.extra.chips,
mult = card.ability.extra.mult,
xmult = card.ability.extra.xmult
}
end
end
}```
got that working. thank you, its going to help a lot. one more question before i stop bugging the chat for at least 20 minutes: how did you know about Card.calculate_joker? it isnt in the documentation afaik
why is catcount not local-ed?
Its in the vanilla code
i made it local and nothing changed
Is line 160 if mod < 0 then?
yes
if mod < 0 then
and then it crashes trying to compare a number with a table
so for some reason mod is a table
full crash
andddd how
It seems that you're not set_abilitying correctly.
if you wanted to swap position 1 and 2 you can do it this way
local swap = G.jokers.cards[1]
G.jokers.cards[1] = G.jokers.cards[2]
G.jokers.cards[2] = swap
but i havent changed anything outside of what you said? should i have? is it the patch?
tnks
Why are you counting cats under every single possible context instead of only under joker_main?
look i copied the cat counting code from yahimod
idk how to change that
What does the patch look like?
ok i just changed where the if context joker main is
it still only counts one cat card tho
i lied another question. If i want to use calculate_joker, I need to pass in context. i am going to wager that this context is the joker that i want to calculate. Is there a "get_joker_by_name" style function that I can use to get the required context?
im dumb, scratch that. If i am replicating the existing jokers function, i have the context
My suggestion:SMODS.Joker { key = 'catner', loc_txt = { name = 'catner', text = { "Gives {C:money}+$1{}, {C:chips}+20 Chips{},", "{C:mult}+15 Mult{}, and {C:mult}+x0.2 Mult{}", "for each cat joker.", "{C:inactive}Currently #1# cats.", "{C:inactive}'i like this cat. its a good cat.'" } }, cost = 5, rarity = 2, pools = { ["cat_shop"] = true, ["cat"] = true }, atlas = 'spr', --pos = { x = , y = ,} config = { extra = {} }, calculate = function(self, card, context) if context.joker_main then local catcount = 0 for _,J in ipairs(G.jokers.cards) do if J.config.center.pools and J.config.center.pools.cat then catcount = catcount + 1 end end return { money = catcount, chips = catcount * 20, mult = catcount * 15, xmult = (catcount / 5) + 1 } end end }
can you print card.ability.extra right before you calculate_joker and after you set_ability
it still only counts 1 cat joker
here?
odd
124 lines of 3
nothing happened
where did you ran that code
why'd you need to send all the lines
Have you checked if the other joker has pools.cat or not?
i thought it would send as a file
it was too late
the joker im testing with it is this one:
SMODS.Joker {
key = 'Kat',
loc_txt = {
name = 'cat.',
text = {
"I am cat. I am cat. I am cat card. I am only cat.",
"{C:inactive}'help im stuck getting tons of cat. jokers from my box of cats'"
},
atlas = 'spr',
--pos = { x = , y = ,}
cost = 5,
rarity = 1,
pools = {
["cat"] = true
}
}
}
can you send the full crash log
would it be easier if i push it, making you be able to look at it hands on?
in a joker's context.before and context.cardarea == G.jokers inside of an G.E_MANAGER:add_event
nvm it worked
it just decided to swap itself with itself the first time
Honestly, I'm not exactly familiar with pools.
My next guess would be modprefix...?
As in, J.config.center.pools.[modprefix]_cat
odd indeed, mod should've been 3 by what your print statement said
where would i even add it (if u feel like adding it yourself the prefix is tmod)
why does lua hate me
could i perchance get some help debugging?
do you hate lua
no i hate myself
apparently with lua it's a 1 sided love
You are in the right place. Just ask us then.
do i just paste the code or is there a certain way to send it?
i think half of the people here need help debugging
and the other half is helping debugging
also just send the file or if its isolated to just one part send the joker or wtvr
You can also send a screenshot
It depends on you
but your question made me double check and i realised that all this time i had cardarea == G.play
(to send it in that code form do three ` in a row)
key = "creep_josh",
name = "Joshua the Creepy",
unlocked = true,
discovered = true,
rarity = 2,
cost = 6,
atlas = "Joshs Jokes",
pos = {x = 4, y = 0},
config = { mult = 0 },
loc_txt = {
text = {
'Gain +1{X:mult,C:white} Mult{} per discard left',
'at round end. Lose {C:money}$1{} per 2 discards.'
}
},
calculate = function(self, context)
if context.end_of_round and not context.blueprint then
local discards = G.GAME.round_resets.discards_left or 0
self.ability.mult = (self.ability.mult or 0) + discards
ease_dollars(-math.floor(discards/2))
return {
message = localize{type='variable',key='a_mult',vars={discards}},
mult_mod = discards
}
end
if context.joker_main then
return { message = "Rain!", Xmult_mod = self.ability.mult or 0 }
end
end
}
also describe the issue or send the crash log if there is one
im trying to get it to gain 1 mult per remaining discard at end of round and deduct $1 per two remaining
it isnt crashing but it just does nothing
self.......
pretty sure its self, card, context not just self, context
It should be calculate = function(self, card, context),
and then you'll also have to replace self with card inside the function.
how do i print something
print()
Uhh, print?
ok i just didnt know the syntax for it
Yo guys how can make a Card that gain chips reset after a specific action in jokerforge?
Just throw a string-ish stuff into it
jokes on you, im throwing a variable into it
still works
ask the joker forge thread
ima try this rq sorry for slow response im working on this in down time at work
not like this man......
does it also crash when copying trading card
i can check
so it is setting the hands mult to 0 when scoring and at end of round it activates with +0 mult and does so on every card in hand while granting $0 for every card in hand
no (you cant tell from this image, but it's doing the trading card wobble)
cards were destroyed properly
it's just burglar
did you add some code to do something specifically when it's burglar?
oh and
tried it without additions, crashed
tried it but i added burglar's code to it manually within the joker, crashed
clearly it's unaffected
ok so
why is this joker (catner) not counting the amount of jokers you have in pool cat
the joker im testing it with is the second joker, kat/cat.
SMODS.Joker {
key = 'catner',
loc_txt = {
name = 'catner',
text = {
"Gives {C:money}+$1{}, {C:chips}+20 Chips{},",
"{C:mult}+15 Mult{}, and {C:mult}+x0.2 Mult{}",
"for each cat joker.",
"{C:inactive}Currently #1# cats.",
"{C:inactive, s:0.8}' 'catner catner catner; catns the amount of catners that catn catners in this encatner' when'"
}
},
cost = 5,
rarity = 2,
pools = {
["cat_shop"] = true,
["cat"] = true
},
atlas = 'spr',
--pos = { x = , y = ,}
config = { extra = {} },
calculate = function(self, card, context)
if context.joker_main then
local catcount = 0
for _,J in ipairs(G.jokers.cards) do
if J.config.center.pools and J.config.center.pools.cat then
catcount = catcount + 1
end
end
print("catcount")
return {
money = catcount,
chips = catcount * 20,
mult = catcount * 15,
xmult = (catcount / 5) + 1
}
end
end
}
SMODS.Joker {
key = 'Kat',
loc_txt = {
name = 'cat.',
text = {
"I am cat. I am cat. I am cat card. I am only cat.",
"{C:inactive}'help im stuck getting tons of cat. jokers from my box of cats'"
},
atlas = 'spr',
--pos = { x = , y = ,}
cost = 5,
rarity = 1,
pools = {
["cat"] = true
}
}
}
the print in catner is reached, but if i set it to the variable catcount instead of the word, it prints nothing for some reason
tell me if you need more info for anything
If you didn't define an ObjectType with key = 'cat' then adding the pool to your joker won't do anything
i have it
"{C:inactive, s:0.8}' 'catner catner catner; catns the amount of catners that catn catners in this encatner' when'"
wrong copy paste
god damn it
hey guys, so I was looking into the card sc and found this random parameter in the set_ability function called center
can someone tell me what it is please?
you can use it to find some card properties
card.config.center.key is the key, and i think you can do card.config.center.rarity too?
and probably some other stuff
No, it's card.config.center
ok now its working
I still don't get it, is it a table that contains the stats of the card?
center contains important stuff
like the key
and some other things
stats go in card.ability, though they more often go in card.ability.extra
what is "the key" you mention?
how do i change the amount of money the player has when a joker triggers
"money = " isnt working
it's dollars =
item key, like whats used to refer to the card internally
or any item for that matter
ty
sry I'm new to modding and trying to figure it out slowly
vanilla centers are like this yeah
and the effects are hardcoded into the Card functions
ohh now I get it
https://github.com/nh6574/VanillaRemade look at this for reference btw its really useful
SMODS centers contain both the stats and behavior of jokers/consumables and whatever else
stats arent in card.config.center though?
they go in card.config or card.config.extra i thought
The stuff in the center's config is copied into the card's ability table
It holds the initial stats would be better wording
at least for jokers
should i make the sprite size for my consumables the same as my jokers
yes
yes, consumables just get scaled down in-game
btw I want to ask about how joker abilities are handled? does every joker have its own file?
Can someone please help me with a thing?
Basically, I need to take the message function out of a Joker from Mayhem which says "Upgraded!" every time any valid scoring context happens. I need to put this function into my Joker that I generated from Joker Forge. How would I do this?
Here is the code for the Mayhem Joker:
key = 'taimuresu',
loc_txt = {
name = 'Taimuresu',
text = {
"{C:attention}Gains{} {X:mult,C:white}X#1#{} Mult when",
"{C:attention}any scoring context{} happens",
"{C:inactive}Currently{} {X:mult,C:white}X#2#{} {C:inactive}Mult{}",
"{C:inactive,s:0.5,E:1}This is gonna take a while...{}"
}
},
rarity = 'cry_exotic',
atlas = 'cry_joker1',
pos = { x = 6, y = 0 },
soul_pos = { x = 8, y = 0, extra = { x = 7, y = 0 } },
blueprint_compat = true,
config = { extra = { Xmult = 1, Xmult_gain = 0.0004 } },
cost = 50,
loc_vars = function(self, info_queue, card)
return {vars = { card.ability.extra.Xmult_gain, card.ability.extra.Xmult } }
end,
calculate = function(self, card, context)
if context and not context.joker_main and not context.blueprint then
local myself = card
card.ability.extra.Xmult = card.ability.extra.Xmult + card.ability.extra.Xmult_gain
return {
message = 'Upgraded!',
colour = G.C.MULT,
card = myself
}
end
if context.joker_main and card.ability.extra.Xmult > 1 then
return {
Xmult_mod = card.ability.extra.Xmult,
message = 'X'..card.ability.extra.Xmult..' Mult',
colour = G.C.MULT,
card = card
}
end
end
}```
I also need to get rid of the mult function.
Here's the code for my Joker:
name = "Boowomp",
key = "boowomp",
config = {
extra = {
}
},
loc_txt = {
['name'] = 'Boowomp',
['text'] = {
Not putting the text here because there are 270 lines
}
},
pos = {
x = 0,
y = 0
},
cost = 1,
rarity = "boowomp_boowomp",
blueprint_compat = true,
eternal_compat = true,
unlocked = true,
discovered = false,
atlas = 'CustomJokers',
set_ability = function(self, card, initial)
card:set_eternal(true)
end
}```
for making mods you can do whatever you want
as long as you load the file in some way
in vanilla?
I would like to know how it is in vanilla yes
in Mods/lovely/dump/card.lua under the Card:calculate_joker function
yes, all in one file, in one function
I'd suggest using multiple files, ultimately it's at your discretion
Putting everything into one file is not a good idea in my opinion
a lot of mods use one per item, some big ones like cryptid just make a few big files with a bunch of stuff, anything works basically
sending these as images, maybe something missing. maybe it's the same reason yorick doesn't work either
idk im losing it
those images are way too big actually
any tips on destroying specific cards on hand play? i'm struggling a tad with the documentation with it
fix
what code would i use to put lucky cat in a pool
in your objecttype add cards = { j_lucky_cat = true }
you could probably run destroy_card in G.play
what i have in the image is for Queens in hand
oh OH that's what i was doing wrong i was messing up the get id portion
1 second i'll report back
replicated 1:1 for testing doesn't seem to be going through
ah wait this is a me ended thing perhaps
Lowercase P
thats also for specifically queens
oh I see
No prob!
key = 'eepy',
blueprint_compat = true,
loc_txt = {
name = 'eepy cat',
text = {
"Sleeps for 3 antes, then gives {C:chips}+300 Chips{}.",
"#1#/3 Antes",
"{C:inactive}'can this little goober WAKE UP ALREADY??????'"
}
},
rarity = 2,
config = { extra = { antes = 0 } },
atlas = 'spr',
pos = { x = 0, y = 0 },
loc_vars = function(self, info_queue, card)
return { vars = { card.ability.extra.chips, card.ability.extra.antes } }
end,
calculate = function(self, card, context)
-- next line is 248
if context.joker.main and card.ability.extra.antes == 3 then
chips = 300
end
if context.end_of_round and context.beat_boss and context.main_eval and not context.blueprint then
if not card.ability.extra.antes == 3 then
card.ability.extra.antes = card.ability.extra.antes + 1
end
end
end
}```
ok im just confused on this one (error occurs when selecting joker)
it's context.joker_main, not context.joker.main
yeah im stupid
how dangerous is it to change a card's sort id
ranges from not too <-> incredibly
what even is a sort id..
bump
(crashes on setting_blind)
pretty much what it sounds like
it doesnt sound like anything to me
it's a reference id for sorting/assignment
when would that even be relevant
like the only place i can think of with sorting is the hand sort buttons
It's a unique id for cards.
the...the cards?
its a vanilla thing
thats also not my point
i just want to know what the point of it is
distinction between cards
i know this
The pinned sticker.
this too
doesnt pinned sticker just put things on the left
or does it use the sort id to determine the order of the pinned cards
Yes, but it sorts multiple cards with the sticker by sort_id
interesting development decision
a necessary one imo
wouldnt it make more sense to just be able to drag around pinned cards among each other
how would it drag them?
No, because their pinned.
if you have multiple pinned cards it doesnt make sense to have them in a fixed order
tho
it just says its forced to the left
not that you cant move them
if you can move them, they can be moved from the leftmost
yeah?
i mean like move only among other pinned cards
instead of having a fixed order of pinned cards
let's say there's 2 pinned jokers
the joker on the very left is in the leftmost position
the joker next to it is ALSO in the leftmost position because that's the area it's given
the first pinned joker is going off of 5 cards, while the second is going off of 4
How does one make it so after the cards are unhighlighted in G.play, scoring triggers again?
oh so theyre both in position 1 but the sort order seperates them?
there you go
so the sort id is just like an object key but a number?
leftmost is relative to the area
so you can sort things
that
that is in fact what i said at the beginning 😭
yeah but i didnt know the point of that??
fair
but you get it now?
i do get it now
nice
i dont see why you would want to change it though
you can trigger scoring again??
or do you literally just mean retriggers
No, I mean like if you played the same hand again.
ohhhhh
i know what youre talking about but i forget the card
copper cards
No, that's for a singular card.
right yeah
copper cards can do the entire hand
its adjacent ones
if your entire hand is copper it rescores all of them
and you literally NEED at least 2
Yes, I'm referring to if you played the hand again, including jokers.
it does that though?
so like card sharp?
it even does stuff like hanging chad, retriggers, position based stuff again
or do you mean specifically the cards IN the hand
Yes, I'm referring to scoring triggering twice so it would be like pressing the play button again with the same hand.
oh so repeating the ENTIRE scoring sequence?
ohhh
Yes.
yeah you probably need to do fancy stuff for that
yeah
red seal play hand
-# Just like I do, basically--
It's because sort ID is saved for certain actions, but seemingly only for booster packs
So hopefully manually resetting the sorting ids won't hurt me, since I only ever need to do it for jokers
Anyway our server had a really cool challenge idea
non...leftmost?
story???
but theyre... all pinned....
balala lore?
how can i make a joker change its own sell value?
egg

me 3 years ago
just watch the video toma
sell cost is under card.ability.extra_value iirc
increase it with a loc var and then call card:set_cost()
i did i was messing with you
That said the sort_id thing was about pinned jokers because unfortunately, if all cards being sorted are pinned, they're sorted by sort_id, which is also the order they're created (very unintuitive, pinned is clearly not built to be used with more than one pinned card). So it means pinned cards from the shop will be added and then sorted always to the left, and then in reverse order from their shop position
So for the purpose of this challenge at least I made it so they're still added to the right as expected, and "leftmost" is relative to any non-pinned jokers to their right
does it work okay? any issues so far?
No it works fine
Really the only annoyance is the one leftover from vanilla. If you have multiple pinned jokers, the "pinned" description makes less sense, as you implied
Only one joker can technically be absolute leftmost at a time
WELL
i mean localthunk did say it was only added for the one challenge
unless you read it as leftmost RELATIVELY
it is, it's leftmost relatively
and a player can pretty easily figure out what's going on with the challenge despite that
Regardless the point of the challenge is that a card can't be sold unless you sell all other cards left to it in sequence
meaning changing your jokers is a lot riskier
It does however make certain effects more predictable (anything that randomly destroys will always target the leftmost joker)
