#💻・modding-dev

1 messages · Page 681 of 1

vale grove
#

oh yeah than that would work

gilded blaze
vale grove
#

might just do that for the idea

#

because this joker is supposed to have a degenerate gambling idea behind it

#

so maybe having to spend your rerolls is more fun

#

or rather accurate to the idealogy

gilded blaze
#

now, about the interest part

#

how do you plan to undo it via remove_from_deck

vale grove
#

G.GAME.interest_amount = 1?

gilded blaze
#

To The Moon adds 1 on obtaining, and subtract 1 on removing

#

hard-setting the value will interrupt the process

vale grove
#

seems fine to me

gilded blaze
#

interrupting doesn't mean not working

vale grove
#

okay but then theres no issue right?

gilded blaze
#

let's say you obtain this joker, then To The Moon

#

To The Moon recovers interest by adding 1 to 0

#

you then remove this joker

#

hard-setting the value to 1 nullifies To The Moon

vale grove
#

it doesnt

#

gave the intended value

#

the only issue that i might see is that on green deck you now gain interest all of a sudden

#

okay just tried that

#

that just works properly

#

life when your code works as inteded

frosty rampart
#

that said i would still recommend not hard setting the interest amount in case any other modded content interacts with it

daring fern
vale grove
#

funnily enough to the moon gives interest again with this joker(if you have both not when sold)

#

but

#

thats a feature now

gilded blaze
#

to prevent ambiguity

vale grove
#
        if context.main_scoring and context.cardarea == G.play then
            assert(SMODS.modify_rank(1))
        end

again probably something very small,. this is a seal that is supposed to increase the rank of the card by 1 when played however currently i get this error

300: attempt to index local 'card' (a number value)
#

what does context.other_card do?

#

this also sadly still crashes the game with the same error

frosty rampart
#

it should actually just be SMODS.modify_rank(card, 1)
you need to pass a card to the function for it to work, and card in the calculate function of a seal is the card that it's on

#

context.other_card refers to a card object in some contexts, but it doesn't exist during context.main_scoring

vale grove
#

oh yeah thanks

rare torrent
#

is it possible to get your current balatro playtime and use that value in a joker?

left sonnet
#

Hey folks, I've got a global table with data, but the data itself seems to restore to its default values in between runs. Is there a way to save this data so it doesn't reset while still being able to reference it as the original table? Would save a bit of work

slim ferry
#

For saving data to a profile rather than a run you should store it in G.PROFILES[G.SETTINGS.profile]

#

G.GAME is for storing data to runs

#

If its just a global table with nothing needing to be saved it can just directly be stored as a global rather than in another table

left sonnet
#

Alright, thank you!

silent sail
#

how would i go about making an edition that retriggers a joker

#

also how do i go about cchanging joker values with other jokers or editions

slim ferry
#

Spectrallib provides that

#

Or you can just do it with your own copied function but when it eventually breaks and you have to figure it out yourself thats probably not going to go well

rapid stag
#

it's probably bad practice, but if you want to save mod-specific info to the profile, what i do in my mod is i just save it to a profile-index-indexed table in my mod config girldmDizzy

rapid stag
#

that's why i qualify with mod-specific info, so the profile isn't bricked if the player decides to uninstall the mod. bad practice, but the profile stays safe

silent sail
rapid stag
#

...am i being too paranoid about that

#

i have like a whole table full of stuff

daring fern
slim ferry
#

Which was probably happening anyway if you were saving mod data

rapid stag
#

i guess

rapid stag
#

is there a func i should call when i change data in G.PROFILES[G.SETTINGS.profile] or does it just auto save whenever it's changed? which i'm pretty sure is not the case

silent sail
#

ive managed to get it to edit the description of the joker but instead of aapplying that to teh joker it just gives 1.5 xmult

left sonnet
#

I'm back gang, I'm wondering what I'm doing wrong here? METER_DATA seems to not be saving

local start_run_hook = Game.start_run
function Game:start_run(args)
    start_run_hook(self, args)
    G.GAME.METER_DATA = {}
    G.GAME.METER_DATA.meter = 0
    G.GAME.METER_DATA.meter_active = true
end```
silent sail
# daring fern Code?

SMODS.Shader({ key = 'gold', path = 'gold.fs' })

SMODS.Edition {
key = 'golden',
shader = 'gold',

config = {
    extra = {
        modifytbl = {
            ["*"] = 1.5
        }
    }
},

loc_txt = {
    name = 'Golden',
    text = {
        [1] = 'This Joker has its values',
        [2] = 'increased by {C:attention}50%{}'
    }
},

calculate = function(self, card, context)
    if context.pre_joker then

        card.ability.extra = card.ability.extra or {}

        if not card.ability.extra.golden_applied then
            BAGGUTRO.modify_joker_values(card, self.config.extra.modifytbl)
            card.ability.extra.golden_applied = true
        end
    end
end

}

daring fern
silent sail
daring fern
frosty dock
#

chat i cooked up something

silent sail
#

i got it

#

YAY

#

ok new problem is that i may need to redo all my jokers to not be static values because those dont scale but vanilla ones do

near coral
#

I have a sticker that makes it to where if one is sold or destroyed all others with that sticker are too, but it hasn't been working

daring fern
# near coral I have a sticker that makes it to where if one is sold or destroyed all others w...
local oldcardremove = Card.remove
function Card:remove()
    local g = oldcardremove(self)
    local destroyed_cards = {}
    if self.ability.modprefix_stickerkey then
        for k, v in pairs(G.jokers.cards) do
            if v ~= self and v.ability.modprefix_stickerkey then
                if G.CONTROLLER.locks.selling_card then
                    G.FUNCS.sell_card({config = {ref_table = v}})
                else
                    table.insert(destroyed_cards, v)
                end
            end
        end
    end
    if next(destroyed_cards) then
        SMODS.destroy_cards(destroyed_cards)
    end
    return g
end
near coral
#

o oke

daring fern
near coral
left sonnet
#
local igo_hook = Game.init_game_object
function Game:init_game_object()
  local g = igo_hook(self)
    g.METER_DATA = {
        meter = 0,
        depth = 0,
        meter_active = true,
    }
  return g
end``` I realize now g is G.GAME
naive agate
#

how do I have a joker increase the hand size after two cards of a specific enhancement are scored'

daring fern
# naive agate how do I have a joker increase the hand size after two cards of a specific enhan...
if context.individual and context.cardarea == G.play then
    local cards = {}
    for k, v in pairs(context.scoring_hand) do
        if SMODS.has_enhancement(v, 'm_modprefix_key') or SMODS.has_enhancement(v, 'm_modprefix_key2') then
            table.insert(cards, v)
        end
        if v == context.other_card then break end
    end
    if #cards == 2 then
        card.ability.extra.hand_size = card.ability.extra.hand_size+1
        G.hand:change_size(1)
        return {message = localize('k_upgrade_ex'), message_card = card}
    end
end
naive agate
#

cheers

fading rivet
#

any idea how to fix this?
It worked if I use draw_to_hand, so I tried copying the code the moves the card and it does this instead

buoyant shard
#

Whenever the following code triggers, my game crashes

    if pseudorandom('charming') < G.GAME.probabilities.normal / card.ability.extra.odds then
        -- Thanks to nh6574 and bepisfever in the Balatro Discord for the help with this next line!
        SMODS.add_card{ set = "Joker", edition = "e_negative", key = "charming"}
        return {
            card = card,
            message = "Wow!",
        }
    end
end```
sturdy compass
#

your key seems very short

#

no 'j_' and no mod prefix

fading rivet
buoyant shard
rapid stag
#

i have a bit of an issue here - i have a joker that adds +1 mult per enhancement/edition/seal to all played cards, but for some reason the red seal on unscored cards is retriggering the effect.

how do i prevent that?

sturdy compass
#

instead of context.individual try using context.main_scoring instead. You should replace mentions of context.other_card with just card too iirc nvm I thought this was for an enhancement

fading rivet
#

here's code for a card I have witha similar effect

-- Forte
SMODS.Consumable{
    key = "vb_forte",
    set = "Voicebank",
    atlas = "voicebanks",
    pos = {x=0,y=4},
    calculate = function(self, card, context)
        if card.ability.immutable._active then
            if context.before then
                for _, _card in ipairs(context.full_hand) do
                    if not SMODS.in_scoring(_card, context.scoring_hand) then
                        SMODS.scale_card(card, {
                            ref_table = _card.ability,
                            ref_value = "perma_bonus",
                            scalar_table = {_card:get_id()},
                            scalar_value = 1
                        })
                    end
                end
            end
        end
    end
}

ignore the _active thing thats not important

daring fern
drifting garden
#

how do i use the initial_deck parameter? i want to create a deck of only Kings of Hearts, but i can't seem to get it to work properly

#

also im using apply with an event to add steel and red seals to every card, which creates a massive delay when starting a run. is there a workaround for that?

rapid stag
rapid stag
drifting garden
drifting garden
rapid stag
#

and before adding them, you can sneak in code that will add the red seal you want to the one king that you start with

drifting garden
#
       G.E_MANAGER:add_event(Event({
            func = function()
                for _, c in ipairs(G.playing_cards or {}) do
                    if c.set_ability then c:set_ability("m_steel") end
                    if c.set_seal then c:set_seal("Red") end
                end
                return true
            end
        }))
    end,
#

this is what i was doing originally

#

i removed the parameters i had before bc i only have one card at this point

drifting garden
#

mb

#

inside?

rapid stag
# drifting garden inside?

try:

apply = function(self, back)
                G.E_MANAGER:add_event(Event({
                    trigger = 'immediate',
                    blocking = false,
                    blockable = false,
                    func = function()
                        if G.deck then
                            G.deck.cards[1]:set_seal('Red', nil, true)
                            
                            for _ = 1, 51 do
                              SMODS.add_card{
                                area = G.deck,
                                set = 'Base',
                                rank = 'King',
                                suit = 'Hearts',
                                seal = 'Red',
                                no_edition = true
                              }
                            end

                            return true
                        end
                        
                        return false
                    end
                }))
            end,
drifting garden
#

if i add steel to them under the set_seal, do i need to also make them steel in the add_card?

#

also it still has a 2sec ish delay at the start while it populates the deck

rapid stag
#

i'm assuming you want all of them to be steel in that case, so you'd want to add enhancement = 'm_steel' to add_card

#

weird, i recently wrote a deck that does something similar with those exact event params and it's instant for me

drifting garden
#

also am i adding steel = m_steel to the add_card? or do i need a G.deck.cards[1]:set_ability or something

rapid stag
#

omg i'm dumb

drifting garden
#

well you're smarter than i am

#

i've been banging my head at the wall for like 3 hours trying to make a single deck

#

of one card

rapid stag
#

yeah, G.deck.cards[1]:set_ability('m_steel') below that first seal set will give you the steel on the first card

drifting garden
#

surely there's a better way to do decks than to manually create 51 extra copies of the same card :(

#

also why do i always forget the damn comma

#

well this functions, now i just need to figure out why its taking so long, and if i can make it all happen at once

#

ty

rapid stag
#

apparently there may be changes to SMODS.Deck at some point to allow you to more finely adjust the deck, but atm this is how you do it. that's the best way atm

drifting garden
#

i mean i guess ive never played with a modded deck that is like this, but i swear they always load instantly

#

even CCD in cryptid which makes them all consumables doesnt take this long i swear

#

but im sure its a lot more thought out in general

rapid stag
#

does anyone know if it's possible to mess with the cards in played hand during context.press_play?

frosty rampart
#

they're not actually in the played hand yet, so they're still in G.hand.highlighted

rapid stag
#

ahh. i figured out a better solution to my problem, anyway

umbral zodiac
#

if you highlight cards in context.press_play i think you can add them to the played hand forcefully its really funny

frosty rampart
drifting garden
drifting garden
# rapid stag ...huh?

they have the correct sprite in the deck, but in my hand they are just blank steel cards with red seals

frosty rampart
#

smods issue, will be fixed in the next update

rapid stag
#

unfortunately, sprite issues are beyond me 😭

frosty rampart
drifting garden
#

😭

#

well hey at least it works

idle plaza
drifting garden
rapid stag
#

how do i do thousands separator in string.format?

idle plaza
# drifting garden unfortunately not, it also makes a sound everytime it's doing whatever it is doi...

Alternate idea then, let the deck create the typical 52 card deck as a basis, then in your event, loop through each card in G.playing_cards and:

SMODS.change_base(card, 'Hearts', 'King')
card:set_ability('m_steel')
card:set_seal('Red', true, true)

Notably, SMODS.add_card calls card:set_seal without the added true, true that prevents the animation and sound of adding a seal to a card. This method should let you skip that.

drifting garden
#

that worked, but now i dont draw cards to my hand

idle plaza
#

what

drifting garden
#

maybe i did something wrong

idle plaza
#

What's your code for the deck?

drifting garden
#
apply = function(self, back)
        G.E_MANAGER:add_event(Event({
            trigger = 'immediate',
            blocking = false,
            blockable = false,
            skip_materialize = true,
            func = function()
            for _, card in ipairs(G.playing_cards) do
                SMODS.change_base(card, 'Hearts', 'King')
                card:set_ability('m_steel')
                card:set_seal('Red', true, true)
            end
        end
        }))
    end,
#

this is my apply function

daring fern
drifting garden
#

after the for loop?

idle plaza
#

Yeah, immediately after the loop.

Also you can remove the skip_materialize = true,

drifting garden
#

beautiful

#

thank you

#

and now they have sprites as well

vital wren
#

can i set the sprite of an enhancement dynamically so all cards with that enhancement get it instead of changing the sprites per card?

vital wren
#

dang

frosty rampart
#

it's "possible" with a custom drawstep rendering the sprite instead of the enhancement itself, but that's certainly not simple

gilded blaze
#

above center but below front
yep, possible but not simple

vale grove
#

im reading up on how to make certain consumables appear in the shop

        G.GAME.spectral_rate = self.config.spectral_rate(this value is 2)

this is how ghost deck does it but how would you do it for a custom consumable type you added?

teal grotto
#

i guess search for spectral_rate

daring fern
teal grotto
#

you can just do that?

vale grove
daring fern
vale grove
#

oh hype

vital wren
#

is there a way to change the sprite of a joker's center in such a way that it affects all of them instead of setting the sprite for each individual copy of that joker?

daring fern
vital wren
#

understood. thanks

drowsy heath
#

is there a way to check for specific jokers to beat a blind

#

like say i wanted it to select from an pool (objectType) that has to be in hand or the blind is not beatable

fading rivet
#

nvm got it

#

had tow rap in an event for some reason

vital wren
#

can i just shove a bunch of code in a joker's context return?

frosty dock
vital wren
#

oh right. the returned func value gets executed at the appropriate context return timing, iirc

frosty dock
#

func gets run after other effects, pre_func prior. if you need more control use extra tables

silent sail
#

my sprite sheet is in order, my jokers are using correct sprites, but they arent appearing inn theh right order in the collection

red flower
#

they appear in loading order

#

so the order they are in the code

silent sail
#

i havnt changed any of that is what confuses me

mystic river
#

shot in the dark, are you iterating over a table with pairs() as part of the load process?

silent sail
#

how would one check this

mystic river
#

well uh
did you on purpose type something like for k,v in pairs(files)

red flower
#

i do type that accidentally some times

red flower
silent sail
#

turns out the load order changed itself

#

i didnt touch it yet somehow it had been altered

#

anyway new problem

#

pretty sure its because sell value doesnt exist yet but i dont know how to go around that

red flower
#

what is the code

silent sail
red flower
# silent sail

if g.jokers doesnt exist that function will return nil

mystic river
#
card.ability.extra.thisjokersellvalue + ((function() for _, joker in ipairs(G.jokers and (G.jokers and G.jokers.cards or {}) or {}) do if joker == card then return joker.sell_cost end end end)()) * 2

why do you do this instead of just using card.sell_cost?

red flower
#

also yeah lol

#

it is also not good practice to in line functions like this it makes it less readable

silent sail
#

alas joker forge as a base

red flower
#

makes sense

mystic river
#

it will definitely crash if the card isn't in G.jokers for some reason, such as the collection

red flower
#

dont edit jokerforge code, either use it or code it from scratch imo

silent sail
#

yeah fair enough

#

so how would one do this effect then

silent sail
#

oh right yeah thanks

#

game stopped crashing but now it doesnt actually modify sell fvalue

red flower
#

maybe start again by copying Egg but setting the value to 1 is harder

#

you would need to hook set_cost

whole lava
#

can someone help me how to fix "engine/ui.lua:698: attempt to index field 'colour' (a nil value)"?

eager nimbus
#

to any future travelers looking up how to change the "saved by mr. bones" text for a death saving thing, just change saved = true, to saved = "string"

frosty dock
#

that's in the wiki, isn't it?

dreamy thunder
eager nimbus
#

and a few others

frosty dock
red flower
eager nimbus
#

oh i figured it out

primal robin
vale grove
#

how do you increase boosterpack slots?

slim ferry
#

SMODS.change_booster_limit(amount)

#

I think

#

Its something like that

#

Autocomplete should get it after the first bit

gilded blaze
#

autocomplete isn't available for every mod dev

red flower
#

the documentation is

whole lava
#

Oops! The game crashed:
engine/ui.lua:729: attempt to index field 'colour' (a nil value)
I keep having these crash i don't know how to handle it

slim ferry
#

post the full crash log

#

Also probably just update steamodded

final jewel
#

how do I add a Credit tag under the badge of a card

#

I dont remember which mod add that

median veldt
#

Is there a function or something that gets called when a booster pack is over?

median veldt
final jewel
#

3x credit is an infoqueue ?

median veldt
#

No, you just add this to your object at the end

credit = {
    art = "Artist Name",
    code = "Coder Name",
    concept = "Concepter Name"
},
median veldt
dreamy thunder
median veldt
#

mmmmmmmmthat wont do

#

i cant use contexts

dreamy thunder
#

G.FUNCS.end_consumeable = function(e, delayfac)

median veldt
#

end_consumeable?

#

does that apply to. like. using a consumable

dreamy thunder
#

what are you trying to do

median veldt
#

hey please don't dump your crash logs like this

whole lava
#

Ok sr

dreamy thunder
median veldt
#

oh yeah true

#

i forgot patching existed

#

i just suck at it

whole lava
#

Maybe i'll ask later:))

dreamy thunder
median veldt
#

does G.GAME.modifiers.booster_size_mod exist? i got an error that it was nil

#

or is it nil until its set to something

red flower
#

i think it's nil until it is set

#

but i dont have the code with me rn

median veldt
#

1 sec

idle plaza
median veldt
#

i see

#

okay so. i am now just realizing that setting this variable does NOT work while still inside a booster pack

#

is there a way to make it work?

#

like draw an extra card?

red flower
#

probably emplace it into G.pack_cards?

median veldt
idle plaza
#

Yeah

median veldt
#

hm alright

red flower
#

all boosters should have a G.P_CENTERS.key.create_card

#

even the vanilla ones

median veldt
#

oh?

#

does that take any params

#

actually i can just check

mystic river
#

the params are self (prototype), card (booster object), i (which card in the pack it is) and it returns either a Card object or a table that can be passed into SMODS.create_card

median veldt
#

mm

#

okay so well new development i didny need to do any of that, there was miscommunication

#

what's the variable that tells how many choices you have left (e.g. 2 in a Mega pack)

#

im guessing G.booster_pack.something

mystic river
#

G.GAME.pack_choices

median veldt
#

ah. thanks

#

is that permanent or just for the current pack

queen crescent
median veldt
queen crescent
#

new suit coming to 0.0.3a

#

when i finish the code im gonna do the crossmod i need to do (for example pwx with uno cards)

median veldt
drifting garden
#

how do you properly replace blinds in a custom deck?

idle plaza
drifting garden
#

this is what I'm trying, there's something I'm not doing properly:

  calculate = function(self, context)
    if context.setting_blind and context.blind and context.blind.boss then
      if G.GAME.round_resets and (G.GAME.round_resets.ante % 8 == 0) then
        G.GAME.round_resets.blind = 'bl_plant'
      end
    end
  end,
median veldt
queen crescent
idle plaza
median veldt
queen crescent
#

a whole brand new suit

median veldt
#

no i mean the thing youre using to show it

queen crescent
#

pwx

median veldt
#

?

queen crescent
#

polterworx

mystic river
#

for that matter - do you want the win ante, or every ante that gives a showdown blind?

drifting garden
mystic river
#

it does not, it's only the one that wins you the game

#

and might not even be that if mods mess around with things

idle plaza
drifting garden
#

so if i use G.GAME.round_resets.ante % 8 == 0 it will do all showdowns?

mystic river
#

it'll do every 8th ante

frosty rampart
#

for compatibility with other mods that change the win ante, it should be G.GAME.round_resets.ante % G.GAME.win_ante == 0

mystic river
frosty rampart
#

and yea there's a number of mods out there that add e.g. a stake that makes showdowns appear twice as often

mystic river
#

if you want every showdown, put your hook code after the original function and return the plant if the original result would've been a showdown blind

#

probably edit used blinds in the process to avoid side effects

drifting garden
# mystic river probably edit used blinds in the process to avoid side effects

im doing this rn, how can i properly edit the used blinds in this function?

local get_old_boss = get_new_boss or function() return "bl_big" end

function get_new_boss()
    if G.GAME 
    and G.GAME.selected_back 
    and G.GAME.selected_back.effect 
    and G.GAME.selected_back.effect.center 
    and G.GAME.selected_back.effect.center.key == "b_steeldeck_all_kh"
    and G.GAME.round_resets 
    and (G.GAME.round_resets.ante % G.GAME.win_ante == 0) then
        return "bl_plant"
    else
        return get_old_boss()
    end
end```
mystic river
#

in that function, you don't

idle plaza
fading rivet
#

How could I change what cards you start with in a deck
I can't use challenge decks

red flower
#

apply

frosty rampart
#

i've seen what you're working on, i think the best route for you is to use initial_deck to start with a totally empty deck and then the apply function to add all the cards

fading rivet
red flower
fading rivet
#

can I just supply nothing and it'll be nothign

#

initial_deck = {}

#

or do I need at least 1 card

red flower
#

you need to set it to exclude them i think

fading rivet
#

tried this and it gave me the full deck

rapid stag
#

try initial_deck = { Suits = { 'D', 'H', 'C', 'S' }, Ranks = { 'A', 'K', 'Q', 'J', '10', '9', '8', '7', '6', '5', '4', '3', '2' }, exclude = true }

fading rivet
#

ranks worked

#

initial_deck = {Ranks = {}} worked

#

thx

rapid stag
#

i would love more granular control over the starting deck girldmDead when are the changes

frosty rampart
#

i mean
you have it
initial_deck gives you control over the ranks and suits in the deck, and the apply function lets you do basically everything else to that deck

red flower
#

maybe a remove initial deck flag would be cool

#

so you dont have to specify all the suits and ranks which might not be loaded yet

red flower
red flower
#

also just use add_card it does all the stuff at the bottom

fading rivet
#

ah

fading rivet
#

wait I didn't provide a return

rigid solar
#

So I have a deck in my mod. For me, it appears last in the list (as intended) but someone's reporting it's appearing 2nd, right after red deck. Would anyone happen to know why?

#

I assume it's a difference in either steamodded or lovely version, or both?

median veldt
#

How do I get the last played poker hand?

median veldt
#

Is there a way to get the hands played from this round?

frosty rampart
#

not specifically in smods, i'd probably use the mod calculate to save details about played hands to a table and then wipe it clean during end_of_round

median veldt
#

Mod calculate?

faint yacht
#
function SMODS.current_mod.calculate(self, context)
  ...
end
median veldt
#

That's a thing??

frosty rampart
#

since 0827

median veldt
#

🥹

frosty rampart
#

it's very handy

median veldt
#

Well, thanks!

silent sail
#

how do i actualyl code in a joker retrigger for like an edition

#

like it retriggers teh card or joker thats used

#

ive got the optional feature turned on

#

i just dont know how it works

median veldt
#

Question - can you add weights to consumables? Is that a thing?

rapid stag
#

it'll be easier when eremel's new PR is merged

#

idk how you'd approach that atm though

median veldt
#

Maybe I'll just wait?

silent sail
median veldt
#

(This is for something else; effectively rerolling a booster pack)

red flower
#

g.booster_pack is a uibox i think

median veldt
#

I have now realized that G.B- yeah

red flower
#

where is this code

median veldt
#

If you're going to suggest a context, that's not applicable here.

#

Though, I could throw something together and create a variable that checks the key.

red flower
#

i mean if i suggest a context you could always use mod calculate

#

but i wasnt going to

median veldt
#

My point exactly.

#

Oh?

#

What were you going to say?

red flower
#

checking something in the code give me a sec

#
local _card_to_spawn = SMODS.OPENED_BOOSTER.config.center:create_card(SMODS.OPENED_BOOSTER, #G.pack_cards)
local card
if type((_card_to_spawn or {}).is) == 'function' and _card_to_spawn:is(Card) then
    card = _card_to_spawn
else
    card = SMODS.create_card(_card_to_spawn)
end
if card then G.pack_cards:emplace(card) end
#

maybe that works?

median veldt
#

Okay hold on.

#

This only spawns one card, correct?

red flower
#

yes

median veldt
#

What if I want to create two? Just, put this in a for loop?

#

Here actually before I test that

#

Let me just try this as is

red flower
#

yeah it should work

#

the loop i mean, idk if it actually works properly in the first place

median veldt
#

Now let me try it in a loop.

red flower
#

but i dont think it matters that much

median veldt
# red flower yeah it should work
local num = #G.pack_cards
SMODS.destroy_cards(G.pack_cards.cards)
for i=1,num do
    local _card_to_spawn = SMODS.OPENED_BOOSTER.config.center:create_card(SMODS.OPENED_BOOSTER, #G.pack_cards+1)
    local card
    if type((_card_to_spawn or {}).is) == 'function' and _card_to_spawn:is(Card) then
        card = _card_to_spawn
    else
        card = SMODS.create_card(_card_to_spawn)
    end
    if card then G.pack_cards:emplace(card) end
end

Didn't work. User error?

#

It worked before I added the for loop, for the record.

#

Yeah, it's my fault (added a print statement and it didn't print anything), but I'm not sure what I did wrong

wintry solar
#

local num = #G.pack_cards.cards

median veldt
#

Aw.

#

I always forget that.

red flower
final jewel
#

what is the key of the stakes sticker

#

those in the vanilla game

frosty rampart
#

it's handled differently from regular stickers
card.sticker is set to just the key of the stake without a "stake" prefix, e.g. white or modprefix_key for modded stakes

final jewel
#

like im trying to take ownership on them

frosty dock
final jewel
#

oh so I cant edit the texture ?

frosty dock
#

no you absolutely can do that, the relevant properties are on the stake

#

sticker_atlas and sticker_pos

#

there just don't exist sticker objects for these because they don't act like stickers, they're just sticker sprites

final jewel
#

create_badge(localize(v, "labels"), get_badge_colour(v), SMODS.get_badge_text_colour(v))

is it possible to make an invisible background color for a badge

red flower
#

G.C.CLEAR instead of get_badge_colour?

final jewel
#
[manifest]
version = '1.0.1'
dump_lua = true
priority = 0

[[patches]]
[patches.pattern]
target = 'functions/UI_definitions.lua'
match_indent = true
position = 'before'
pattern = '''
if AUT.card_type ~= 'Locked' and AUT.card_type ~= 'Undiscovered' then
'''
payload = '''
if obj and obj.art_credit then
    badges[#badges + 1] = create_badge(localize('taw_credit', "labels") + obj.art_credit, get_badge_colour(G.C.CLEAR), SMODS.get_badge_text_colour(G.C.GREY))
end

'''```
#

I probably did something wrong but idk

frosty rampart
#

replace the whole get_badge_colour function call with just G.C.CLEAR

final jewel
#

ok

true jasper
#

how do i change the background color to a custom color when in a booster i made

final jewel
#

wait i think i know

#

ok it works

#

but how do I make the text smaller

median veldt
#

Before I do this manually, I just want a quick sanity check.

Is there a variable that keeps track of the last used consumable?

frosty rampart
#

not in general, only one for consumables that The Fool can create

median veldt
#

Alright, I'll just do it manually.

wanton jolt
#

i want to change the animated sprite of a joker, i managed to do it for soul but not the card sprite itself

#

nvm

median veldt
#

Sanity check. Am I doing this right?

red flower
#

SMODS.create_card({ key = [the key in that table] })

#

you can also add edition = key in create_card

#

or use add_card instead if you want it to be emplaced in consumables automatically

median veldt
#

Oh!

rapid stag
#

sanity check: self.ability.rarity? or self.rarity? or self.config.rarity? or self.config.center.rarity? on Card, in highlight()

red flower
near coral
#

Is there a way to select more than 1 joker for the sake of a consumable

rapid stag
#

modify area highlight limit

#

i forget how, n will probably say

red flower
near coral
#

Maybe I should change the consumable effects

rapid stag
red flower
#

what if the other mod changes the value in between those

rapid stag
#

hmm

near coral
#

The middle one switches between the vanilla version and its counterpart in my mod, meanwhile the ones on the sides are to switch 2 vanilla jokers into the counterparts and vice versa

red flower
#

or what if another mod requires the highlighted limit to be 99 or 0 or something

frosty rampart
#

usually if you want to have a consumable affect two jokers, it's in the form of "select this joker and affect the joker to the right of it"

rapid stag
#

weren't there helper functions to adjust highlight limits?

red flower
#

i dont think so

#

i personally just made an overlay menu with a selection :3

near coral
#

Maybe I have them only be used up after 2 uses

near coral
rapid stag
#
if G.jokers.config.highlighted_limit < 2 then
  G.jokers.config.highlighted_limit = G.jokers.config.highlighted_limit + 1
end
```?
frosty rampart
near coral
#

Where is the link again

frosty rampart
near coral
#

Do seals come with only one badge or are they able to have many like other stuff

#

I'm asking because my coder wasn't able to put the mod badge in the sticker counterparts

#

And I wanna know if it's the same for seals

rapid stag
#

you can absolutely put mod badges on stickers

#

and seals

whole lava
idle plaza
# whole lava how can i fix this?

Your SMODS.calculate_context hook is set up incorrectly.

The full function is SMODS.calculate_context(context, return_table, no_resolve) and when you catch its return, you should use local ret = smods_calc_ref(context, return_table, no_resolve) or return_table

daring fern
whole lava
#

ok thanks:))

#

am i stupid or it's keeping crashing with the same error?

gilded blaze
#

what are you trying to do

#

permanent chips/mult/etc. on playing cards?

whole lava
#

I want to make skill tree that give you more chips and mult

urban wasp
#

i'd like to make a deck that destroys enhanced cards when discarded, but decks don't naturally have a way to check for discarded cards i don't believe
i tried making a hook, but it still doesn't work; any help?

-- soapy deck effect
local original_discard_cards = G.FUNCS.discard_cards_from_highlighted
G.FUNCS.discard_cards_from_highlighted = function(e, hook)
    local result = original_discard_cards(e, hook)
    if G.GAME.back and G.GAME.back.key == "b_bof_soapy" and not hook then
        for i = #G.discard.cards, 1, -1 do
            local card = G.discard.cards[i]
            if next(SMODS.get_enhancements(card)) ~= nil then
                if SMODS.shatters(card) then
                    card:shatter()
                else
                    card:start_dissolve()
                end
                table.remove(G.discard.cards, i)
            end
        end
    end
    return result
end
daring fern
gilded blaze
urban wasp
daring fern
urban wasp
#

there's no way it's that simple

#

oh my god it really is that simple

urban wasp
#

why doesn't this work how i would like it to (win on red deck with blue stake or win on blue deck with red stake)

check_for_unlock = function(self, args)
    if args.type == 'win_deck' and args.deck then
        local deck_key = args.deck.key
        local stake_level = args.stake_level or 1
        
        return (deck_key == 'b_blue' and stake_level == 2) or (deck_key == 'b_red' and stake_level == 5)
    end
    return false
end
#

additionally, why doesn't this colour appear in-game?

G.C.plasma = { 0.8, 0.45, 0.85, 1 }
...
                    "hands or discards by",
                    "{C:plasma}+#1#{} for the next {C:attention}Ante"
                },
...
gilded blaze
wary moth
#

Is there a way to flip cards drawn?
I was trying to search messages for context for on card drawn, but, doesn't look like that exists
But, I know card:flip() is a thing, and I know one of the boss blinds in the game flips card on drawn
Is there any way to do this on a joker?

daring fern
red flower
pseudo furnace
#

Can you juice the back?

daring fern
pseudo furnace
gilded blaze
#

smods handles this automatically for individual objects
juice the topmost card in G.deck, or G.deck itself in case it's empty

pseudo furnace
#

Whoaa G.deck works

next timber
#

is there a way for a voucher/joker/whatevr to make all jokers within an ObjectType be more likely to appear?

gilded blaze
#

we need whatever stored inside a SMODS.ObjectType object to be copied over to G.GAME, plus various changes to shift the focus there in order for it to work

#

that's just the tip of the iceberg tho 😭

red flower
#

i think it will be easy with the new weight stuff eremel is working on

gilded blaze
#

I see

next timber
#

ok well i guess uhh can i make a single joker more common and just manually do that for each joker

gilded blaze
#

you can, by modifying the return values of get_current_pool, but the work would be tedious

#

and it breaks seeding as well, due to different pool size after modification

fading rivet
#

How can I convert a rank id into its key?

#

preferably without iterating though anything

red flower
#

SMODS.Ranks[key].id

fading rivet
coral flume
#

Could I get a hand with this edition?

    calculate = function(self, card, context)
        if context.post_joker or (context.main_scoring and context.cardarea == G.play) then
            G.GAME.blind.chips = math.floor(G.GAME.blind.chips - G.GAME.blind.chips * 0.05)
            G.GAME.blind.chip_text = number_format(G.GAME.blind.chips)
            return {
              message = 'Depleted!',
              colour = G.C.MULT,
              card = card
            }
            
        end
    end

Function-wise its working but visuals are a bit odd, the score updates all at once at the start instead of score by score. I tried using context.individual but that seemed to break it.

fading rivet
#

I have the id, but I need the key

red flower
fading rivet
#

rip

#

slowish method it is

red flower
#

also why do you have the id but not the key

fading rivet
#

the deck view code uses obj_buffer

#

which stores the ids

#

wait actually im stupid I have actual card objects

#

I can just store the key

red flower
#

yeah

red flower
#

for the chips_text

coral flume
#

Oh swag, I'll look into that

#

I think I read something about that at one point

red flower
#

it's in the docs

gilded blaze
coral flume
red flower
#

i dont think it's silly, the docs don't really explain that problem

#

vanillaremade does however

coral flume
#

On Vanilla Remade now :)

fading rivet
fading rivet
#

thx

coral flume
#

I think I did it wrong, the event is making it change text when it triggers instead of right away, but does the entire update on first trigger

#
   calculate = function(self, card, context)
      if context.post_joker or (context.main_scoring and context.cardarea == G.play) then
        G.GAME.blind.chips = math.floor(G.GAME.blind.chips - G.GAME.blind.chips * 0.05)
          return {
            message = 'Depleted!',
            colour = G.C.MULT,
            card = card,
          },
        G.E_MANAGER:add_event(Event({
          func = function()
            G.GAME.blind.chip_text = number_format(G.GAME.blind.chips)
            return true
          end
        })) 
      end
    end
#

Is it as simple as event in wrong spot?

silent sail
#

if i have a consumable that gives a negative joker it doesnt work if joker slots are full, how would i fix that?

gilded blaze
urban wasp
#

okie dokie

gilded blaze
urban wasp
#

actually i just used loc_colour()

#

worked like a charm

red flower
red flower
silent sail
red flower
#

wdym

next timber
#

ok how much of a nightmare would it be to make a joker that adds like. a joker slot to the voucher area that refills once per ante

urban wasp
#

tried with a different technique but it still didn't work

check_for_unlock = function(self, args)
    return (
        (args.type == "win_stake" and get_deck_win_stake() == 5) and 
        (args.type == "win_deck" and get_deck_win_stake("b_red"))
    ) or (
        (args.type == "win_stake" and get_deck_win_stake() == 2) and
        (args.type == "win_deck" and get_deck_win_stake("b_blue"))
    )
end
calm marten
#

How i am i supposed to set a custom badge color without needing to set another rarity again?

#

i forgot

red flower
red flower
#

it should be one of the create_badge options

silent sail
#

or i mean itwould give it to me

#

also i have consumables that destroy a joker and make a joker but they also dont work when slots are full but then if it destroys a negative and the bypass is on it would overflow

red flower
#

hmm i still don't fully understand what you want to do

fading rivet
#

how could I prevent a card area from being destroyed when you start a run?

wanton jolt
#

how do you destroy a card area

fading rivet
#

which destroys all the cards in it

#

and apparently starting a run destroys the offscreen card areas i drew in the menu

red flower
#

they way i did it for one of my mechanics was to have the card not be in the uibox at all and just drawn over it

another way is to just copy_card the cards

wintry solar
#

You ca also save them and reload them

fading rivet
red flower
#

code?

fading rivet
# red flower code?

had no internet ;-;
found the issue though
For some reason the cards got removed

#

I think I've fixed it though

rapid stag
#

i wanna unparent a card from an area and have it bounce around the screen like the dvd logo 🤔

gusty compass
#

what would i have to use beside context.setting_blind to ensure G.hand.cards is set up

rapid stag
#

if G.hand.cards and #G.hand.cards >= G.hand.card_limit then?

gusty compass
#

ill give it a shot but idk if it will work since it prints nothing within the table when context.setting_blind runs

rapid stag
#

you could put it in an event, inside the event, return true at the end, outside return false
and that way the event will do the thing you are trying to do when the hand is set up?

#

but tbh a better way might be

#

context.first_hand_drawn?

gusty compass
#

actually that could work since all i really need is the cards to show up

#

also yea sry if my questions are stupid like that since i am not that much of an expert

red flower
#

or the context wouldn't be called because it's outside a run

gusty compass
#

then i might just be dumb lol

red flower
#

wait i misunderstood

#

first_hand_drawn should work

#

setting_blind is before cards are drawn

gusty compass
#

yea it did work, im a little curious how i can automatically play a hand but ill figure it out eventually

red flower
#

i think play_hand_from_highlighted exists

#

or something like that

gusty compass
#

yea that was my original plan but...

red flower
gusty compass
#

yea

#

also fyi

calculate = function(self, card, context)
        if context.first_hand_drawn then
            G.E_MANAGER:add_event(Event({
                trigger = "after",
                func = function()
                    local cards = CalcLib.get_random_items(G.hand.cards, 5, "fatesreward")
                    
                    for _, card in pairs(cards) do
                        card:highlight()
                    end 

                    G.FUNCS.play_cards_from_highlighted()

                    return true
                end
            }))
        end
    end```
#

its allegedly this part thats breaking

final jewel
#

is it possible to make the idea text smaller

#
badges[#badges + 1] = create_badge(localize('giga_art_credit', "labels")..obj.giga_data.art_credit, G.C.CLEAR, SMODS.get_badge_text_colour(G.C.GREY))

im using this

red flower
#

i think it's one of the arguments for create_badge let me check

final jewel
#

ok nice

red flower
#

yes it's the next argument

final jewel
#

like a number between 0 and 1 ig

red flower
#

no it's a multiplier iirc

#

so >1 would make it bigger and <1 smaller

final jewel
#

yeah

#

thx @red flower the goat

red flower
#

this is in the docs btw : )

final jewel
#

yeah but my lazy ass thought of modding-dev before 🤣 😭

#

can I get the link to the doc

red flower
final jewel
#

like I want to remove the padding under

red flower
#

you can't with the api

final jewel
#

oh

#

its a patch ig

red flower
#

well maybe with generate_ui but that's barely user friendly
you would need to modify the box

final jewel
#

well im bad with ui

red flower
#

arent we all

final jewel
#

i understand nothing in that

final jewel
#

@mod

#

like Sleepy is good

rapid stag
#

i wish there were more UI helper functions 😭

#

i'd write some, but it requires enough understanding of UI structure to make something functional

#

tbh, it could at least result in cleaner and more readable code

gusty compass
#

anything to make the ui structure easier to understand would be a blessing

versed swan
#

time for me to shill tf out of JTML

#

Actually I wanna revise it first ignore my shilling

gusty compass
#

balatro is such a great game to mod

primal robin
gusty compass
#

yea prob, the framework looks nice

primal robin
#

What if we take generate_card_ui
Scrap it entirely
And rebuild card (center, tag, blind) info UI in general by using new contexts similar to calculate context

gusty compass
#

🤷

rapid stag
#

go on

versed swan
#

Depending on where you wanna do this that seems a bit overkill

#

I.e. SMODS? Might be BetterCalc: The Empire Strikes Back

gusty compass
#

-# i have no idea how to make this stupid joker effect run in series

gusty compass
#

basically im making this joker that automatically plays a hand without consuming it and in essence it just breaks with multiple copies

#

since it runs its effect in parallel for whatever reason and highlights already playing cards

gusty compass
rapid stag
gusty compass
#

i could try but should i show the code just in case

rapid stag
#

sure

gusty compass
#

SMODS.Joker {
    key = "TBOJ_Fatesreward",
    atlas = "Placeholders",
    pos = {
        x = 0,
        y = 0
    },

    loc_vars = function(self, info_queue, card)
        return {
            key = "j_TBOJ_Fatesreward"
        }
    end,

    rarity = 2,
    cost = 7,

    blueprint_compat = false,

    calculate = function(self, card, context)
        if context.first_hand_drawn then
            local cards = CalcLib.get_random_items(G.hand.cards, 5, "fatesreward") -- shuffles cards
            
            G.E_MANAGER:add_event(Event({
                trigger = "after",
                func = function()
                    for _, selectedcard in pairs(cards) do
                        highlight_card(selectedcard, 0.5, "up")
                        table.insert(G.hand.highlighted, selectedcard)
                    end

                    card:juice_up(0.5, 0.2)
                    CalcLib.smart_play_cards(cards, 0) -- modified function to support deduction of 0 hands when played
                    
                    return true
                end
            }))

            return {}
        end
    end
}

rapid stag
#

how do you want multiple instances of the joker to be handled? only ever does it once, or does it for as many hands as there are copies of the joker?

urban wasp
rapid stag
gusty compass
#

for one copy it works fine, just not with multiple

#

-# i think

fading rivet
#

basicly this is whats happening

red flower
#

for this i would probably stop any copies while another one is calculating, and then do it again after

#

if the hand didn't win

fading rivet
#

joker1 pushes event
joker2 pushes event
...
joker1 event occurs
joker1 event pushes more events
joker2 event occurs
joker2 event pushes more events
...
more joker1 events
these pushes more
etc.

gusty compass
rapid stag
#

blocking = true, blockable = true?

red flower
rapid stag
#

ah

red flower
#

i wouldn't manage this through events only, it would be a nightmare

fading rivet
#

that might work

gusty compass
red flower
#

you can set a condition in the event so it doesn't happen until all the other ones finish but that seems really convoluted

#

also it would cause problems if a hand wins

primal robin
#

in short it's possible but not worth it, dont do that

gusty compass
red flower
#

i would either have some logic so each joker goes after the previous OR set up some other place (like mod's calculate or hooks) that handles each individually in one place

rapid stag
#

yeah, i would still say to do so in the mod calculate like i said, but also instead of checking next(SMODS.find_card('j_TBOJ_Fatesreward')), put SMODS.find_card('j_TBOJ_Fatesreward') into a local variable and iterate through that

gusty compass
#

ill give it a shot i guess

red flower
#

the issue is that you still need logic to do the effects sequentially so only a loop probably wouldn't work

#

don't really know how to explain it better without coding it myself

#

there's also no smods context for selecting hand after a hand is played

primal robin
#

what you're trying to do

gusty compass
#

im thinking since the function im using to remove the hand consumption is not part of the source code, i can just add another parameter to block it when one is already running but it would probably end up horribly

rapid stag
#

well forcing the hand to be played ends up calling more calculate events, right? so you could use other contexts to drive your loop iteration rather than just a simple for loop

gusty compass
#

and i thought this joker was gonna be a cakewalk 😭

rapid stag
#

although in that case instead of a local var, you make the SMODS.find_card('j_TBOJ_Fatesreward') go into a global var in your mod table

primal robin
#

I mean what you need to do is when first hand drawn context called, set global variable and event in event in event.
Check for variable to prevent duplicate behavious, event in event will play hand when it will became available

#

I see no problem here

red flower
#

wouldn't that be janky if anything has another event in an event

gusty compass
#

how would i pause the event exactly

primal robin
#

context calculating is single-layer queue so 3 layers in most cases will be enough

red flower
#

also some cards might expect for some context to be present after a hand is played

red flower
primal robin
#

Or, you can setup non-blocking non-blockable event which checks for game state and, if it's selecting hand, play it instantly, if it's round end, discard, otherwise keep itself in queue

#

then you'll not miss that for sure

primal robin
#

shouldnt be that difficult imo

rapid stag
#

oh, that would work

#

like ```lua
if G.STATE == G.STATES.SELECTING_HAND then
...
return true
end
return false

#

although a little more sophisticated than that, maybe

primal robin
#

you check for selecting hand as state where you need play hand, transition to round_eval to exit a loop when round win

#

And respect G.GAME.STOP_USE and G.CONTROLLER.locked

rapid stag
#

yes

gusty compass
#

im clearly stupid, i dont get it

#

i dont see where i would leave an event in queue bec its still all doing it in parallel

gusty compass
#

yea

rapid stag
#

the return false is what's leaving it in queue

#

ish

red flower
#

the state probably doesnt change immediately then

gusty compass
#

i think it would be better to make a variable in G.GAME or something and set stuff there

#

if not, im lowk lost

rapid stag
#

yeah, have a G.GAME var to halt additional event instances

#

although those additional instances should still be checking G.GAME.STOP_USE and G.CONTROLLER.locked, as well as G.STATES == G.ROUND_EVAL specifically to terminate if one of the hands wins

primal robin
#

that's exactly what you need to do

gusty compass
#

kk

#

would something like this work

rapid stag
#

closer to

if
  not (G.GAME.FRTriggering
  or G.GAME.stop_use
  or G.CONTROLLER.locked)
then
  G.GAME.FRTriggering = true
  
  ...
  
  return true
elseif G.STATE == G.STATES.ROUND_EVAL
  return true
end
return false
``` but yes, that is the general idea
#

actually, that's still gonna be queued if you start it, go to the main menu and do smth like start another run. even just reloading the saved run would make it screwy. would need to check against something like G.STAGE == G.STAGES.MAIN_MENU to kill the event too

primal robin
#

close but not like this

#
if context.first_hand_drawn and not G.GAME.FRTTriggered then
  G.GAME.FRTTriggered = true
  G.E_MANAGER:add_event(Event({
    blocking = false,
    func = function()
      if G.STATE == G.STATES.ROUND_EVAL then
        G.GAME.FRTTriggered = false
        return true
      elseif G.STATE == G.STATES.SELECTING_HAND and (G.GAME.STOP_USE or 0) < 1 and not G.CONTROLLER.locked then
        -- play your hand
        return false
      end
      return false
    end
  }))
end
#

that's it

rapid stag
#

i'd say also

if G.STATE == G.STATES.ROUND_EVAL or G.STAGE == G.STAGES.MAIN_MENU then
      G.GAME.FRTTriggered = false
      return true
...
``` to prevent save reload/new run tomfoolery
primal robin
#

no needs, event queue is fully cleared when you exit a run

rapid stag
#

oh right

primal robin
#

and of course you need setup a check on game:load or somewhere and check is joker which starts thing is present and if so start loop again

#

othervise save-scum breaks the loop

rapid stag
#

is joker?

primal robin
#

variable is not reliable method because it will be nil on first hand

gusty compass
#

im pretty sure theres something wrong with the code i have since it just seems to ignore all other conditions

#

it just prints ELSE

#

yea G.GAME.STOP_USE is at 1 apparently

rapid stag
#

@primal robin

primal robin
#

blocking = false

gusty compass
#

for one thing it works but it does it indefinitely

rapid stag
# gusty compass it just prints ELSE

you also need to put not G.GAME.FRTriggering in the middle condition.

i'm not sure about turning it back off within that same condition, either. you can try it, but i suspect you'll still have the same issue

gusty compass
#

yea the blocking got it to work

#

but i gotta make it know it has already played its hand

rapid stag
#

try changing the return false in the second condition to true instead

gusty compass
#

awesome :D

#

thanks you guys

rapid stag
#

is there a way i can juice_card_until juice more aggressively? like increase the rate at which the animation is played

primal robin
#

probably make own one

vale grove
#

eligible_card:set_edition({ polychrome = true })
eligible_card:add_sticker({ eternal = true })
how do i correctly apply the eternal sticker to a joker?

daring fern
sleek valley
#

im trying to make a function for a consumeable with a charge rate
basically, you use the consumeable once and it stays in play but you cant use it again until it recharges, but im struggling with how to make it
i want it to gain 1 charge after each round

#

can anyone help?

timid zinc
#

is there a way to make the scaling work with joker retriggers?

viscid talon
#

currently redoing risky revolver

sleek valley
timid zinc
#

Looks good to me

vale grove
#

is there a context check for a seal being played for jokers?

slim ferry
#

this acts as doing nothing return wise but will still calculate joker retriggers and post_trigger

timid zinc
#

if it scales after\

slim ferry
#

oh wait

#

nvm

#

the scaling should just work?

#

i think

timid zinc
#

what if it was after the if card.ability.extra.x_mult > 0 check

daring fern
vale grove
daring fern
vale grove
# daring fern No, it would be `context.other_card:get_seal() == 'Red'`
    calculate = function(self, card, context)
        if context.individual and context.cardarea == G.play and context.other_card:get_seal() == "Everyone" then
            card.ability.extra.mult = card.ability.extra.mult + card.ability.extra.mult_mod
            return {
                message = localize { type = 'variable', key = 'a_mult', vars = { card.ability.extra.mult_mod * card.ability.extra.mult } },
            }
        end
        if context.joker_main then
            return {
                mult = card.ability.extra.mult
            }
        end
    end

ive been bashing my head against the wall with this for a little bit and cant seem to figure out why it doesnt work

fading rivet
vale grove
#

the key = 'Everyone'

#

if thats what you wanna know

fading rivet
#

ight

#

hows it not working

vale grove
#

i think the first contexxt check doesnt work

fading rivet
#

print statement

#

test it

vale grove
#

bcs i have a #1# on mult that doesnt increase at all

vale grove
#

lowk

fading rivet
#

💀

#

okay here's what id try

#

seperate the first 2 conditions in the if

#

print the result of get seal

#

then check the seal

vale grove
#

otherwise #1# would go up

fading rivet
#

first 2 conditions as in context.individual and contex.cardarea == G.play

vale grove
slim ferry
#

for the seal...

vale grove
#

also tried _seal

#

at the end

slim ferry
#

i mean you do need the mod prefix

#

and not _seal

vale grove
#

ive tried modprefix_sealkey

#

idk if thats the incorrect way to do it?

viscid talon
#

I get a strange error when using Room No. 2024

After a few antes, it may sometimes reset by subtracting a bit too much, leading to weird situations where I get -2 hand size

#

I don't really know how to explain it, I would need to show the code

#
-- Room No. 2024
SMODS.Joker({
    key = "room2024",
    config = { extra = { h_size = 0, h_mod = 1 } },
    loc_txt = {
        ["name"] = "Room No. 2024",
        ["text"] = {
            {
                "If played hand contains a {C:attention}#2#{}",
                "add {C:attention}+1{} hand size, resets when",
                "{C:attention}Boss Blind{} is defeated"
            }
        },
    },
    pos = { x = 7, y = 7 },
    cost = 9,
    rarity = 3,
    blueprint_compat = true,
    eternal_compat = true,
    perishable_compat = true,
    unlocked = true,
    discovered = false,
    atlas = "CustomJokers",

    loc_vars = function(self, info_queue, card)
        return { vars = { card.ability.extra.h_size, localize('Full House', 'poker_hands', card.ability.extra.h_mod) } }
    end,

    calculate = function(self, card, context)
        if context.before and not context.blueprint and (next(context.poker_hands['Flush House']) or next(context.poker_hands['Full House'])) then
            -- See note about SMODS Scaling Manipulation on the wiki
            card.ability.extra.h_size = card.ability.extra.h_size + card.ability.extra.h_mod
            G.hand:change_size(card.ability.extra.h_mod)
            return {
                message = localize('k_upgrade_ex'),
            }
        end
        if context.end_of_round and context.game_over == false and context.main_eval and not context.blueprint then
            if context.beat_boss and card.ability.extra.h_size > 1 then
                G.hand:change_size(-card.ability.extra.h_size)
                return {
                    message = localize('k_reset'),
                }
            end
        end
    end,

    remove_from_deck = function(self, card, from_debuff)
        G.hand:change_size(-card.ability.extra.h_size)
    end -- I mean it kinda works? Kinda? I dont know, I got a weird error where I got a handsize of 4 once. No idea how to reproduce that.
})
vale grove
#

im honestly so lost at how to do this

#

who knew calling a seal in jokers was so annoying

red flower
#

you should do that in both end_of_round and remove_from_deck

vale grove
viscid talon
slim ferry
#

card.ability.extra.h_size = 0

#

exploding head emoji

viscid talon
#

no need to be a dick abt it

#

anyways

#
-- Demented Face
SMODS.Joker({
    key = "dementedface",
    config = { extra = { xmult = 1.5, odds = 4 } },
    loc_txt = {
        ["name"] = "Demented Face",
        ["text"] = {
            {
                "{C:white,X:mult}X#3#{} Mult",
                "for played {C:attention}face{} cards",
                "{C:green}#1# in #2#{} chance to",
                "{C:attention}destroy{} played cards"
            }
        },
    },
    pos = { x = 0, y = 8 },
    cost = 9,
    rarity = 3,
    blueprint_compat = false,
    eternal_compat = true,
    perishable_compat = true,
    unlocked = true,
    discovered = false,
    atlas = "CustomJokers",
    loc_vars = function(self, info_queue, card)
        local numerator, denominator = SMODS.get_probability_vars(card, 1, card.ability.extra.odds,
            'j_hatch_dementedface')
        return { vars = { numerator, denominator, card.ability.extra.xmult } }
    end,

    calculate = function(self, card, context)
        if context.individual and context.cardarea == G.play and context.other_card:is_face() and SMODS.pseudorandom_probability(card, 'j_hatch_dementedface', 1, card.ability.extra.odds) then
            SMODS.destroy_cards(context.full_hand, nil, nil, true)
        end
        if context.individual and context.cardarea == G.play and context.other_card:is_face() then
            return {
                xmult = card.ability.extra.xmult
            }
        end
    end
})

what context should i run for SMODS.destroy_cards?

#

bc im guessing context.full_hand just... fucks things up

rapid stag
#

you shouldn't be destroying cards in calculate that way, give me a second to give an example how you should do that because i don't really know how to explain

viscid talon
#

i tried remove but it didnt rly work 😭

#

it just juiced and then did nothing

#

im guessing i need context.destroy_card

rapid stag
#

yes. you should generally be using context.destroy_card for things like this. see:

if
  context.individual
  and context.cardarea == G.play
  and context.other_card:is_face()
  and not context.other_card.getting_sliced
  and SMODS.pseudorandom_probability(card, 'j_hatch_dementedface', 1, card.ability.extra.odds)
then
  for _, pCard in ipairs(context.full_hand) do
    pCard.getting_sliced = true
  end
end

if context.destroy_card and context.destroy_card.getting_sliced then
  return { remove = true }
end

...
viscid talon
#

ah

#
    loc_vars = function(self, info_queue, card)
        local numerator, denominator = SMODS.get_probability_vars(card, 1, card.ability.extra.odds,
            'j_hatch_dementedface')
        return { vars = { numerator, denominator, card.ability.extra.xmult } }
    end,

    calculate = function(self, card, context)
        if context.individual and context.destroy_card and context.cardarea == G.play and context.other_card:is_face() and SMODS.pseudorandom_probability(card, 'j_hatch_dementedface', 1, card.ability.extra.odds) then
            return {
                remove = true
            }
        end
        if context.individual and context.cardarea == G.play and context.other_card:is_face() then
            return {
                xmult = card.ability.extra.xmult
            }
        end
    end
})
#

this is how i did it

#

although

#
    calculate = function(self, card, context)
        if context.individual and context.cardarea == G.play and context.other_card:is_face() and SMODS.pseudorandom_probability(card, 'j_hatch_dementedface', 1, card.ability.extra.odds) then
            if context.destroy_card then
            return {
                remove = true
            }
        end
    end
#

this may be cleaner

rapid stag
#

you shouldn't do a context.destroy_card check inside a context.individual check

#

they're separate contexts

#

but you should also generally not be returning remove = true in any context that isn't context.destroy_card

#

if you're concerned about the cards being parsed by other things, that's what setting getting_sliced helps with. it also makes the card fail can_calculate()

viscid talon
#

o

red flower
rapid stag
#

oh?

red flower
#

it was made to handle this

rapid stag
#

ahhh girldmDizzy

#

there's really no issue with them destroying the entire hand in context.individual?

viscid talon
#

then idk what to do 😭

#

do i keep the SMODS.destroy_cards(context.full_hand, nil, nil, true) ?

rapid stag
#

yes. based on what N said, i was wrong and what you were doing with SMODS.destroy_cards in context.individual is fine

viscid talon
#

i see

#

i just tested my original code and its being a little wacky

#

the idea is this

#

every face card thats played has a 1 in 4 chance to destroy itself

#

and a 3 in 4 chance to give x1.5 mult

#

how do i make my code reflect that

rapid stag
#

ohhhhhh

viscid talon
#

yeah, bc i have no idea how to make the code do that

frosty rampart
#

here's some pseudocode to get you started

during context.individual
  if context.other_card is a face card
    1 in 4 chance to SMODS.destroy_cards(context.other_card)
    3 in 4 chance to return { xmult = 1.5 }
  end
end
#

(is the 3 in 4 chance independent from the 1 in 4 chance?)

viscid talon
#

no, it's the same probability

rapid stag
#

you make that hinge on whether the pseudorandom check suceeds (destroy) or fails (xmult)
i would implement that like:

if
  context.individual
  and context.cardarea == G.play
  and context.other_card:is_face()
  and not context.other_card.getting_sliced
then
  if SMODS.pseudorandom_probability(card, 'j_hatch_dementedface', 1, card.ability.extra.odds) then
    context.other_card.getting_sliced = true
  else
    return { xmult = card.ability.extra.xmult, message_card = context.other_card }
  end
end

if context.destroy_card and context.destroy_card.getting_sliced then
  return { remove = true }
end
viscid talon
#

so it would be smth like if else

#

im just scared to use else statements 😭

frosty rampart
#

ok yea it'd just be if 1 in 4 chance, destroy. else, xmult

viscid talon
#

apparently its lazy code but idk

frosty rampart
#

who told you that?? blundering them with my mind rn

rapid stag
#

no, elses in general are fine, what the problem is with them is making giant if else blocks. see the vanilla calculate_joker code 🥴

viscid talon
#

ah so thats what sliced means

frosty rampart
#

yea a regular if/else statement is perfectly fine, if you're doing if ... elseif ... elseif ... elseif ... elseif ... else you might want to consider restructuring your code lol

viscid talon
#

thats the stuff i see in my old jokerforge code

#

i had to go back and redo certain jokers of mine to make it better

versed swan
viscid talon
#

example being risky revolver, which has been redone bc i didnt like how it worked

#
-- Demented Face
SMODS.Joker({
    key = "dementedface",
    config = { extra = { xmult = 1.5, odds = 4 } },
    loc_txt = {
        ["name"] = "Demented Face",
        ["text"] = {
            {
                "{C:white,X:mult}X#3#{} Mult",
                "for played {C:attention}face{} cards",
                "{C:green}#1# in #2#{} chance to",
                "{C:attention}destroy{} played cards"
            }
        },
    },
    pos = { x = 1, y = 8 },
    cost = 9,
    rarity = 3,
    blueprint_compat = false,
    eternal_compat = true,
    perishable_compat = true,
    unlocked = true,
    discovered = false,
    atlas = "CustomJokers",
    loc_vars = function(self, info_queue, card)
        local numerator, denominator = SMODS.get_probability_vars(card, 1, card.ability.extra.odds,
            'j_hatch_dementedface')
        return { vars = { numerator, denominator, card.ability.extra.xmult } }
    end,

    -- credit to NopeTooFast and Meta and N' for help
    calculate = function(self, card, context)
        if context.individual and context.cardarea == G.play and context.other_card:is_face() and not context.other_card.getting_sliced then
            if SMODS.pseudorandom_probability(card, 'j_hatch_dementedface', 1, card.ability.extra.odds) then
                context.other_card.getting_sliced = true
            else
                return {
                    xmult = card.ability.extra.xmult
                }
            end
        end
        if context.destroy_card and context.destroy_card.getting_sliced then
            return { remove = true }
        end
    end
})```
#

heres what i got

#

time to test this bitch

#

ok it works

viscid talon
#

how do i animate a joker again

#

animation atlas

frosty rampart
#

it's a lot simpler nowadays
in the atlas definition, set atlas_table = "ANIMATION_ATLAS" and frames = (number of frames), and then you just have to put all the frames in a single row in the image and the game will animate it on loop for you
it defaults to 10 fps, but you can set fps = something to change it

viscid talon
#

oo yay

urban wasp
ancient escarp
#

can i slow down the speed messages play? ex: the "again!" on retriggers

red flower
vital wren
#

is there a method or context i can use for when a card with a specific sticker is added to the deck? tried using add_to_deck in the sticker definition but it doesn't seem to have that

red flower
#

stickers have apply

rapid stag
#

N, do you know if steamodded has anywhere where it declares any G.GAME vars? thinking of iterating my dynamic (un)ban system some more now that i have a vague idea of how i'm going to tackle it

wanton jolt
#

is there a reason why this would not work

#

the rarity check

vital wren
#

i need to use add to deck instead because the sticker debuffs the card so i want that to happen when its gained not when its created in the shop

rapid stag
wanton jolt
#

i tried that before and had no luck

rapid stag
wanton jolt
#

its revo's rarity

red flower
red flower
red flower
#

i can check revos repo i guess

#

hmm i dont see why that wouldn't work

wanton jolt
#
SMODS.Rarity({
    key = "p",
    badge_colour = G.C.RARITY[3],
    pools = {
        ["Joker"] = {
            rate = 0.01,
        },
    },
    default_weight = 0.01,
})
rapid stag
wanton jolt
rapid stag
#

and it's just that one condition that doesn't work?

wanton jolt
#

yeah only the printer doesnt work

vital wren
#

found what i was looking for: context.card_added

rapid stag
wanton jolt
#

if i try without its just going to apply everywhere

rapid stag
#

if you're concerned about that when testing it, you could comment out the add sticker function and add a print

wanton jolt
#

everyWHERE EXCEPT THE PRINTER CARD???

#

@dreamy thunder

#

i'm commenting out the printer totem

#

while u try to see why this happens

rapid stag
#

what was it you commented out to test for this result, can you show

wanton jolt
#

i just removed the rarity check

rapid stag
#

ah

wanton jolt
#

so it applies to every card if the totem head is printer (which was the case)

rapid stag
#

yeah, i don't get why the rarity check doesn't work

#

i'm not familiar with revo's

true jasper
#

how would i change the rarity badge text for a specific joker

rapid stag
true jasper
rapid stag
frosty rampart
#

(it's not so much "editing the rarity badge" as it is "skipping the rarity badge and letting you make your own")

rapid stag
#

yeah

mystic river
#

(for your own joker, just skip the taking-ownership part)

obtuse mortar
#

hey hi hello im a Little Bit new here (joined like 50 minutes ago lol) and i come here in search of some help. only recently bought balatro on steam, though ive had an idea floating around in my head a bit. i did a very silly and Bad mod as a quick test and got some of the basics down, though im unsure of the specific syntax(?) used for stuff i had thought of and searching on my own yielded nothing.

Xmult scaling is the first thing ive not figured out yet. i Assume it’s just Xmult_scale similar to an example i saw using chip_scale but i still wanted to corroborate
resetting the scaling after a certain condition similar to Ride the Bus is another part that’s been stumping me (more on the specific condition later)
splash. just in general. i tried to look for what makes it count all cards in scoring in the source code but i couldn’t find anything and left feeling the fool myself

now, for any of this to work, the joker needs to check something which i gaslit myself into thinking was already in the game? but it isn’t, which complicates things quite a bit. i need to somehow check the sum of the base chip value for the played cards. ive no idea how to go about this At All. the gist of my idea was that, if the base value of the played cards was equal to the specified number, it would play out the splash effect and scale the Xmult. if the total went over, however, it would Not do that and reset back to 1x instead (the number is 21 it’s blackjack it’s just a blackjack joker)

but anyway, if anyone has any form of helping out that would be cool thamks. also my apologies for the big ass wall of text i just wanted to make sure i put out all the information at once skfjdkfjfj

fading rivet
#

use vremade and the smods wiki instead

#

also looking at other people's code helps

sturdy compass
fading rivet
#

man I wonder who made those

sturdy compass
#

I did not make the starter pack smh

fading rivet
#

yeah but the other one

urban wasp
#

any idea why this doesn't work? been asking for about a day now

check_for_unlock = function(self, args)
    return (
        (args.type == "win_stake" and get_deck_win_stake() == 5) and 
        (args.type == "win_deck" and get_deck_win_stake("b_red"))
    ) or (
        (args.type == "win_stake" and get_deck_win_stake() == 2) and
        (args.type == "win_deck" and get_deck_win_stake("b_blue"))
    )
end
#

on a deck btw

idle plaza
idle plaza
idle plaza
#

You're welcome.

final jewel
#

why does I get an extra box

coral flume
#

Was testing out tables, Does anyone know why this happens like a million times at round end?
Code:

if context.end_of_round then
      if card.ability.extra.count < 5 then
        card.ability.extra.count = card.ability.extra.count + 1
        print(Count[card.ability.extra.count])
        print(card.ability.extra.count)
      else if card.ability.extra.count == 5 then
        card.ability.extra.count = 1
        print(Count[card.ability.extra.count])
        print(card.ability.extra.count)
        end
      end
    end
idle plaza
coral flume
#

oh lmao

#

Cheers :)

tulip pecan
#

how would I read the a config value from a sticker if the location I want to read it to isn't inside the sticker code? For instance, I know that I can read default extra values from a joker by using G.P_CENTERS.j_modprefix_key.config.extra, but I don't know how to do the same with a sticker.

wary moth
#

Is there a way to disable vanilla tarot cards, spectral cards, and tags from all showing up?
I know theres a mod out there that lets you manually set that up, but, it'd be cool if I could have it automatically do that for my mod

tulip pecan
daring fern
wary moth
true jasper
#

how do I change the color of the background of a joker description

reef belfry
#

how do people usually make a certain type of consumable in a consumable set appear less, something akin to soul

wintry solar
#

Wait four hours and do it really easily

reef belfry
#

eh? cant I have a parameter set for how often it appears?

wintry solar
#

In the next release you can just do weight = 0.1 for example which will be 100 times rarer

reef belfry
#

how timely convenient

teal grotto
#

how do you make a sticker incompatible with others like eternal/perishable

reef belfry
#

what API method am I supposed to use to use for the consumable to be updated in a booster pack?

#

and why is it returning high card and not 4oak since thats supposed to be the default

red flower
reef belfry
# red flower loc_vars? what's the code

SMODS.Consumable{
    key = 'nptunicorndream',
    set = 'creamerycons',
    atlas = 'creameryatlas',
    nptrarity = 'rarecreamery',
    hidden = true,
    soul_set = 'creamerycons',
    can_repeat_soul = true,
    soul_rate = 0.0,
    pos = {x = 0, y = 2},
    soul_pos = {x = 1, y = 2},
    config = { extra = {base=3, scaleou = 1, scale = 3, mphand = tje.mphand(), max = 50} },
    loc_vars = function(self, info_queue, card)
        if card.ability.extra.base + (math.floor(G.GAME.hands[card.ability.extra.mphand].level/card.ability.extra.scale)) > card.ability.extra.max then
            return {vars = {card.ability.extra.base, card.ability.extra.scaleou, card.ability.extra.scale, card.ability.extra.max or card.ablity.extra.base, card.ability.extra.mphand, card.ability.extra.max}}
        else
            return {vars = {card.ability.extra.base, card.ability.extra.scaleou, card.ability.extra.scale, card.ability.extra.base + (math.floor(G.GAME.hands[card.ability.extra.mphand].level/card.ability.extra.scale)) or card.ablity.extra.base, card.ability.extra.mphand, card.ability.extra.max}}
        end
    end,
    update = function(self, card, dt)
        card.ability.extra.mphand = tje.mphand()
    end,
    can_use = function()
        return true
    end,
    calculate = function(self,card, context)
        card.ability.extra.mphand = tje.mphand()
    end,
    use = function(self, card)
        if (math.floor(G.GAME.hands[card.ability.extra.mphand].level/card.ability.extra.scale)) > card.ability.extra.max then
            print((math.floor(G.GAME.hands[card.ability.extra.mphand].level/card.ability.extra.scale)) ..'is more than'.. card.ability.extra.max)
            udnpttargetlevel = card.ability.extra.base + card.ability.extra.max
        else
            print((math.floor(G.GAME.hands[card.ability.extra.mphand].level/card.ability.extra.scale)) ..'is less than'.. card.ability.extra.max)
            udnpttargetlevel = card.ability.extra.base + (math.floor(G.GAME.hands[card.ability.extra.mphand].level/card.ability.extra.scale))
        end
        update_hand_text({sound = 'button', volume = 0.7, pitch = 1.1, delay = 0}, { mult = G.GAME.hands[card.ability.extra.mphand].mult, chips = G.GAME.hands[card.ability.extra.mphand].chips, handname = card.ability.extra.mphand, level = ''})
        level_up_hand(card, card.ability.extra.mphand, nil, udnpttargetlevel)
        update_hand_text({sound = 'button', volume = 0.7, pitch = 1.1, delay = 0}, {mult = 0, chips = 0, handname = '', level = ''})
    end
}
#

made this like very long ago I think

#

so apologies if it seems really REALLY annoying to read

red flower
#

yeah if you set a value in config it's only set when you boot up the game

reef belfry
#

ahh

red flower
#

instead of saving it to config just replace the return in loc_vars for the mphand with that function

frosty dock
#

zeebee

red flower
reef belfry
#

it uhhh still returns high card 😭

frosty dock
reef belfry
#

whar

frosty dock
#

are you updating the value in your loc_vars function?

reef belfry
#

updating how

#

idk!!!

#

sorry!!

reef belfry
#

ahh found out why, tnx anyways!!

final jewel
rapid stag
rapid stag
#

uhhhhh

#

what about generate_ui?

final jewel
#

I dont have that

#

thats weird right

rapid stag
#

this is the only thing you get an extra multibox for?

final jewel
#

yeah

rapid stag
#

are you doing any other description-related stuff, like patching vanilla code somewhere, or

final jewel
#

I have a patch but for credit

rapid stag
#

hmmm

mystic river
#

after updating the code, did you start a new run and get a new item?

rapid stag
#

yes, check a fresh instance

final jewel
#

I wasnt in a run when I check

#

I was in collection

rapid stag
#

ah

#

could i see the full joker code, then? i don't see how you could possibly have an extra random multibox

final jewel
#
SMODS.Joker:take_ownership("j_joker", { --Ritten
    atlas = 'Jokers',
    pos = {x = 0, y = 0},
    taw_data = {
        grow_time = 2,
        mult_add = 1,
        mult = 0
    },
    loc_vars = function(self,info_queue,center)
        return{vars = {center.ability.mult + center.config.center.taw_data.mult, center.config.center.taw_data.grow_time}}
    end,
    calculate = function(self,card,context)
        if context.joker_main then
            return {
                mult = card.ability.mult + card.config.center.taw_data.mult
            }
        end
        if context.end_of_round and context.main_eval then
            card.config.center.taw_data.grow_time = card.config.center.taw_data.grow_time - 1
            if card.config.center.taw_data.grow_time <= 0 then
                Taw.grow(card, 'j_taw_rat')
            end
        end
    end
}, false)```
#

im just taking ownership

rapid stag
#

hm. i wonder if you assign your joker localization to a new key in the localization file, then return that key as an arg from loc_vars: key = 'your_new_key_here'

#

that might solve your issue

fickle gust
#

i'm trynna make a custom edition and this keeps crashing idk why

#
SMODS.Edition {
    key = 'test',
    shader = 'foil',
    prefix_config = {
        shader = true
    },
    calculate = function(self, card, context)
    end
}```