#💻・modding-dev
1 messages · Page 628 of 1
so i do this then?
No, you're not inputting a table into pseudorandom_element
Yes.
...oh
How would you spawn in a joker from the joker pool from only one mod prefix?
Slash like what would the set be or if theres something else additional
You mean you want to spawn a random joker from a specific mod?
yep
using my new consumable crashes the game, not sure what is causing this
if i want to change this to affect Hands instead of Hand sizes, what should i do
G.GAME.round_resets.hands = G.GAME.round_resets.hands (+ or -) card.ability.extra.(whatever variable you have for modifying the hands) could probably work although im not sure
Code?
SMODS.Consumable {
key = 'beth',
set = 'blindconsumabletype',
pos = { x = 1, y = 0 },
atlas = 'blindcon',
display_size = {w = 64, h = 64},
config = { max_highlighted = 1 },
loc_vars = function(self, info_queue, card)
info_queue[#info_queue + 1] = G.P_CENTERS.m_mult
info_queue[#info_queue + 1] = G.P_CENTERS.m_wild
info_queue[#info_queue + 1] = G.P_CENTERS.m_glass
info_queue[#info_queue + 1] = G.P_CENTERS.m_steel
info_queue[#info_queue + 1] = G.P_CENTERS.m_stone
info_queue[#info_queue + 1] = G.P_CENTERS.m_gold
info_queue[#info_queue + 1] = G.P_CENTERS.m_lucky
return { vars = { card.ability.max_highlighted } }
end,
use = function(self, card, area, copier)
G.E_MANAGER:add_event(Event({
trigger = 'after',
delay = 0.4,
func = function()
local enhancement = poll_enhancement('emuismeaningsmile', nil, true, true,
{ 'm_mult', 'm_wild', 'm_glass', 'm_steel', 'm_stone', 'm_gold', 'm_lucky' })
local change_card = G.hand.highlighted[1]
charge_card:set_enhancement(enhancement, true)
card:juice_up(0.3, 0.5)
return true
end
}))
end,
can_use = function(self, card)
if G and G.hand then
if #G.hand.highlighted ~= 0 and #G.hand.highlighted <= 1 then
return true
end
end
end
}
modify G.GAME.round_resets.hands i believe
-# trying to make it give a selected card a random enhancement
double Ds?
thunk isnt that silly
It's SMODS.poll_enhancement
😭
oh
i'm soggy cat
Also it's set_ability not set_enhancement
ah noted
like this? @umbral zodiac
you should actually modify it rather than just setting it , but yeah
how tho
ohhhh
yep this
carp
so
G.GAME.round_resets.hands = G.GAME.round_resets.hands - card.ability.extra.hands
yea
same code and situation, different crash
SMODS.Consumable {
key = 'beth',
set = 'blindconsumabletype',
pos = { x = 1, y = 0 },
atlas = 'blindcon',
display_size = {w = 64, h = 64},
config = { max_highlighted = 1 },
loc_vars = function(self, info_queue, card)
info_queue[#info_queue + 1] = G.P_CENTERS.m_mult
info_queue[#info_queue + 1] = G.P_CENTERS.m_wild
info_queue[#info_queue + 1] = G.P_CENTERS.m_glass
info_queue[#info_queue + 1] = G.P_CENTERS.m_steel
info_queue[#info_queue + 1] = G.P_CENTERS.m_stone
info_queue[#info_queue + 1] = G.P_CENTERS.m_gold
info_queue[#info_queue + 1] = G.P_CENTERS.m_lucky
return { vars = { card.ability.max_highlighted } }
end,
use = function(self, card, area, copier)
G.E_MANAGER:add_event(Event({
trigger = 'after',
delay = 0.4,
func = function()
local enhancement = SMODS.poll_enhancement('emuismeaningsmile', nil, true, true,
{ 'm_mult', 'm_wild', 'm_glass', 'm_steel', 'm_stone', 'm_gold', 'm_lucky' })
local change_card = G.hand.highlighted[1]
change_card:set_ability(enhancement, true)
card:juice_up(0.3, 0.5)
return true
end
}))
end,
can_use = function(self, card)
if G and G.hand then
if #G.hand.highlighted ~= 0 and #G.hand.highlighted <= 1 then
return true
end
end
end
}
how would i recover the player's hand if it was removed from the deck
if you used + in the add_to_deck, then use - and vice versa
i see
SMODS.poll_enhancement takes a table as input.
it's basically reversing the thing you just did
my plan was to set the player's hand into 1
so should i transfer { 'm_mult', 'm_wild', 'm_glass', 'm_steel', 'm_stone', 'm_gold', 'm_lucky' }) somewhere
No, it would be {key = 'emuismeaningsmile', guaranteed = true, options = {'m_mult', 'm_wild', 'm_glass', 'm_steel', 'm_stone', 'm_gold', 'm_lucky'}}
Sorry just curious, would it be possible to create a joker from the pool a specific mod?
G.E_MANAGER:add_event(Event({
func = function()
if card.ability.extra.count == 3 then
card.ability.extra.count = 0
local new_card = SMODS.create_card({
set = "Joker",
no_edition = true,
})
if new_card then
G.jokers:emplace(new_card)
new_card:juice_up(0.4, 0.4)
end
end
return true
end,
}))```
(This is the code I'm using to spawn the joker)
I'm curious about making it to where this can only pull from the pool of one mod, I think it might be something related to the mod's prefix but I'm not sure how to implement it
how would i prevent joker retriggering itself
"and not context.repetition" I'm pretty sure
Putting that on the if context statements you don't want retriggered mean those parts won't be retriggered by any including it's own retriggers
hmm
i want to retrigger all played cards
but it always does this
I think the issue is that you don't have context.individual
It's hitting itself which honestly I'm totally gonna use in my own mod that seems funny
:SOBS:
you can just do SMODS.add_card instead of create and emplace
there isn't a way to grab from a specific mod easily, you can make an ObjectType with all the jokers from that mod or manually go through G.P_CENTER_POOLS.Joker and filter all jokers by mod
and not context.retrigger_joker iirc
well that's any retriggers
retrigger_joker should be the card retriggering
question: can i set hand size to a specific value, eg. 5
game crashed when i skipped on my custom skip tag
the card key youre using doesnt exist, youre probably forgetting the mod prefix from the looks of it
ah
still looking for answers for this
G.hand:change_size(-G.hand.config.card_limit+5)
and then that variable = 0 right
no that's the code
it's not a variable either it's a function call
thanks
question: how do i make created cards a certain suit
update: i have somehow figured this out myself
Is there a way to change the money reward from a boss blind during that said boss lind ?
SMODS.Joker {
key = 'Rubber_ducky',
pools = { ["Scalala"] = true, },
loc_txt = {
name = 'Rubber ducky',
text = {
"Spawns a copy of self at end of round, gaining +5 chips per copy"
}
},
rarity = 1,
cost = 6,
blueprint_compat = true,
eternal_compat = true,
perishable_compat = true,
unlocked = true,
discovered = true,
atlas = 'Jokers',
pos = { x = 1, y = 3 },
config = {
extra = { chipgain = 5, chiptotal = 0 },
},
loc_vars = function(self, info_queue, center)
return { vars = { center.ability.extra.chipgain, center.ability.extra.chiptotal } }
end,
update = function(self, card, dt)
local count = 0
for _, j in ipairs(G.jokers.cards) do
if j.key == self.key then
count = count + 1
end
end
card.ability.extra.chiptotal = card.ability.extra.chipgain * count
end,
calculate = function(self, card, context)
if context.joker_main then
return {
chips = card.ability.extra.chiptotal
}
end
if context.end_of_round and context.main_eval and not card.YA_no then
local drac = SMODS.add_card({ key = self.key })
drac.YA_no = true
end
end
}
iam trying to make a joker that spawns copies of itself and gains +5 chips for every copy of the joker there is (lets say you have 3 of these you get +15 in total) , however i cant get the chips part of this joker to work
An easier way to get the count is #SMODS.find_card(self.key)
how does that work?
find_card returns an array of all the found jokers with that key, so it's just getting the length
self.key here is the same as putting 'j_modprefix_Rubber_ducky'
SMODS.Blind {
name = "boss_strong",
key = "boss_strong",
atlas = "pseudoblinds",
mult = 2,
pos = { y = 1 },
dollars = 5,
loc_txt = {
name = 'The Strenght',
text = {
"X2 Blind Requirement if",
"scored hand exceeds it",
},
},
boss = { min = 2 },
boss_colour = HEX('cc702a'),
calculate = function(self, card, blind, context)
if not blind.disabled then
if context.after and SMODS.last_hand_oneshot then
G.GAME.blind.chips = math.floor(G.GAME.blind.chips * 2)
G.GAME.blind.chip_text = number_format(G.GAME.blind.chips)
blind:wiggle()
end
end
end
}```
i've a crash about context being nil despite context being in the calculate function, not sure what is going on
your parameters are wrong, should just be function(self, blind, context)
is there a joker triggered context
card:calculate_joker(context)
i think
no I mean
a context for when a joker got triggered
like when greedy joker triggers
it sends out context.card_triggered
context.card=(the greedy joker)
if a diamond card is scored 5 times that context would be sent 5 times
Ah i see
is there any scoring context like context.individual, that doesn't count when the card is retriggered? a check like "not context.repetition" would be fine too but I can't find any that work
how do i make it properly add a negative joker at the start?
i think you could just use something like
if not context.repetition and context.cardarea == G.play then
should work
not context.repetition doesn't seem to do anything, idk why
what's the goal of your joker ?
retrigger every 4th card scored, not counting other retriggers
and not context.repetition then
card.ability.extra.cards_left = card.ability.extra.cards_left - 1
end
if context.repetition and context.cardarea == G.play then
if card.ability.extra.cards_left <= 0 then
card.ability.extra.cards_left = card.ability.extra.retrigger_req
return {
message = localize("k_again_ex"),
repetitions = 2,
card = card,
}
end
end```
i think you need to replaced the false with true
well it should work out, i don't think other repetitions will affect your own
nope, still the same error
probably should've posted that first
im so smart
SMODS.add_card({key = 'j_pareidolia', edition = 'e_negative', stickers = {'eternal'}, force_stickers = true})
Also put it in an event.
nope, any retriggers including the joker itself still count it down. I've tried a few things but they either count every retrigger or none of the normal cards
also i just realised that if think having twice context.repetition is why it does nothing properly, since it already check once for the context it might not recheck twice, especially you use the card area context too
you'll be better fusing your context in one go
if context.individual and context.cardarea == G.play then
if not context.repetition and not context.blueprint then
card.ability.extra.cards_left = card.ability.extra.cards_left - 1
elseif context.repetition then
if card.ability.extra.cards_left <= 0 then
card.ability.extra.cards_left = card.ability.extra.retrigger_req
return {
message = localize("k_again_ex"),
repetitions = 2,
card = card,
}
end
end
end```
you might need to adjust it but it should be something like this
I made quantum ranks into balatro
that works
without having to add your own compatibility
it automatically replaces the card code
who wanna see?
sure
wait I'll show u
should I launch it in my lib as W.I.P for other people to use and help catch errors (If there's any)
there were some cases where it wasn't working
lets goo, thanks for the help ❤️
well put it a video here so we can see what it even does
but then I fixed it by counting variables
SMODS.Blind {
name = "bos_clarity",
key = "bos_clarity",
atlas = "pseudoblinds",
mult = 2,
pos = { y = 3 },
dollars = 0,
loc_txt = {
name = 'The Purity',
text = {
"Gives no reward money",
},
},
boss = { min = 1 },
boss_colour = HEX('64d5e6'),
modifiers = function()
G.GAME.modifiers.no_blind_reward = G.GAME.modifiers.no_blind_reward or {}
G.GAME.modifiers.no_blind_reward = true
end,
defeat = function(self)
G.GAME.modifiers.no_blind_reward = G.GAME.modifiers.no_blind_reward or {}
G.GAME.modifiers.no_blind_reward = false
end,
disable = function(self)
G.GAME.modifiers.no_blind_reward = G.GAME.modifiers.no_blind_reward or {}
G.GAME.modifiers.no_blind_reward = false
G.GAME.blind.dollars = 5
end,
}```
Could someone help me on this ? My blind is meant to not give any reward money unless disabled, however i'm trying to also give it the text change
for exemple if there's no reward for the blind (like small blind on Red Stake)
and i want it when disabled to change the text to show the appropriate number of dolalrs you get, i've tried to do something but i think i'm wrong on some parts, if someone could help please
@hidden timber web based bmm lite when 🙏
(basically just the interface to download mods, no utility to enable/disable them)
wrong channel but that had a ping
Wouldn‘t take too long to make it. I‘ll take a look at it after the release of 0.3.0
In 0.3.0 we‘ll have Linux support!
through wine or through löve
any modded jokers I should test with
Well through proton
Yes, but we‘re working on multiple solutions dw
awesome
It‘s looking good so far
so from what i get, your queen can now act as all ranks at the same time ?
No, you would have to patch.
if not context.repetition and not context.blueprint then
card.ability.extra.cards_left = card.ability.extra.cards_left - 1
end
if card.ability.extra.cards_left <= 0 then
card.ability.extra.cards_left = card.ability.extra.retrigger_req
return {
message = localize("k_again_ex"),
repetitions = 2,
card = card,
}
end
end```
ok this mostly does what I want it to do, but for some reason it only gives 1 retrigger instead of 2 like I was trying. The exact code block you sent didn't work, presumably because it doesn't pass the context.repetition check
any card can act as any rank
Ah i see so like just rewriting up the base code to even do that then ?
that was hard to make
I made a quantum rank function
where you can define if a card count as a specific rank or not
this could work if I just wanted it to be the card in the 4th position every time, but I want it to carry over between multiple hands, or for example retrigger the 1st and 5th card
It should be working without other people having to add :is_rank to their card
because I used a parser to loop through code blocks and replace it so it could act that way
still W.I.P
but I think should be working w every or most cases
atleast I hope so
i have this:
~/Games/Balatro/Mods/...
~/Games/Balatro/run.sh : LDLOVELY=~/Games/Balatro/liblovely.so love src
~/Games/Balatro/src/main.lua, card.lua, ...
mucho texto
and reenable only If I have to check why smth going wrong
what should I do next
after quantum ranks?
What am I doing wrong here?
I can't seem to get the sprite for square to go back to normal unless it goes back into the square shape hitbox
yeah i did expected what i sent wouldn't work out properly, good to know you did figured eventually, and for the repetions of 1 instead of two ..well i'm not so sure, you could either try to create a card.ability.extra.(whatever name you want) set to 2 and then you could do something like *repetitions = card.ability.extra.(whatever name you want) or 2
that or since the repetitions doesn't work since there's no context.repetition active for the other part of your code, that's why i went else at first
i'm no pro at the coding but i suppose you could try experimenting this, at least you're closer to it
I think I know what I'll do next
I'mma do force triggering jokers context
and joker got triggered context
a function to get the context's of when a joker triggers as a table of strings would prolly be very cool and useful too
ok what if
I just make it 1 retrigger every 3 cards instead
and then I don't have to think
that's honestly my way to go when i struggle too hard on codding one joker
go for it
oh wait
it plays the retrigger animation but doesn't actually retrigger anything
💀
that's also why it didn't count them wrong
Yes, you retrigger in context.repetition not context.individual
yeah that's what the original plan was
but then we're back to the problem of needing to check whether a card score was from a retrigger or not
why dont you do the counter in context.repetition too
it triggers once per card
so then there wouldnt be the issue anymore
oh I guess that does make sense
Wait, so Balatro on steam doesn't automatically run through proton as far as I understood and you use Balatro via steam here?
nope
i run it directly through love
all i'd really need is a way to manually set the mods folder and executable path
Wait so you don't use Balatro via steam, right?
nope, but i have it on stesm
i run it natively for performance reasons
wine has overhead, and proton is wine based
In this case, you would have to unpack the game, no? Is there any way to do this without unpacking the game on Linux so you can run it natively
card.ability.extra.cards_left = card.ability.extra.cards_left - 1
if card.ability.extra.cards_left <= 0 then
card.ability.extra.cards_left = card.ability.extra.retrigger_req
return {
message = localize("k_again_ex"),
repetitions = 2,
card = card,
}
end
end```
Yeah this worked, it was actually so simple
no not really, and yea i do unpack the game
Mhmm, so in this case, this might introduce some legal issues for BMM
how so?
Well you're open sourcing a closed source program without having the rights for the code
thats not what open sourcing is
open sourcing would be publishing that code on github
Yes, that's right, but I mean "open sourcing" in the sense of the user being able to read the original source code of the program and BMM would enable this
ah
if you're worried abt it then the web ui idea would be more than enough for my case
Yeah dw this will most probably come soon
Isnt it already pretty common knowledge how to do this
I thought it was
Yeah but it's about getting in legal trouble
that
does anyone know what's the size image for the blinds ?
34x34 iirc
thanks
You missed several other parts of the code that target self.ability.name == "Square Joker"; there's also one in an SMODS override of Card:set_sprite
I think these patches should resolve the issue:
oh thanks. I'll try it once i'm done playing with other mods
i'm trying to make a subclass of Tag, but for some reason it doesn't appear in G.P_TAGS
SMODS.current_mod.RTD_Tags = {}
SMODS.current_mod.RTD_Tag = SMODS.Tag:extend {
obj_table = SMODS.current_mod.RTD_Tags,
obj_buffer = {},
atlas = "pencil_rtd",
class_prefix = "rtd",
set = "RTD Tag"
}
SMODS.current_mod.RTD_Tag {
atlas = "tags",
pos = { x = 0, y = 0 },
config = { type = "new_blind_choice" },
key = "workshop",
}
also can someone send a link to vanillaremade
Works, thanks!
what is this and why it looks so cursed
don't worry about it
betalatro
how do i make a different lose condition
My API has this functionality if you want to use it
Currently for challenges though. I should probably extend it for stakes
is it that difficult to setup?
i know that win setup is very easy because i made wordle that makes you win if you get it right
It's basically in two parts. One is that you can patch to disable the default lose condition if you don't want to end the game at Ante 8, and otherwise I have it routing through check_for_unlock to then run a lose condition function on the current challenge
Arguably you could do this with mod calculate
no i dont need a challenge
i made this fella
and i wanna make it so that if you beat the blind in one hand you lose
in that case, you can force a loss condition in the calculate
Oh well thats not really a lose "condition" I would say. You could just have the blind call the lose behavior
yes and i wanna know how to do that
I've got a helper function hold on
maybe condition was fautly writing for me
im working on something else in the meantime so im kinda locked out XD
G.E_MANAGER:add_event(Event({trigger = 'after', delay = 4, func = function()
G.STATE = G.STATES.GAME_OVER; G.STATE_COMPLETE = false
return true end }))
is how I do it for my mod's joker that has a loss-state associated with it
I need to check quickly to see how I handle the joker quip for the loss state
local function game_over_handler(win)
if win then
win_game()
G.GAME.won = true
else
G.STATE = G.STATES.GAME_OVER
if not G.GAME.won and not G.GAME.seeded and not G.GAME.challenge then
G.PROFILES[G.SETTINGS.profile].high_scores.current_streak.amt = 0
end
G:save_settings()
G.FILE_HANDLER.force = true
G.STATE_COMPLETE = false
end
end```
oh, you can literally just set
G.GAME.loss_condition = "ringu"
which allows you to change the quips that jimbo makes, or which joker appears on the end screen
WAIT REALLY
hold on
i might try something funny with that
https://github.com/icyethics/Kino/blob/main/items/Jokers/ringu.lua
nothing too useful in this besides the snippets, but this just so you have context for the code
how funny is it if you lose to a blue shell and get a luigi with a blue shell in hand laughing at you
This is far more extendable with jimboquips fwiw
You can run events as repeated timers, yeah
Or use the update function of the blind, depending on what you need
have you ever played mario kart?
Yeah
yk when ur in first place
and you get a blue shell thrown towards you
you get that warning sound
how do you change the joker tho?
Check out the joker quip api smods added
might be a bit late but go here and scroll down until you find # New Features ## SMODS.JimboQuip
calculate = function(self, card, context)
if context.joker_main then
return {
chips = card.ability.extra.chiptotal,
sound = "YA_Rubberducky"
}
end
if context.end_of_round and context.main_eval and not card.YA_no then
local drac = SMODS.add_card({ key = self.key })
drac.YA_no = true
play_sound("YA_Rubberducky")
end
end
}
anyone got any clue why this one doesnt work sound = "YA_Rubberducky"
but this one does play_sound("YA_Rubberducky")
hi, I need some help for a joker that I'm making. the description of my joker add "Xmult" depending on how many suit a "soul_seal" is apply the. example "1 soul = 2X, 2 soul = X4, 3 soul = X6, etc" sadly the problem is that I don't know how to code a function that detect if "suit" with "soul_seal" apply to it give the amount of "Xmult" it need to give
how many of a specific suit your seal is applied to?
where though
like in full deck?
remaining in deck?
played?
played
scoring or just played
What do I need to put after the joker name here?
get rid of that use SMODS.add_card
ohhh, ok
also why are you checking for G.consumeables
I forgot to say this code is for a deck
that doesnt answer the question
the check feels unnecessary
if theres no reason for it remove it
sorry, I'm pretty new making mods and I use a ton of copypaste
ok
Can I do that inside a deck?
well here a image because I don't how explain this. in this image I played three card which one of them has the soul_seal. this mean that the joker should give you a "Xmult" of 2. again I'm not good a explaining it so, hope this image help
true, sorry, got really confused for a moment
it doesnt. when you play a high card with a king with the seal and a two with the seal does it give x2 or x4
well, I think what I should had said that every card played with a seal apply will give a additional x2 mult doesn't matter if it a high card or a pair or a flush. so your example tell me that the king with the seal and two other card with the same seal would give a "X8".
the suit only matter for the seal to be apply. The joker only look for how many seal is played in your hand.
and even without any seal played it still gives x2 mult?
yes because I though that if you get the joker before your have the seal apply on a suit it wouldn't do something and will just end up as a useless joker,
thats what in_pool is for
like how lucky cat doesnt appear if you have no lucky cards
irregardless you'd want to do something like this
local xmult = card.ability.extra.xmult -- set in your config
for k, v in ipairs(context.full_hand) do
if v:get_seal() == "your seal's key" then
xmult = xmult + card.ability.extra.xmult
end
end
return {xmult = xmult}
can you debuff hands with jokers?
afaik its the same way blinds do it
pretty sure they get the same contexts
dont quote me on it though
i tried this and it didnt work
the function is for blinds
ill try that
worked!
How would I go about leveling up the most played hand (without spawning its respective planet card)
local tempplayed = 0
local hand
for k, v in pairs(G.GAME.hands) do
if v.played > tempplayed and SMODS.is_poker_hand_visible(k) then
tempplayed = v.played
hand = k
end
end
if hand then
SMODS.smart_level_up_hand(card, hand)
end
Why does my edition only work once and why do Jokers that get their debuff removed by it not get debuffed anymore?
It's false not "prevent_debuff"
That fixed it, thank you :)
No, remove the first if and change the second if to if context.individual and context.cardarea == G.play and context.scoring_name == 'High Card'
ok, thx
It finally works, thak you!!! I've been trying to end this joker for so long
how do I get a poker hand's level?
G.GAME.hands['modprefix_key'].level
I never noticed this but apparently the sprite drawn for the splash background is this one:
What function would I hook to save the last scored rank?
Also heck yeah nice
I'm a little confused. I checked in-game and it does give a number. so why is it comparing it as a table?
Does anyone have the list of tag contexts?
how do i access the return table of a context
Do you have Talisman installed?
yes, but for this mod I don't want talisman to be required
if to_big(G.GAME.hands[k].level) <= to_big(0) and put to_big = to_big or function(x) return x end at the top of your file.
thanks for not being on something 😇

local copy = G.jokers.cards[pseudorandom("comedy_funny_thing", 1, #G.jokers.cards)]
SMODS.add_card(copy)
for some reason it's getting the copy from my deck, and not from the jokers I have
local joker = pseudorandom_element(G.jokers.cards, 'comedy_funny_thing')
local copy = copy_card(joker)
joker.area:emplace(copy)
copy:add_to_deck()
one more thing. how can I make sure a sticker can only be applied to jokers?
sets = {Joker = true}
when i play a hand, it skips over all of the playing cards and doesn't affect anything
code:
if MyDreamJournal.chipmodkeys[key] then
local return_table = {}
local op_modification = SMODS.calculate_context({
modify_chips = true,
op_number = MyDreamJournal.keysToNumbers[MyDreamJournal.chipmodkeys[key]],
chips = amount,
is_demicolon = is_demicolon
}, return_table)
amount = return_table.chips
elseif MyDreamJournal.multmodkeys[key] then
local return_table = {}
local op_modification = SMODS.calculate_context({
modify_mult = true,
op_number = MyDreamJournal.keysToNumbers[MyDreamJournal.multmodkeys[key]],
mult = amount,
is_demicolon = is_demicolon
}, return_table)
amount = return_table.mult
elseif MyDreamJournal.glopmodkeys[key] then
local return_table = {}
local op_modification = SMODS.calculate_context({
modify_glop = true,
op_number = MyDreamJournal.keysToNumbers[MyDreamJournal.glopmodkeys[key]],
glop = amount,
is_demicolon = is_demicolon
}, return_table)
amount = return_table.glop
end```
joker:
@tranquil gull How did you get src from balatro
Bumping this
(I have a global variable and stuff kinda like how idol does it)
You mean the last scored played card's rank, or the last card that calculated an effect's rank?
The last scored played card's rank
I.e if I played only an Ace, it'd be Ace
Or if I played a straight but put a 6 at the end it'd be a 6
SMODS.score_card
Okay sweet, I'd hook that?
Where would I find it in the files
src/utils.lua
sweet, thanks!
key = "preOwned_sticker",
atlas = "comedy_stickers",
pos = { x = 0, y = 0 },
badge_colour = HEX("fd5f55"),
rate = 0.1,
needs_enable_flag = true,
should_apply = function(self, card, center, area, bypass_roll)
-- only applies if the card has values that can be made worse
return G.GAME.modifiers.enable_comedy_preowned and (card.ability ~= nil) and card.ability.extra
end,
-- rest of code works
how can I make it so that it 1.) applies less often and 2.) only applies to jokers
-# fish
the fish is from another mod I'm just doing a test run with said other mod because why not
-# the mod in question is "Tangents"
return G.GAME.modifiers.enable_comedy_preowned and SMODS.Sticker.should_apply(self, card, center, area, bypass_roll)
local card_score_hook = SMODS.score_card
function SMODS.score_card(self)
card_score_hook(self)
if not SMODS.has_enhancement(card_score_hook.card, "m_stone") then
G.GAME.current_round.j8mod_bookmark_rank = card_score_hook.card.base.value
end
end
``` this causes it to crash due to indexing local 'context'
i'd imagine it's not as simple as this
local oldsmodsscorecard = SMODS.score_card
function SMODS.score_card(card, context)
local g = oldsmodsscorecard(card, context)
if context.cardarea == G.play and not SMODS.has_no_rank(card) then G.GAME.current_round.j8mod_bookmark_rank = card.base.value end
return g
end
do Game:start_run() get called when you continue a run?
Yes.
apparently by doing that the sticker just doesn't appear at all
SMODS.Sticker {
key = "preOwned_sticker",
atlas = "comedy_stickers",
pos = { x = 0, y = 0 },
badge_colour = HEX("fd5f55"),
rate = 0.25,
needs_enable_flag = true,
should_apply = function(self, card, center, area, bypass_roll)
-- only applies if the card has values that can be made worse
return SMODS.Sticker.should_apply(self, card, center, area, bypass_roll) and G.GAME.modifiers.enable_comedy_preowned
end,
--sets = {Joker = true},
apply = function(self, card, val)
card.ability[self.key] = val
Comedy_multiplyCardValues(card, 0.75, true)
end
}
although maybe I'm doing something wrong
all working, thank you!
localize("e_base","Edition")
why is this returning "ERROR"
??
context.discard is called for every card discarded. Is there a context that's called just once, when the Discard button is pressed?
context.pre_discard
That worked. Thanks, Eremel!
idrk where to ask this but i figured this was good enough, but my game kept crashing after loading the ut/dt mod. and due to discord's limit on messages, i can't send the error log
Send the message as a file.
where is the log file?
Mods/lovely/log
Hey guys, can you help me? I'm trying to inject code to functions/state_events.lua so that i can override which glass card breaks. However upon loading it tells me that pattern on target resulted in no matches. For other files code injection works but it struggles with state_events for some reason...
youre probably patching after smods
which would replace the pseudorandom bit with smods probability systems
for a little more context, the game crashes when i play a hand at all, it plays the intro and title properly and works as intended when unloading the mods, but crashes apon playing a hand with talisman and the UT/DR mods loaded
localize("e_base","Edition")
returns "ERROR"
and localize("e_negative","Edition") also returns "ERROR"
what I'm doing wrong?
if edition_name == nil then edition_name = localize("e_base","Edition") end
As in the value of edition_name gets set to "ERROR"
i think, lemme check
also ask in modding-chat not modding-dev
ah alr, thank you
that worked, thank you so much for the help!
out of curiosity and a genuine question; would the other setting besides vanilla work for the ut/dr pack? also what causes it to crash to begin with? i'm interested in game dev and want to analyze these crashes also to pick it out again if it happens in the future
Yes, SMODS overrides glass cards.
You're right, found the SMODS override. Thanks guys!
No, it takes ownership of glass cards.
Huh so i need to patch game_obect.lua from smods then?
ok, thanks guys! I was really struggling on this one
Is there a way to check if the player is leaving a booster pack?
not being in one, but getting out of it
Hook G.FUNCS.end_consumeable
thanks!
🤔
Need to redo this whole menu at some point but I'll be busy with stuff for the next month at least
Is it possible to "fake" setting a blind? i.e. calculate the context of setting a blind for a joker
-# I'm working on Modeling Clay and I need it to do that whenever it changes what Joker it's copying
SMODS.calculate_context({setting_blind = true, blind = G.GAME.round_resets.blind})
crashes the game, no error log
do you need it for everything in the game or just a specific joker?
just a specific joker
try doing joker:calculate_joker({...}) instead, where joker is the card object of the joker you need to get calculated
(same arguments as what somethingcom gave you)
alrighty!
stack overflow
ah, wait
i wasn't trying to copy the effect, i was trying to copy the joker itself, which caused an infinite loop
Code?
calculate = function(self, card, context)
-- thanks, somethingcom515 !
if (context.setting_blind or context.pre_discard) and not context.blueprint then
card.ability.extra.copying_joker = pseudorandom_element(G.P_CENTER_POOLS.Joker, 'j8mod_modeling_clay').key
-- vv problem line
G.P_CENTER_POOLS.Joker[card.ability.extra.copying_joker]:calculate_joker({
setting_blind = true,
blind = G.GAME.round_resets.blind
})
-- ^^ problem line
return {
message = localize { type = 'name_text', set = "Joker", key = card.ability.extra.copying_joker } .. "!",
colour = G.C.RARITY[G.P_CENTERS[card.ability.extra.copying_joker].rarity]
}
end
if card.ability.extra.copying_joker then
local key = card.ability.extra.copying_joker
G.j8mod_savedjokercards = G.j8mod_savedjokercards or {}
G.j8mod_savedjokercards[card.sort_id] = G.j8mod_savedjokercards[card.sort_id] or {}
if not G.j8mod_savedjokercards[card.sort_id][key] then
local old_ability = copy_table(card.ability)
local old_center = card.config.center
local old_center_key = card.config.center_key
card:set_ability(key, nil, 'quantum')
card:update(0.016)
G.j8mod_savedjokercards[card.sort_id][key] = SMODS.shallow_copy(card)
G.j8mod_savedjokercards[card.sort_id][key].ability = copy_table(G.j8mod_savedjokercards[card.sort_id]
[key].ability)
for i, v in ipairs({ "T", "VT", "CT" }) do
G.j8mod_savedjokercards[card.sort_id][key][v] = copy_table(G.j8mod_savedjokercards[card.sort_id]
[key][v])
end
G.j8mod_savedjokercards[card.sort_id][key].config = SMODS.shallow_copy(G.j8mod_savedjokercards
[card.sort_id][key].config)
card.ability = old_ability
card.config.center = old_center
card.config.center_key = old_center_key
for i, v in ipairs({ 'juice_up', 'start_dissolve', 'remove', 'flip' }) do
G.j8mod_savedjokercards[card.sort_id][key][v] = function(_, ...)
return Card[v](card, ...)
end
end
end
return G.j8mod_savedjokercards[card.sort_id][key]:calculate_joker(context)
end
end,
you'll probably recognize this one
-# there's also the very minor issue of scaling Jokers breaking in the description of the card but that's another problem; was going to ask if there was a way to reset/refresh the variables after setting the ability of a card
G.P_CENTER_POOLS.Joker has centers, not cards.
i wasn't sure what to use instead of just card, which caused a stack overflow
You do it after you create the fake card, and use that.
so more something like this:
if (context.setting_blind or context.pre_discard) and not context.blueprint then
card.ability.extra.copying_joker = pseudorandom_element(G.P_CENTER_POOLS.Joker, 'j8mod_modeling_clay').key
return {
message = localize { type = 'name_text', set = "Joker", key = card.ability.extra.copying_joker } .. "!",
colour = G.C.RARITY[G.P_CENTERS[card.ability.extra.copying_joker].rarity]
}
end
if card.ability.extra.copying_joker then
local key = card.ability.extra.copying_joker
G.j8mod_savedjokercards = G.j8mod_savedjokercards or {}
G.j8mod_savedjokercards[card.sort_id] = G.j8mod_savedjokercards[card.sort_id] or {}
if not G.j8mod_savedjokercards[card.sort_id][key] then
local old_ability = copy_table(card.ability)
local old_center = card.config.center
local old_center_key = card.config.center_key
card:set_ability(key, nil, 'quantum')
card:update(0.016)
G.j8mod_savedjokercards[card.sort_id][key] = SMODS.shallow_copy(card)
G.j8mod_savedjokercards[card.sort_id][key].ability = copy_table(G.j8mod_savedjokercards[card.sort_id]
[key].ability)
for i, v in ipairs({ "T", "VT", "CT" }) do
G.j8mod_savedjokercards[card.sort_id][key][v] = copy_table(G.j8mod_savedjokercards[card.sort_id]
[key][v])
end
G.j8mod_savedjokercards[card.sort_id][key].config = SMODS.shallow_copy(G.j8mod_savedjokercards
[card.sort_id][key].config)
card.ability = old_ability
card.config.center = old_center
card.config.center_key = old_center_key
for i, v in ipairs({ 'juice_up', 'start_dissolve', 'remove', 'flip' }) do
G.j8mod_savedjokercards[card.sort_id][key][v] = function(_, ...)
return Card[v](card, ...)
end
end
end
return G.j8mod_savedjokercards[card.sort_id][key]:calculate_joker(context)
end
if context.setting_blind or context.pre_discard then
card:calculate_joker({
setting_blind = true,
blind = G.GAME.round_resets.blind
})
end
(just moving the calculate_joker function under setting the ability)
No, change card to G.j8mod_savedjokercards[card.sort_id][card.ability.extra.copying_joker]
alright, thank you
one last thing - as of right now, the joker properly displays what joker it's copying on the side, but the values themselves don't update. is there a way to fix that?
info_queue[#info_queue + 1] = G.P_CENTERS[card.ability.extra.copying_joker]
(just doing this as of now)
Yes, ```lua
local center = G.P_CENTERS[card.ability.extra.copying_joker]
local other_center = SMODS.shallow_copy(center)
other_center.loc_vars = function(self, info_queue, uncard) return center.loc_vars(self, info_queue, G.j8mod_savedjokercards[card.sort_id][card.ability.extra.copying_joker]) end
table.insert(info_queue, other_center)
damn, awesome!
thank you so much 🙇
it doesn't seem to work
(should be +8 chips)
loc_vars = function(self, info_queue, card)
-- super thanks somethingcom515 !
local center = G.P_CENTERS[card.ability.extra.copying_joker]
local other_center = SMODS.shallow_copy(center)
other_center.loc_vars = function(self, info_queue, uncard)
return center.loc_vars(self, info_queue,
G.j8mod_savedjokercards[card.sort_id][card.ability.extra.copying_joker])
end
table.insert(info_queue, other_center)
info_queue[#info_queue + 1] = { key = "credits_sharb", set = "Other" }
return { vars = { card.ability.extra.copying_joker and localize({ type = 'name_text', set = "Joker", key = card.ability.extra.copying_joker }) or 'Nothing', colours = { card.ability.extra.copying_joker and G.C.RARITY[G.P_CENTERS[card.ability.extra.copying_joker].rarity] or G.C.FILTER } } }
end,
(code0
also, I got merry andy, which gave me a permanent -1 hand size
...and then got turtle bean, which gave me a permanent +5 hand size
Yes, because you're adding it to the deck but you're not removing from the deck.
wouldn't that mean i'd have to call the removing from deck context (?) before setting the new ability?
-# is there even a context for that
No, card:remove_from_deck()
-# (the other thing doesn't though)
i'll figure this out later
sorry for clogging up the chat ^^;;
how do you set ante to 0?
ease_ante(-G.GAME.round_resets.ante)
how does saved work? (prevent death and set ante to 0)
return {
message = localize('k_saved_ex'),
saved = 'ph_mr_bones',
colour = G.C.RED
}```
calculate = function(self, card, context)
if context.end_of_round and context.game_over and context.main_eval then
ease_ante(-G.GAME.round_resets.ante)
G.E_MANAGER:add_event(Event({
func = function()
G.hand_text_area.blind_chips:juice_up()
G.hand_text_area.game_chips:juice_up()
play_sound('tarot1')
card:start_dissolve()
return true
end
}))
return {
message = localize('k_saved_ex'),
saved = 'ph_mr_bones',
colour = G.C.RED
}
end
end
}```
does saved replace the text in the payout screen?
Yes.
is there an easy way to do misprint-style dynatext, or am i going to have to figure out the hard way
It's giving nubby's number factory vibes
How do you make a new consumeable type like a tarot or spectral
thx
Do you make a seperate lua file for this or put it in main.lua?
No, it can be anywhere, as long as it is being loaded.
alr ty
SMODS.ObjectType {
key = 'Scrumables',
primary_colour = HEX('fa9629'),
secondary_colour = HEX('148929'),
no_collection = false,
}
``` how do you make it actually show up?
No, it's SMODS.ConsumableType
SMODS.ConsumableType {
key = 'Scrumables',
primary_colour = HEX('fa9629'),
secondary_colour = HEX('148929'),
name = " test2",
loc_text =
{
name = 'test1',
collection = 'Scrumables',
undiscovered = {
name = 'test3',
text = { 'test4' },
},
}
}
what am i doing wrong here?
for context the colour does show up but no names or items inside of the consumable type itself
I remember seeing a mod that had a rescoring mechanic, I think Autumn's mod?
question: how do i get the number of times the most played hand has been played
I use this function to get a list of the most played hands. I added _tally to the return so you immediately get the times played as well, but you can check it for any hand with the G.GAME.hands[key_of_the_hand].played
function get_most_played_hand()
local _tally
local _hands = {}
for k, v in ipairs(G.handlist) do
if G.GAME.hands[v].visible and (_tally == nil or G.GAME.hands[v].played > _tally) then
_hands = {}
_hands[#_hands + 1] = v
_tally = G.GAME.hands[v].played
end
if G.GAME.hands[v].visible and (_tally == nil or G.GAME.hands[v].played == _tally) and not G.GAME.hands[v].played == 0 then
_hands[#_hands + 1] = v
end
end
return _hands, _tally
end
does this work for consumables
What do you mean?
im trying to make a consumable that gives you a dollar per time you played your most played hand
does this code also work on consumables
Oh, yeah, this is just a helper function. You can call it any time during a run, on anything, and it'll work
thanks
do i place this in loc_vars or in use
in neither. It's a function, so you put it in your code somewhere, and then call it using in loc_vars and in use()
local _handlist, _most_played_number = get_most_played_hand()
...end of the conusmables section it is
I recommend just making a file for helper functions, but that works, yeah
i insert this line in loc_vars and in use() right
Yes. It will call that function, and return a table object that will be stored in _handlist, that will contain every hand that counts as most played, and an integer in _most_played_number that is how many times those hands have been played.
aight thanks
Quick question, what's the formula to calculate the chance for custom rarity Jokers to show up in the shop?
there's no consistent one, and it depends on what chances you're going to include. But the vanilla rarities add up to 100, so if the game's chosen a joker as the object to spawn, and your custom rarity has a spawn rate of 0.01, or 1%, then the total chances add up to 101, so there's a 1/101 chance a joker of your custom rarity will be chosen
if you add other mods that add their own custom rarity, these changes change, though
Okay, but the base is rarityChance/allRarityChances?
yes
Okay, thank you :3
question: how do i change the background when opening a booster pack
bump
Do you have set = 'Scrumables' in the consumables?
booster packs can have their own ease background function, which you can use to call the main function
ease_background_colour = function(self)
ease_background_colour_blind(G.STATES.TAROT_PACK)
end,
https://github.com/nh6574/VanillaRemade/blob/main/src/boosters.lua
for where I grabbed that snippet from and how to add it to your booster
and i get this when i do try to insert one of my consumeables into the pool
okay so this was a typo in the set
i can now put consumeables into it
Okay so i can now add jokers into it however the name keeps being error. How do you change the name?
(still lost on why the name is ERROR)
finally figured it out 🙏
How would one go about combining sprites together on a joker
in what sense?
shaders or drawsteps will probably be the way to go, depending on what you're aiming for
like say I have a handful of sprites for a "right half" and a "left half" for a joker, and when the joker gets created it will choose a random right half and random left half to use
and making every combination in an atlas would just take up space and also be pretty infeasable to extend
Drawsteps definitely work for that! I have a similar system in my mod that uses 64 sprites to construct a roasted chicken. My own approach would be to set the base sprite of the jokers to just an empty card, and then pick the two halves based on information from the joker.
I've never used drawsteps ever in my life so like how would it work
i'm checking now to see if the wiki has it documented, haha, give me a sec
smods.drawstep?
https://github.com/Steamodded/smods/wiki/SMODS.DrawStep
is the wiki documentation but that's really foundational documentation that doesn't teach you how to use it. I'd look through the smods code to see examples, as well
https://github.com/icyethics/Kino/blob/main/items/Enhancements/superhero.lua
this is code from my own mod where I use it to put a soul sprite on playing cards for an enhancement, for some more examples. Though in your case, I recommend pre-loading the sprites somewhere. I'd first just mess around seeing what you can do, though, because a lot of learning the drawstep is just realizing how things get rendered in the game by seeing it happen
why wont this work, it just does nothing...
local function table_contains(table, item)
local ret = false
for i=1, #table do
if table[i] == item then
ret = true
break
end
end
return ret
end
local function choose_random_jokers(amount, seed)
local chosen_indices = {}
for i=1, amount do
local c = 0
local function choose()
c = random(seed, 1, #G.jokers.cards)
if table_contains(chosen_indices, c) then
repeat
c = random(seed, 1, #G.jokers.cards)
until not table_contains(chosen_indices, c)
end
end
choose()
table.insert(chosen_indices, c)
end
local joker_table = {}
for k,v in ipairs(chosen_indices) do
table.insert(joker_table, G.jokers.cards[v])
end
return joker_table
end
.......
SMODS.Blind {
.......
calculate = function(self,blind,context)
local function debuff_half()
if G.jokers.cards and #G.jokers.cards > 0 then
-- get a random half of G.jokers.cards
local chosen = choose_random_jokers(math.ceil(#G.jokers.cards/2), 'qerehir124')
for k,v in ipairs(chosen) do
G.GAME.blind:debuff_card(v)
end
end
end
if context.setting_blind then
debuff_half()
end
if context.after then
debuff_half()
end
end
}
can anyone tell me how exactly does sticker offsets worked? for something to fix?
im tryna debuff a random half of the jokers after every played hand and at the start of blind
the heck-? I tried pixel-size but it also changed the size of the hitbox.
I just realized the sticker graphics were already positioned. though I have an idea.
Crashing on modload; any idea why? Effect intent is 4 in 13 cards drawn are drawn face-down, +mult when the joker is scored
whats the crash log
nvm it's this comma
this'll pick a random card on purchase right?
add_to_deck = function(self, card, from_debuff)
local ids = {14,13,12,11,10,9,8,7,6,5,4,3,2}
local suits = {'Diamonds','Spades','Hearts','Clubs'}
local c = pseudorandom('j_thecloset_mike_wazowski' .. G.GAME.round_resets.ante)
self.config.extra.card_id = ids[c]
local y = pseudorandom('j_thecloset_mike_wazowski' .. G.GAME.round_resets.ante)
self.config.extra.card_suit = suits[y]
end
pseudorandom returns a value between 0 and 1
no, c and y would be numbers between 0 and 1, also you don't want to save things to self because that's the prototype not the card
card.ability.extra.card_id = pseudorandom_element(ids, 'j_thecloset_mike_wazowski' .. G.GAME.round_resets.ante) is what you want
Still crashes, bleph
that second if shouldnt be there
also i dont really like this file loading method you have because it makes reading crashlogs harder imo
oh ok thanks
Cut both the if and the then, or just the if?
just the if, the then has to be there for the if on the previous line
is there a way to get the hand score
OH I didn't see the "and" there, that makes sense ty!
like the chips and mult of the current poker hand?
total score of the hand
there are some entries on getting score on the vanillaremade wiki
SMODS.calculate_round_score()
ok
I thought of making custom sticker diaplays like this but not sure how this would work to replace the stickers on a single joker
can i hook contexts
or like
add a function
where if said context happens
then it affects a global variable
is there any coding resources for balatro/lua
VanillaRemade Wiki and SMODS Wiki For Balatro Modding specifically.
YouTube is a good place to look for general lua programming.
Here's one:
https://www.lua.org/pil/contents.html
Unless you're making complex mods, I'd stick to the VanillaRemade and SMODS wiki rather than YT Lua-tutorials
yes
Sure it is. Just call it G.GAME.<your_mod_prefix>_your_variable_name.
bump
has anyone have an idea on how I can apply custom sticker atlas to replace a sticker atlas for a single joker for instance?
i think you would have to change the drawstep that draws the stickers for that joker
I haven'T done that exactly yet.
like i know how to make global variables
hook sticker drawsteps
but would there be any issue with just
adding a context
then modifying said variable
global mod calculate
Well, that's how you make one. And the <your_mod_prefix> bit is just so it doesn't conflict with any other mod's similarly-named global variable.
thanks
I think you need to be more clear on what you want to achieve. If what you mean is "how do I use calculate contexts outside of a joker" then use the global mod calculate like paradox said
Alright, where exactly I can find that?
the sticker drawsteps?
whos paradox
i dont. know
me
ah
search smods for SMODS.DrawStep
the only person who said the thing I wrote 😭
like this one?
no, that's a function to modify a variable
alright. sorry if I had to askcause I am slowly going back into modding but I need to re-learn what I had remember once before
oh
SMODS.current_mod.calculate = function(self, context)
im confused
Now how do I hook it?
vanillaremade wiki has a guide on hooks
local sticker_drawstep_ref = SMODS.DrawSteps.stickers.func
SMODS.DrawSteps.stickers.func = function (self, layer)
if self.config.center_key == "your joker key" then
-- code
else
sticker_drawstep_ref(self, layer)
end
end
Shall you be done soon? I have a big debacle I want sorted out and I don't want to step on your toes while at it.
alright-y then.
that's more annoying, just ask your question
what am i supposed to do with this?
I apologize, I didn't mean to be rude.
I've managed to trigger a stack overflow and crash my game through a feature in my mod. It stems from a custom Tarot card of mine, Rebirth, which gives a copy of the run's most-used consumable card. On this particular run my most-used consumable was The Fool, so I activated a Rebirth to grab another copy of The Fool, and then my game seized up and crashed.
I took this up with wingedcatgirl and she said it's 'cause since each one's set to create the other, the two cards try to create infinite info queues, and an attempt to reduce the length of a generated info queue failed, crashing the game as before.
Here's a small excerpt from the crash log; it's got thousands of statements just like this one.
(2315) Lua global 'generate_card_ui' at file 'functions/common_events.lua:3143'
Local variables:
_c = table: 0x3ad319f0 {order:1, _d:false, unlocked:true, set:Tarot, consumeable:true, _saved_d_u:true, cost:3, discovered:true, effect:Disable Blind Effect, pos:table: 0x3ad31ba0 (more...)}
full_UI_table = table: 0x3b2a7a30 {badges:table: 0x3b2a79d0, info:table: 0x3bc69120, type:table: 0x3bc69148, main:table: 0x3bc690f8, card_type:Tarot, name:table: 0x3bb04550, box_colours:table: 0x3f6b0da8 (more...)}
specific_vars = nil
card_type = nil
badges = nil
hide_desc = nil
main_start = nil
main_end = table: 0x3cb3c2d8 {1:table: 0x3cb3c318}
card = nil
first_pass = nil
desc_nodes = table: 0x3cb3c190 {1:table: 0x3cb3c870, 2:table: 0x3cb3ca28, 3:table: 0x3cb3cfe0, 4:table: 0x3cb3d188, 5:table: 0x3cb3c2d8, name:The Fool, name_styled:table: 0x3cb40a48 (more...)}
name_override = nil
info_queue = table: 0x3cb3c168 {1:table: 0x3b4793b8}
loc_vars = table: 0x3cb3c698 {1:Rebirth}
cfg = table: 0x3ad31c00 {}
(for generator) = C function: builtin#6
(for state) = table: 0x3cb3c168 {1:table: 0x3b4793b8}
(for control) = number: 1
_ = number: 1
v = table: 0x3b4793b8 {can_use:function: 0x3b479528, loc_vars:function: 0x3b4790b0, _saved_d_u:true, original_key:rebirth, mod:table: 0x3b1bfed8, _u:true, _discovered_unlocked_overwritten:true (more...)}
it works like a joker's calculate
you should post the code and the full log
you can just upload the file for the log
The full log is that, thousands of times over, and clocks in at 4MB.
But I can give you a relevant excerpt from Rebirth's code.
local rebirth_c = G.GAME.LAPSEMS_most_used_consumable_that_is_not_rebirth and G.P_CENTERS[G.GAME.LAPSEMS_most_used_consumable_that_is_not_rebirth] or nil
local most_used_consumable_that_is_not_rebirth = (rebirth_c and localize{
type = 'name_text',
key = rebirth_c.key,
set = rebirth_c.set
}) or localize('k_none')
local colour = ((not rebirth_c) and G.C.RED) or G.C.GREEN
if (not (not rebirth_c or rebirth_c.name == 'Rebirth')) then
info_queue[#info_queue + 1] = rebirth_c
end
where is this
loc_vars?
or a patch
It's within the code for the Rebirth card itself, within its SMODS.Consumable{} bit.
so loc_vars?
is there a context for after the score is totalled?
context.after
See also:
use = function(self, card, area, copier)
G.E_MANAGER:add_event(Event({
trigger = 'after',
delay = 0.4,
func = function()
if G.consumeables.config.card_limit > #G.consumeables.cards then
play_sound('timpani')
SMODS.add_card({key = G.GAME.LAPSEMS_most_used_consumable_that_is_not_rebirth})
card:juice_up(0.3, 0.5)
end
return true
end
}))
delay(0.6)
end,
can i see the whole function
That is the whole use() function.
loc_vars = function(self, info_queue, card)
local rebirth_c = G.GAME.LAPSEMS_most_used_consumable_that_is_not_rebirth and G.P_CENTERS[G.GAME.LAPSEMS_most_used_consumable_that_is_not_rebirth] or nil
local most_used_consumable_that_is_not_rebirth = (rebirth_c and localize{
type = 'name_text',
key = rebirth_c.key,
set = rebirth_c.set
}) or localize('k_none')
local colour = ((not rebirth_c) and G.C.RED) or G.C.GREEN
if (not (not rebirth_c or rebirth_c.name == 'Rebirth')) then
info_queue[#info_queue + 1] = rebirth_c
end
local main_end = {
{
n = G.UIT.C,
config = { align = "bm", padding = 0.02 },
nodes = {
{
n = G.UIT.C,
config = {align = "m", colour = colour, r = 0.05, padding = 0.05},
nodes = {
{
n = G.UIT.T,
config = {
text = ' ' .. most_used_consumable_that_is_not_rebirth .. ' ',
colour = G.C.UI.TEXT_LIGHT,
scale = 0.3,
shadow = true
}
},
}
}
}
}
}
return {
vars = {
card.ability.max_cards
},
main_end = main_end
}
end,
wingedcatgirl recommended I limit the info_queue to #info_queue < 10, but to no avail.
ok, try this
if not card.fake_card and (not (not rebirth_c or rebirth_c.name == 'Rebirth')) then
info_queue[#info_queue + 1] = rebirth_c
end
print not printing
Before I try that, may I ask: what is card.fake_card?
remove the cardarea bit
it's when a card is created for the info_queue
so it stops infinite tooltips
why is the third so small?
What've you set the {s:} value to?
its still not printing
ok thx
to replace a joker at an index do you do G.jokers.cards[idx] = card?
weird, what SMODS version do you have
yes
1.0.0-beta-1016c
hmm no idea then
It worked! 🎉
like
local r = pseudorandom('bl_thecloset_ochre_obelisk', 1, #G.jokers.cards) -- random joker index num
local g = pseudorandom('bl_thecloset_ochre_obelisk', 1, 2001) -- random obelisk num
local card = nil
if g ~= 1 then
local obelisk = 'obelisk_' .. g
local card = SMODS.create_card(key = 'j_thecloset_' .. obelisk)
else
local obelisk = 'obelisk'
local card = SMODS.create_card(key = 'j_obelisk')
end
G.jokers.cards[r] = card
?
You know, I could probably use this gimmick to farm Fortune Teller ad infinitum. I should probably forbid that.
<@&1133519078540185692>
yes but you also need to move the joker in that position
if you want to move all jokers right then you need more logic
how?
the same way
I just want to replace the joker at the selected index with a random obelisk
then you need to either destroy that joker or use set_ability to change it
what's giong on?
?
what?
I saw you ping the mods not sure if it was me or not
Oh.
Well can't blame cause I was a victim to geting hacked on that and messed me up as pstd triggered on me.
anyways, I had this issue as I wasn't sure how exactly you can replace the sticker atlas thing.
not sure if I'm doing this right.
you have to use your own sprite instead of the G.shared_stickers one
i dont really have any examples, sorry
I see.
How do I check when the score catches fire?
how do i destroy a random held Tarot?
if context.final_scoring_step and (hand_chips * mult > G.GAME.blind.chips) then
Paperbacks' Ice Cube Joker uses this aswell
No, it should be SMODS.calculate_round_score() > G.GAME.blind.chips
Oh?
Doesn't this work fine, though?
No, because of SMODS.ScoringCalculation existing now
So chips * mult will not always be accurate
ah
what?
I tried to use G.ASSET_ATLAS["stickers_hoylecasino"] and it doesn't find it?
how would I make a song play when I'm fighting an specific boss?
how do i modify probabilities for multiple specific jokers
what's the use case?
Oops! All 6s but for certain Jokers (1,2,3,4,5,6,7,9,10,11,12,13,14 & 15 Ball)
If you have control over the jokers, you can use the additional key argument to make it so only specific types of chance are affected
What you could also do is just use the odds context and check if those jokers are part of a specific set. If you add 'is_num_Ball = true' to those jokers, you can just check for 'card.config.center.is_num_ball'
i have noticed that you excluded 8 ball
where would i add is_num_ball = true?
Yes.
config.extra?
like where? 😭
Why do you need to seperately draw a sticker on the sprite instead of just making it part of the sprite
this is an example from my own code. The highlighted line is one I use to check if the joker can be targetted by bullet loading
SMODS.Joker {
key = '1_ball',
atlas = 'Jokers',
blueprint_compat = true,
rarity = 1,
cost = 5,
pos = ball_positions[1],
loc_txt = {
...
},
unlocked = true,
discovered = true,
config = { extra = { odds = 4, rank = 14 } },
loc_vars = function(self, info_queue, card)
local numerator, denominator = SMODS.get_probability_vars(card, 1, card.ability.extra.odds, 'thecloset_balls_1')
return { vars = { numerator, denominator } }
end,
...
}
ohh
bump
Whats even the goal
how do i get if a joker has it tho?
like
ik you said card.config.center.is_num_ball
but
where is card being referenced?
oh nvm
;-;
how would i add that boolean to the original 8 Ball aswell?
I'd just hardcode that into your check, honestly. There's definitely ways, but that means either using take ownership, or injecting it before the run starts
How would i take ownership?
I've never used that before
but I recommend just hardcoding in the check to see if it's 8 ball, for now
like card.config.center.key == '8_ball'?
it would be j_8_ball
yeah
can you show that line that's crashing?
nvm I think I figured out I just had to include ones like eternal, perishable and rental but for some reason it doesn't show the clear for the base chips upon clearing?
nvm I tihnk I managed to figure out
this is valid right?
SMODS.create_card(key='thecloset_ball_ball', edition='e_negative')
I think it was because of the caps I ha dthat doesn't render the sticker
so if anyone had a crash because of forgotten stickers and such. here's this as a reference.:
sorry for the trouble
I think it works pretty well in some way like this. though not sure if I were to release this mod so if any mods with thier own specific stickers can be made into this.
sorry, how would I make a song play when I'm fighting an specific boss?
Now time to do the other ones just in case
how can i make all legendary jokers able to spawn in the shop
basically change the weight of the Legendary rarity.
for a Voucher.
dead chat? 😭
Pointing out that no one responded to you in fifteen minutes is a good way to get no one to respond to you ever
if edition_name == nil then edition_name = localize("e_base","Edition") end
why localize("e_base","Edition") is returning "ERROR" what am I doing wrong here?
bump
pls speed I need this
😢
you get an edition name with the last method here
https://github.com/nh6574/VanillaRemade/wiki#how-can-i-get-the-localized-name-of-x
thanks
in select_music_track for your smods sound, return a big number when the boss blind is active
how do I detect if my boss is active from the smods sound? Is there a way to detect a boss' key?
i would like to see how you coded it if possible
https://github.com/thenumbernine/lua-parser
I used this parser
and for the function(probably theres like a 1000 better ways of doing it but I'm not good at that stuff) I used the following code:
this is where the ast classes are defined
local function advanced_destroying_logic(ast_root, args)
args.card_variable = args.card_variable or "card"
return parser.ast.traverse(ast_root, function (node,parent)
if parser.ast._call:isa(node) and parser.ast._indexself:isa(node.func) then
print("found call?")
print("func:",node.func.expr.name)
print("key:",node.func.key)
if node.func.key == "start_dissolve" or node.func.key == "remove" then
return parser.ast._call(
parser.ast._var("SMODS.advanced_destroying"),
parser.ast._var("{card_being_destroyed="..node.func.expr.name..",".."card_destroying="..args.card_variable.."}")
)
end
end
return node
end)
end
This is simpler stuff that could maybe help u understand how it works (replacing :start_dissolve or :remove with another function)
it only checks for function calls that includes : because of parser.ast._indexself:isa(node.func)
Can't make it work, dunno why
Is there any way to make it so (*temporary) variables are actually named in the crash log code? Or is that just impossible, since they're all local variables?
No, all local variables are shown in the crash log with their name and value.
Ah. Guess that's just for loops then
is it possible to "transform" a joker without removing it and adding a new joker?
like i want a consumeable that rerolls either all or one joker havent decided yet into just a completely random one
card:set_ability('j_modprefix_key')
Welp, I think I start to like this detail especially since I thought of like making the stickers into the same style as well but yeah I might explain that later.
can't get the set_ability() event to trigger for some reason
Replace G.hand.cards[v] with v
Also context.cards_destroyed and context.glass_shattered don't exist.
i'm using glass joker as reference and i guess it does?
what's the right context for checking for glass breaking then
No, it doesn't.
That is there but it never triggers.
question real quick, is there somewhere where there's a list of colors you can use in the text? like the {C:mult} stuff? Also am i able to use hexcodes there? and also also, am I able to use those same color codes in the name of the joker?
the key is in G.GAME.blind.config.blind.key
also it should be bl_modprefix_key
- yes the smods text styling wiki
- no but you can add those, I'll link to how in a sec
- yes
thank you
thx!!
how can i make a voucher like the tarot merchant but for my consumeables
what's responsible for triggering all of a blind's effects? is it the calculate or set_blind? (im trying to make a challenge deck where every blind boss beaten has its effects become permanent until the end of the run)
it tells me I'm giving blind a nil value
):
return G.GAME.blind and G.GAME.blind.config.blind.key == 'bl_modprefix_key' and math.huge or false
both
but set_blind is initial setup
so you wouldn't need to run it again
that being said, you would need to prevent it from reversing that setup in the disable or defeat call
Why are you storing the blinds in a local variable?
thx!! What would I have to put in G.GAME.blind to make it work for a deck?
i give up trying to brute force this...
how do i get a joker to create a consumable? trying to test if one of my jokers will work with creating a consumable.
current code below
it's currently set to try and create The Fool Tarot
i have the chance set to 1,1 to have it auto succeed
You mean you want the music to play when a specific deck is selected?
SMODS.add_card({key = 'c_fool'})
thank you
return G.GAME.selected_back and G.GAME.selected_back.effect.center.key == 'b_modprefix_key' and math.huge or false
thx!! I'm almost done with the mod and you all are helping me a lot, thanks!!
2 things, one of which might be the main issue
- image below the text for the joker isn't being rendered.
- didn't create the card
fixed the text rendering-
Code?
First image: the full code block
Second image: code to summon The Fool
jimbo's wife died 💔
-# will be unresponsive for a moment
well can't say im that surprised but it doesnt work, it doesn't crash, but the main gimmick/rule of it always applying the effects of every beaten boss blind in the run just doesn't happen
made it this, not much changes
No, you would store the key, not the blind, also you would store it in G.GAME
is the key the blind.name? and what next?
No, it's G.GAME.blind.config.blind.key
alright but i dont know what to do with the keys tho
How do I get an image to flash across the screen? (similar to that one joker card in Tangents that randomly flashes Kendrick Lamar on the screen)
I plan on making a joker that disables the boss blind when you trigger its effect, and when you do that it shows this image
very hard stare
cooked
g.game doesn't exist when mods are loaded
G.GAME does not exist during load
you should create it within the challenge in apply probably
alright, but now it doesnt crash and instead soft locks when the challenge is selected
Hello everyone, I was trying to make my own mod using Joker forge and all of a sudden this crash message popped up and I have no idea why its crashing the game
I have the code here for the joker
G.P_CENTERS.[card.ability.extra.m_lucky]
should be
G.P_CENTERS[card.ability.extra.m_lucky]
alright it doesnt brick or crash but it still just doesn't work, ran into the one that debuffs all clubs, and in the next ante, clubs weren't debuffed, so its not persisting like its supposed to, also even in ante 1 before any boss blind its super slow for some reason, takes a while to do anything
uh, now its crashing for a different reason
you can use the mod calculate function and context.tag_added to update a custom global variable every time you obtain a tag
global mod calculate best feature
context.tag.key do i use this?
no, context.tag_added is itself the tag that was added
so it should be context.tag_added.key
so context.tag_added will be the tag itself
yes
im confused
is there a way to just make 2 double tags before i get the tag
wait a minute
...
thats gonna need a hook isnt it
.
you're trying to track the most recently obtained tag, right
track and create two copies
(im trying to make hit from balatro ball z)
ok yea
so in your mod's global calculate function, during context.tag_added you should save context.tag_added.key to G.GAME.modprefix_latest_tag (or whatever you want to call it, just use your mod prefix to keep it unique). then when the thing that creates the copies happens, it can use G.GAME.modprefix_latest_tag as the key to create the tags
like this?
yea that should be good
@solid salmon do you think you could figure this out (i am very desprite)
ok
the joker makes a lucky card when the shop is exited but that crashes the game because the code says base_card but what would i replace that with to make it work
should be mostly good, although there's some special behavior you have to handle for orbital tag. don't remember it off the top of my head
ya that's it
What would I replace the Base_card thing to while keeping the effect
show code around the area
remove base_
btw i had an issue with making this card create dupes of itself
(yes even copying its own edition excluding negative)
well, it made the card but it applied the edition and seal to the joker itself and the created card was not lucky
the joker had a seal, a blue one to be exact
lemme check the wiki to help ya
thank you
what kind of card did you want to make?
a lucky card with a random seal and edition
ok
-- in ComedyGold.lua:
-- this is for image loading
ComedyGold.show_img = {}
ComedyGold.img_timer = {}
ComedyGold.images = {}
-- love.draw stuff for image flashing
local drawhook = love.draw
function love.draw()
drawhook()
if ComedyGold and ComedyGold.show_img then
for img, bool in ipairs(G.ComedyGold.show_img) do
if ComedyGold.img_timer[img] then
Comedy_show_image(img)
end
end
end
end
-- in utils.lua:
-- utility function to load an image
function Comedy_load_image(name)
local path = (ComedyGold.path .. "assets/custom/" .. name .. ".png")
local fileData = assert(NFS.newFileData(path))
local img = assert(love.image.newImageData(fileData))
return (assert(love.graphics.newImage(img)))
end
-- utility function to flash an image on the screen
-- I recommend setting the timer to 0.5 (it's in seconds)
function Comedy_show_image(name)
local xScale = love.graphics.getWidth() / 1920
local yScale = love.graphics.getHeight() / 1080
if ComedyGold.show_img[name] then
if not ComedyGold.images[name] then
ComedyGold.images[name] = Comedy_load_image(name)
end
local a = math.min(1, ComedyGold.img_timer[name] * 2)
love.graphics.setColor(1, 1, 1, a)
love.graphics.draw(ComedyGold.images[name], 0, 0, 0, xScale, yScale)
end
end
-- in jokers.lua (for a specific joker's calculate function):
if context.debuffed_hand or context.joker_main then
if G.GAME.blind.triggered then
G.E_MANAGER:add_event(Event({
func = function()
G.E_MANAGER:add_event(Event({
func = function()
G.GAME.blind:disable()
if not ComedyGold.images["hard_stare"] then
ComedyGold.images["hard_stare"] = Comedy_load_image("hard_stare")
end
ComedyGold.show_img["hard_stare"] = true
ComedyGold.img_timer["hard_stare"] = 0.5
play_sound('comedy_hard_stare')
delay(0.4)
return true
end
}))
SMODS.calculate_effect({ message = localize('ph_boss_disabled') }, card)
return true
end
}))
end
end
I got the sound to play, but the image doesn't show. How do I get the image to show?
-# I had the image loading stuff be put into a utility function in case I want to do something similar later
@silver flame
SMODS.add_card { set = "Playing Card", seal = random_seal, edition = random_edition, enhancement = 'm_lucky' }
: |
this should make the card you want
oh yeah and
local random_edition = poll_edition("modprefix_seed", nil, true, true)
local random_seal = SMODS.poll_seal {key = "modprefix_seed", guaranteed = true}
SMODS.add_card { set = "Playing Card", seal = random_seal, edition = random_edition, enhancement = 'm_lucky' }
use this for the card you want
how do i randomize where in a altas a spawned card draws from
@silver flame put the code i gave in your context and that should do it
oh yeah
also
is there a way to make jokers change their appearance after the right condition
i like making really stupid jokers and then i struggle to come up with ideas on what they should do
-2$ of interest per -5$ dollars at end of round
+5 chips per -1$
💀
i just realized i already added this
i made it so you can go into infinite debt, but every time you buy something it has a 1 in 6 chance to go extinct
Oops all 6's build 💀
that would make it die faster
yeah
yeah
Tony jawk
peam
I doubt this is possible, but I wonder how capable Lua is with image manipulation
I think it'd be extremely helpful if you could build a card just from a few key components, and in-game it'd assemble the card atlas
its probably possible
There's probably a library for it
Oh boy here we go
oh that'd be nice
I'd take a crack at it, but I'm way too inexperienced to evem research it
Bringing this up because for a mod like Sigil, any sort of optimization for making new cards would be wonderful
Now the biggest problem is trying to test this on a VM without nuking my pc
This code never gets called. Can anyone say what I'm doing wrong?
[[patches]]
[patches.pattern]
target = "functions/state_events.lua"
pattern = "local mod_percent = false"
position = "before"
match_indent = true
payload = '''
--[[
Used by The Altar.
--]]
if (G.GAME.blind.config.blind.key == 'bl_lapsems_altar') and not G.GAME.blind.disabled then
blind.triggered = true
print('Blind triggered!')
if (hand_chips < mult) then
local amount_to_transfer = math.abs(math.floor((hand_chips * 0.5) + 0.5))
hand_chips = mod_chips(hand_chips - amount_to_transfer)
mult = mod_mult(mult + amount_to_transfer)
else
local amount_to_transfer = math.abs(math.floor((mult * 0.5) + 0.5))
hand_chips = mod_chips(hand_chips + amount_to_transfer)
mult = mod_mult(mult - amount_to_transfer)
end
update_hand_text({ sound = 'chips2', modded = true }, { chips = hand_chips, mult = mult })
end
'''
I've tested that condition with DebugPlus' eval command and they both evaluate to true when I'm facing The Altar, but the code never gets called!
is the patch applying?
so if you go to Mods/lovely/dump/functions/state_events.lua do you see your injected code
💀
._.
It doesn't show up, but it's in a file with another patch which does appear. I presume that I did something faulty which led to both of them not appearing, so how may I clear that up and get two patches to coexist in one file?
I'm looking at another mod's patch files and I can't identify anything different in structure from my own code.
On further inspection, it looks like the desired code wasn't even in the actual target file. My bad!
there's some context that's basically on every single game action right?
i forget what it is
😭
What is considered a game action?
like literally any context
i've done it on accident before which is funny
No, there is not a context for every context, you would just not check for a context.
that's kinda what i thought at first but that seems to make it update every game "tick" which i didn't really want
it being this
calculate = function(self, card, context)
if pseudorandom("j_bfs_schro_cat") < 0.5 then
card.ability.extra.xmult = math.max(1, card.ability.extra.xmult - card.ability.extra.xmult_mod)
else
card.ability.extra.xmult = math.max(1, card.ability.extra.xmult + card.ability.extra.xmult_mod)
end
if context.joker_main then
return {
xmult = card.ability.extra.xmult
}
end
end
any idea? am i of the stupid?
You mean every frame?
Have you tried printing context?
Yes, what does it print?
There is probably a modded context that is being called every frame.
oh it's hpot_destroyed
so just if not context.hpot_destroyed i assume
or at least for the time being
oh my god ignore all this it's not hotpot
i'm just stupid and forgot about this context someone else in my team made from earlier
local l_update = Card.update
function Card:update(dt)
local ret=l_update(self,dt)
SMODS.calculate_context{
balatrofusion_update=true,
updated_card=self,
balatrofusion_dt=dt,
}
return ret
end
😭
What is this?
This deck is suposed to revive the player once per run, but it crashes the game if you try to pick it
Hi everyone, is there a pool for all Consumables thats easy to acces?
G.P_CENTER_POOLS.Consumeables
calculate = function(self, back, context)
if context.game_over and not G.GAME.modprefix_saved then
G.GAME.modprefix_saved = true
return {saved = true}
end
end
Iam trying to make emperor/high priestress but for all consumeables, i took a look at Vanilla remade but they seem to have predefined planets/tarots, can i just change those values to consumeable?
SMODS.add_card({set = 'Consumeables', area = G.consumeables})
thx!!
is there a way to make it shake while its spawning the tarots
Yes, it is shaking.
no like
while is standing there
it shakes while its spawns
but i want it to shake if its just standing there
how would I make a deck that works like astronomer but for a kind of joker and booster from my mod?
No, you would change it in your localization file.
i still havent figured it out since yesterday 🥀
I really like how it result is now.
for k, v in pairs(G.GAME.BossList or {}) do
local center, effects = G.P_BLINDS[v], {}
if context.press_play and center.press_play then center:press_play() end
if context.debuff_card then local debuff if center.recalc_debuff then debuff = center:recalc_debuff(context.debuff_card, true) elseif center.debuff_card then debuff = center:debuff_card(context.debuff_card, true) end table.insert(effects, {debuff = debuff}) end
if context.setting_blind and center.set_blind then center:set_blind() end
if context.debuff_hand and center.debuff_hand then table.insert(effects, {debuff = center:debuff_hand(context.full_hand, context.poker_hands, context.scoring_name, context.check)}) end
if context.stay_flipped and center.stay_flipped then table.insert(effects, {stay_flipped = center:stay_flipped(context.to_area, context.other_card)}) end
if center.calculate then local effect = center:calculate({effect = center.config}, context) if effect then table.insert(effects, effect) end end
end
unfortunately it still does the same thing
Move it out of the event.
alright
uh oh
why is it spelted consumeables not consumables😭
how would I make a deck that works like astronomer but for a kind of joker and booster from my mod?
Hook Card:set_cost() and check for G.GAME.selected_back.effect.center.key matching your deck.
would it work better for me if hooked onto the blind disable thing and just made it do nothing if its on my challenge deck or will that not work
...I mean, you could.
No, that would not work.
¯_(ツ)_/¯
thx
Blinds are not disabled when defeated also they do not keep calculating.
well then what do i gotta do then
wait i just now noticed you editted it
hold on
lemme try now
well now it doesn't crash and doesn't lag but it also just doesn't persist
Yes, it doesn't work for vanilla blinds.
oh, and to make it work i gotta?
how would make a boss debuff every card except face cards?
Use the recalc_debuff function of SMODS.Blind.
How do I lovelypatch a card area?
this'll work right? I'm just trying to make a re-usable function because i seem to use this a lot.
local indices = {}
local function tbl_contains(tbl, itm)
for k,v in ipairs(tbl) do
if itm == v then
return true
end
end
return false
end
local function pick_unique(seed, pool, taken)
local out
repeat
out = pool[pseudorandom(seed, 1, #pool)]
until not tbl_contains(taken, out)
return out
end
local function range(start, finish, stop)
step = step or 1
local t = {}
for i = start, finish, step do
table.insert(t, i)
end
return t
end
if not set then
for i=1, n do
local c = 0
local function choose()
c = pseudorandom(seed, 1, #area)
if tbl_contains(indices, c) then
c = pick_unique(seed, range(#area), indices)
end
end
choose()
table.insert(indices, c)
end
else
local valid_indices = {}
for k, v in ipairs(area) do
if v.ability.set == set then
table.insert(valid_indices, k)
end
end
for i=1, n do
local pick = 0
local function choose()
pick = valid_indices[pseudorandom(seed, 1, #valid_indices)]
if tbl_contains(indices, pick) then
pick = pick_unique(seed, valid_indices, indices)
end
end
choose()
table.insert(indices, pick)
end
end
return indices