#💻・modding-dev

1 messages · Page 58 of 1

dim nimbus
#

I still just can't seem to figure out how to get the cards to actually, be removed

shell tangle
#

What's your code at now, and what stage of scoring does it lock up on? I'd say figuring out why it's locking up first is more important than figuring out the functionality, or did you mean removing a for statement fixed it?

dim nimbus
#

I would think removing the for i,v in ipairs fixed it, game loads properly

#
    calculate = function(self, card, context)
        if context.individual and context.cardarea == G.play and not context.blueprint then
            if pseudorandom('minihakkero') < G.GAME.probabilities.normal/self.config.extra.odds then
                card.ability.extra.money = card.ability.extra.money + card.ability.extra.money_mod
                card_eval_status_text(card, 'extra', nil, nil, nil, {message = localize('k_upgrade_ex')})
                return {calculated = true}
            end
            local destroyed_cards = {}
            G.E_MANAGER:add_event(Event({
            trigger = 'after',
            delay = 0.2,
            func = function() 
                for i=#G.play, 1, -1 do
                destroyed_cards[#destroyed_cards+1] = G.play[i]
                local card = G.play[i]
                    if card.ability.name == 'Glass Card' then 
                        card:shatter()
                    else
                    card:start_dissolve(nil, i == #G.play)
                    end
                end
            end
            }))
        end
    end
#

What's happening now is that, when i play a hand, the game just doesn't score the hand and softlocks there

frosty dock
#

you forgot the part where the event needs to return true

shell tangle
#

Is there a thorough explanation of the event manager anywhere, or has no one made one yet?

dim nimbus
#

oh lol, hold on let me change that

frosty dock
dim nimbus
#

okay, so it does score now, (just added return true to the end of the event manager)

#

but it still doesnt destroy the cards

#

wait hold on this is wrong in the first place

#

oh this might be more tricky than i thought

#

is there any way i can make it calculate based on just the intial scoring and not based on retriggers?

nocturne garnet
#

problem: where the hell do i put it

shell tangle
shell tangle
# nocturne garnet

Is this meant to be a joker or a new object type? If a joker, upon the creation event, use G.jokers:emplace(card), assuming local card is what you have create_joker or whatever you used as.

dim nimbus
sharp phoenix
#

Is there a way to force a joker to always show up in shops? i want to try and create a new joker but if it only shows up randomly i dont know how to test it

dim nimbus
#

theres this mod called debugplus, allows you to spawn in a joker from your collection

#

also can change suits, enhancements, the whole kahuna

sharp phoenix
#

ah i see do you have a link to it by any chance?

languid mirage
sharp phoenix
#

legend thanks

zealous glen
#

That is the better way to accomplish what you want, but in general an easy way to forcefully spawn a card is via hooking create_card

hushed cradle
#

is there any way i can add text to a row column?

nocturne garnet
#

i always add G.(cardarea):emplace(card)

#

anyways chat am i cooked

#

i got no joker slots

zealous glen
#

Row or column?

hushed cradle
#

row

#

sorry

#

meant to say row node

zealous glen
#

Put a text UI object inside

hushed cradle
#

no i dont wanna have a new text node

#

i wanna know if i can have text in the row

zealous glen
#

You can create a text node inside a row node

hushed cradle
#

i dont want to

zealous glen
#

why not

hushed cradle
#

because then it creates a new row node

zealous glen
#

That’s not how it works

hushed cradle
#

that is

#

ive got a row node with 2 columns, i want text overlayed on the columns

#

if i add a text node

zealous glen
#

It’s either misaligning (compared to what you expect) or you’re doing something wrong

hushed cradle
#

its a new row node

zealous glen
#

It doesn’t create a row node AFAIK

#

Anyways

dim nimbus
#

ive looked at just about every card destroying joker and consumable in the game but i still havent found something that works. its way more difficult to remove a random card than i thought

zealous glen
#

There’s probably some nested combination of nodes that work

#

What do you want it to look like?

zealous glen
dim nimbus
#

i dont know its just never destroying the card. everything else works fine except for that

hushed cradle
zealous glen
# hushed cradle

Oh I don’t think that game supports that. I think you’d either need to edit UI code or try to cheat it in some way. Maybe using root nodes, but Idk how they work

#

Maybe you could cheat it with a shadow?

#

Like make the second color a shadow that’s translated to the side?

shell tangle
# hushed cradle

Might be able to frakenstein it by having the text split into 2 nodes, left being aligned to tr/cr of the left column, and right being aligned to tl/cl of the right column, and some padding shenanigans.

zealous glen
#

That’s also a possibility, but I remember text inside of columns being weird. Although

#

That might’ve just been the X control for tooltips

#

Because it’s hardcoded to behave in a certain way

zealous glen
dim nimbus
#

i was preoccupied with something hold on

#
    calculate = function(self, card, context)
        if context.individual and context.cardarea == G.play and not context.blueprint and not context.repetition then
            if pseudorandom('minihakkero') < G.GAME.probabilities.normal/self.config.extra.odds then
                card.ability.extra.money = card.ability.extra.money + card.ability.extra.money_mod
                card_eval_status_text(card, 'extra', nil, nil, nil, {message = localize('k_upgrade_ex')})
                return {calculated = true}
            end
            local destroyed_cards = {}
            for i,v in ipairs(context.full_hand) do
                destroyed_cards[#destroyed_cards+1] = G.play[i]
            end
            G.E_MANAGER:add_event(Event({
            trigger = 'after',
            delay = 0.2,
            func = function() 
                for i=#G.play, 1, -1 do
                local card = G.play[i]
                    if card.ability.name == 'Glass Card' then 
                        card:shatter()
                    else
                    card:start_dissolve(nil, i == #G.play)
                    end
                end
            return true end}))
        end
    end
shell tangle
zealous glen
#

Also I’m pretty sure here it’s the same as context.full_hand

shell tangle
zealous glen
zealous glen
#

You need to think in terms of Event handling to try and decide what’s better inside or outside

#

Boolean checks are often better inside in case the object ceases to exist before the Event is handled

#

For example with multiple copies of the same effect

#

Although you can check if the object still exists inside the Event

dim nimbus
#

hmmmm. yeah theres still an issue

#

because now it destroys the entire hand irrelevant to which cards activate

zealous glen
#

I mean that’s what the code you wrote does

wintry solar
#

I think using a new root node resets the position somewhat, I haven’t played around with it fully but I was experiencing stacking tooltips when getting galdur tooltips working properly

dim nimbus
#

also the trigger seems to not work the cards dissolve before scoring, but thats probably easy enough to fix

#

so im wondering, would i need to use a pseudoseed in order to make it only have a chance to destroy a played card?

zealous glen
#

Otherwise you either need to inject there or find a context afterwards. Maybe context.after but I think that can break things still

zealous glen
hushed cradle
wintry solar
#

You could also just have the text node in a row below these colours and play with negative padding

hushed cradle
#

im just not gonna worry about it

#

do smth else

zealous glen
#

That is wise

dim nimbus
#

IT WORKS!!!!!!!!

#

and the trigger is fixed too!

#

uh

#

huh

#

it worked and then some really weird stuff started happening

zealous glen
dim nimbus
zealous glen
#

Did you just make the delay really big?

dim nimbus
zealous glen
dim nimbus
#

though that would work

zealous glen
dim nimbus
#

oh no

#

i didnt do that

zealous glen
#

Ah alright

dim nimbus
#

also it seems to be very weird right now

zealous glen
#

what’s it like anyways

dim nimbus
#

okay wait i see why its being weird

hushed cradle
#

how tf have i never noticed the white border almost every joker has

dim nimbus
zealous glen
analog spoke
#

GOOD NEWS, It's stopped crashing, however, now the message isn't working (the mult still isn't working from before either but after some searching I think I might know why that is)

#

this is where the effect is at currently

stray warren
#

I haven't heard of Emult_mod. Is that a thing?

analog spoke
#

I believe it's from talisman, same thing cryptid uses for jokers like exponentia I believe

stray warren
#

ah makes sense

analog spoke
#

I recall hearing that individual context does this differently but I'm not entirely sure how, something to do with changing mult_mod to just mult? but my attempt at doing that made it crash, lol, so I think I'm still missing something

#

I should try looking in the files again

#

ok, I'm really close here, I got it to work with +mult, now I just need to get it to work with emult

#

YES

#

it works, omg, I did it 🫠

#

there are more things I wanna test out and learn using this joker but for now I think I'll play with it for a bit, I wanna have a quick run with this working joker before turning it into something new

hushed cradle
#

i think mult is for giving mult when a card scores, like even steven for example, and mult mod is for jokers that just give mult during the joker scoring part, like default jimbo etc

zealous glen
#

Does anyone know how Smeared Joker causes bosses like The Window to debuff Hearts if you generate it during the Blind?

#

I implemented this Joker which only applies its effects while there are cards in G.play, so it dodges those debuffs, but that seems unintuitive, but I'm not sure how I'd fix it

zealous glen
#

Ah whenever a card is added to the deck it calls Blind.set_blind

shell tangle
#

Smeared joker has a line directly in the ``is_suit()` function, which is why it allows it to count as multiple things.

zealous glen
#

That's an answer to a different question

shell tangle
#

I just assumed that it's because the blind stuff is called automatically, and calls is_suit(), but, I haven't looked at blinds that much myself.

zealous glen
#

That answer is so generic that it's true, but it's so generic it's not helpful

#

Anyways, while Blind.debuff_card is called in a handful of places, as I said adding a card to the deck calls Blind.set_blind which in turn calls Blind.debuff_card

#

Which with Blinds like the Window calls Card.is_suit

#

So maybe I can hook the play function to add a call of Blind.set_blind right at the start? That might not work depending on when cards move to the play CardArea

#

It seems to have worked

static juniper
#

This is probably very impossible, but is there a way to have a card be considered two ranks when scoring?

#

I had an idea for a binary joker, whose gimmick is that 10s are also considered 2s and vice versa, but I have no clue where to start when implementing this.

rough furnace
#

Someone made an X card that can assume a few different ranks but I'm not sure what was involved in that process

static juniper
#

What mod was this?

static juniper
regal wolf
#

well, without putting too much thought into it, theres a calculate_joker function that gets passed arguments and calculates the joker effects.

#

oh wait you can just take a look at something like smeared joker

#

that does what you want, but with suits

static juniper
#

Suits are different though, right? They use the is_suit(suit) function, while ranks use the get_id() function.

regal wolf
#

yes they're different, but you can see how smeared joker works, and apply it to suits

static juniper
#

I'm not sure if I can. The reason Smeared Joker works is that is_suit() returns one value, true or false, based on the required suit. get_id() returns the number corresponding to the id. In theory, if I wanted to have a card count as multiple ranks, I would need to completely rewrite this function and every card that uses it. Unless there's another way, that is.

wintry solar
#

you can hook get_id()

regal wolf
#

^

static juniper
#

But then wouldn't I need to still rewrite all jokers that use it?

zealous glen
#

Because the way to make it work as desired would require rewriting every vanilla Joker, and wouldn't work with modded Jokers

static juniper
#

That's what I thought

zealous glen
#

I did briefly consider an alternative where I make get_id return a custom object which has special comparison operators, but Lua doesn't quite allow it to be used as desired

#

Because IIRC it would only use the custom comparison if both terms were the same custom class

frosty dock
#

yeah the comparisons only work if they have the same exact metatable

zealous glen
#

Maybe you could overwrite the default Lua number class with a class that's almost exactly the same except it has the extra value

#

But that doesn't sound possible

frosty dock
#

that's probably bad

zealous glen
#

Yes

#

tl;dr: It's not possible in a reasonable/good way

#

Actually the solution is to ask thunk to rewrite Balatro in a way that would make it work while also breaking every mod that doesn't update to the new standard, thus forcing modders to update their mods to work with your multirank modded Joker effect

static juniper
#

Cool thanks, will do that /s

zealous glen
#

Conclusion: the easy solution is

#

I'm also amused by the people who said "you totally can just look at Smeared" when ranks work completely different :^)

hushed cradle
#
local new_moon = Ceres.SETTINGS.blinds.enabled and Ceres.SETTINGS.blinds.base_blinds.enabled and SMODS.Blind{
    key = 'new_moon',
    boss_colour = Ceres.C.new_moon,
    dollars = 10, -- default is 5, default showdown is 8
    mult = 2,
    boss = {
        showdown = true
    },
    pos = {
        x = 0,
        y = 0,
    },
    atlas = 'base_blinds',
    discoverd = false or Ceres.discover_all,
    debuff = {
        suit = 'Spades',
        value = 'Ace',
    },
}```
#

this is my code for the blind

#

Ceres.discover_all is true so im not sure why its still not discovered

#

unless it just works differently for blinds

#

im stupid

#

cant spell

#

hate myself

wary crescent
#

Im trying to recreate the visual/sound effects that Ramen makes when you discard cards and the xmult decreases. I'm looking in the game code at Ramen but im not sure where in the code these effects are implemented.

hushed cradle
#

card:calculate joker

#

context.discard or smth similar

#

i think

#
elseif context.discard then
            if self.ability.name == 'Ramen' and not context.blueprint then
                if self.ability.x_mult - self.ability.extra <= 1 then 
                    G.E_MANAGER:add_event(Event({
                        func = function()
                            play_sound('tarot1')
                            self.T.r = -0.2
                            self:juice_up(0.3, 0.4)
                            self.states.drag.is = true
                            self.children.center.pinch.x = true
                            G.E_MANAGER:add_event(Event({trigger = 'after', delay = 0.3, blockable = false,
                                func = function()
                                        G.jokers:remove_card(self)
                                        self:remove()
                                        self = nil
                                    return true; end})) 
                            return true
                        end
                    })) 
                    return {
                        message = localize('k_eaten_ex'),
                        colour = G.C.FILTER
                    }
                else
                    self.ability.x_mult = self.ability.x_mult - self.ability.extra
                    return {
                        delay = 0.2,
                        message = localize{type='variable',key='a_xmult_minus',vars={self.ability.extra}},
                        colour = G.C.RED
                    }
                end
            end```
wary crescent
#

I just meant the actual effects, where they exists within this code you have shown here

hushed cradle
#
return {
  delay = 0.2,
  message = localize{type='variable',key='a_xmult_minus',vars={self.ability.extra}},
  colour = G.C.RED
}```
#

this bit here

#

if you have a joker

#

then in its calculate function you return a message and a colour

#

itll pop up with that message

#

and a little square with the colour

#

as for sounds you can do play_sound()

wary crescent
#

Ok, I see! The thing is, I have this in my Jokers code but its not displaying the effects ?

hushed cradle
#

what does your code look like?

wary crescent
#

I was just making sure i hadnt misunderstood

hushed cradle
#

you can also do card_eval_status_text() to just skip having to return a message

#

because thats what the game does with your message anyway i think

wary crescent
#

I get effects for played 8s but unfortunately no effects for cards played that arent 8s.

hushed cradle
#

hm

shell tangle
#

I mean, it worked fine when I tested it. Is the value not subtracting or what?

wary crescent
#

The value is subtracting correctly but theres no effects displaying when cards that aren't 8 are played

wintry solar
#

what do you mean by effects?

shell tangle
#

I'd assume a message that the xmult went down, right?

wary crescent
#

Yes, I was trying to recreate the Ramen discard effect, theres also a card modded joker named Nachos that has it i was looking at

#

that uses it

zealous glen
#

In the line that reads play_sound

#

But that's when it's eaten

wintry solar
#

I still don't know which part isn't working

wary crescent
#

Theres no visual cue that your xmult is being decreased when a card that isnt an 8 is being played.

#

scored

wintry solar
#

dont you need a card = card in your return?

zealous glen
#

Oh wait

zealous glen
#

also, for the Event in the first part, I think you can replace it with card_eval_status_text

shell tangle
zealous glen
#

Ah the Xmult can be increased in the first if, so not always false

shell tangle
# wintry solar dont you need a `card = card` in your return?

This would explain it, and, my explanation taken from the example joker mod is as follows:

        -- The return value, "card", is set to the variable "card", which is the joker.
        -- Basically, this tells the return value what it's affecting, which if it's the joker itself, it's usually card.
        -- It can be things like card = context.other_card in some cases, so specifying card (return value) = card (variable from function) is required.
wintry solar
#

it doesn't know where to put the message without the card value iirc

zealous glen
#

Is that due to Steamodded or vanilla?

#

Ramen doesn't use it

shell tangle
#

I'd assume a SMODS thing, since we use card for a lot of stuff while the base game uses self a lot of the time.

wintry solar
#

I don't know why Ramen doesn't have it

#

but it's a vanilla thing

#

oh, does the ramen message go on the scoring card?

#

ramen bypasses the card parameter

edgy reef
#

The contexts that have messages possible on both playing cards and jokers needs the specification.

wintry solar
#

Yeah that makes sense from a vanilla point of view. They should probably be patched to use the card parameter though

#

for example, you might want to display a message on a discarded card from a joker effect

#

although I suppose you can just call card_eval_status_text yourself

edgy reef
#

You can do that through manually calling card_eval_status_text

#

This is how Faceless Joker does it.

#

Although I don't think it's needed for it to

wintry solar
#

This is the main reason I don't think recommending new modders to read the source code is a good idea 🙃

#

it's so jank and inconsistent within itself

regal wolf
#

😭

#

my first mods reflect that very well

#

💀

hushed cradle
#

how do i add the lucky info thing to a joker ive got?

#
loc_vars = function(self, info_queue, card)
        info_queue[#info_queue+1] = {set = 'Enhanced', key = 'm_lucky'}
    end,```
#

this is not how

wintry solar
#

G.P_CENTERS['m_lucky'] should do it

hushed cradle
#

is that instead of the = {set thing or the key?

wintry solar
#

the set thing

hushed cradle
#

cool thanks

#

yep it works

#

neat little joker i think

#

art could do with a bit of work tho

static juniper
#

I actually had a joker idea that was very similar to this

hushed cradle
#

beat you to it

hushed cradle
#

i didnt really know what to do there

static juniper
#

I really need to learn how to make passable art

hushed cradle
#

an easy way to do art is take a ref image and just scale it down to the size of a joker lol

#

then try go over the outline if you can make it out

#

might be easier in 2x

zealous glen
hushed cradle
#
calculate = function(self, card, context)
        if context.cardarea == G.jokers and context.before and not context.blueprint then
            local odds = {}
            for k, v in ipairs(context.scoring_hand) do
                if (v:get_id() <= 10 and 
                v:get_id() >= 0 and
                v:get_id()%2 == 1) or
                v:get_id() == 14
                then
                    odds[#odds+1] = v
                    v:set_ability(G.P_CENTERS.m_lucky, nil, true)
                    G.E_MANAGER:add_event(Event({
                        func = function()
                            v:juice_up()
                            return true
                        end
                    })) 
                end
            end
            if #odds > 0 then 
                return {
                    message = 'Lucky',
                    colour = G.C.RED,
                    card = card,
                }
            end
        end
    end,```
#

ty

zealous glen
#

Also obligatory slot machine triple 7s Joker

#

Can Judgement and Soul create cards with editions?

static juniper
#

Yes, iirc

hushed cradle
static juniper
#

I remember getting get Holo Legendary once

hushed cradle
zealous glen
#

I'll hope that changing Editions won't break anything then

hushed cradle
static juniper
hushed cradle
#

ill try that but im pretty sure card is the joker itself

#

nah didnt change anything

static juniper
#

It's just the message not working, the cards are turning lucky, right?

hushed cradle
#

yeah

static juniper
#

I don't think you need the context.cardarea == G.jokers condition, but I don't know if that'll change anything. Try it anyway though.

hushed cradle
#

ill try that

zealous glen
#

I don't think every context automatically handles messages

#

Although I think context.before should

hushed cradle
#

the code is literally ripped from the games code for midas mask lol

zealous glen
#

Ah wait

#

I think the message needs to be a table

zealous glen
#

localize modifies the table in-place

#

I think

hushed cradle
#

oh right yeah

#

mb

#

wait i have other jokers that dont do that

near ivy
#

or at least the white part of it

hushed cradle
#

i havent got room for it with the joker writing sadly

near ivy
hushed cradle
#

i plan on redoing the art tho

near ivy
#

yay!

hushed cradle
#

or touching it up a bit

near ivy
#

that would also work

hushed cradle
#

i didnt even know most jokers had white borders until earlier

#

even after 900 hours

zealous glen
#

All of them except the ones that aren't cards

#

Thunk was inconsistent about it then I think they redid them

#

during development

static juniper
#

I'm trying to make a joker that shuffles all jokers before the spin hand, but this effect, for some reason, only applies after the spin hand.

    G.E_MANAGER:add_event(Event({ trigger = 'before', delay = 0.2, func = function() 
        G.E_MANAGER:add_event(Event({ func = function() G.jokers:shuffle('aajk'); play_sound('cardSlide1', 0.85);return true end })) 
        delay(0.15)
        G.E_MANAGER:add_event(Event({ func = function() G.jokers:shuffle('aajk'); play_sound('cardSlide1', 1.15);return true end })) 
        delay(0.15)
        G.E_MANAGER:add_event(Event({ func = function() G.jokers:shuffle('aajk'); play_sound('cardSlide1', 1);return true end })) 
        delay(0.5)
    return true end }))
end```
hushed cradle
#

might be because its an event

static juniper
wintry solar
#

It’s setting events inside the event

static juniper
#

Idk, I just ripped the code from Amber Acorn. If I take the events out, would it work?

static juniper
#

Shoot, I meant hand

zealous glen
#

but yes it's 99% event managing

hushed cradle
static juniper
#

I'm too used to Luck Be a Landlord modding lol

hushed cradle
#

i dont know how any of it works really

wintry solar
#

Yeah I don’t think you can use events for this

#

From my brief understanding of events

zealous glen
#

Events mostly happen in the order they're created when they're handled, so by putting this in an Event it delays the execution until Events are handled

wintry solar
#

And by nesting events you actually throw them to the end of the event queue

hushed cradle
#

still doesnt work

#

also

#

smth weird

#

it feels like the card scores really quickly after it gets turned lucky

#

like the chips start counting before it finishes juicing

#

well midas mask isnt displaying a message either so i think i broke smth

zealous glen
#

are you in the main SMODS branch?

hushed cradle
#

am i what

static juniper
zealous glen
#

Steamodded

hushed cradle
#

wdym am i in the main branch

zealous glen
#

git

hushed cradle
#

im not in it no

wintry solar
zealous glen
#

Try updating the main branch and checking out to it

hushed cradle
#

im pretty sure i have the latest version

static juniper
zealous glen
wintry solar
#

You need to remove all the events

hushed cradle
#

ive got a bunch of patches that mess with the eval play function so its smth about that probably

zealous glen
#

That's very outdated

hushed cradle
#

"1.0.0-ALPHA-0809b-STEAMODDED"

#

so thats not the latest?

wintry solar
#

No

hushed cradle
#

damn

wintry solar
#

I don’t think that’s the problem though

static juniper
wintry solar
#

That’s more likely

hushed cradle
#

💀

#

who knows

zealous glen
static juniper
#

I'm so confused why this exists

wintry solar
hushed cradle
zealous glen
hushed cradle
#

its a whole bunch of letters and numbers

wintry solar
zealous glen
#

I think in an ideal world everything in a game would be handled by an Event manager

hushed cradle
#

why cant the versions just count up

zealous glen
#

Rather than being hardcoded

wintry solar
#

Those are commit ids

zealous glen
#

And the Event manager would look at the game state and manager Events

hushed cradle
#

yeah but i just donwloaded "1.0.0~ALPHA-0813a-STEAMODDED"

#

oh nvm i cant count for shit

zealous glen
#

Basically, doing an action can create one or more Events, each of which can create one or more Events, and they need to occur at the right time

wintry solar
#

The versions are date strings btw, so you can tell easily how old your version is

zealous glen
#

Balatro doesn't do this, but a robust Event handling system would also support listeners

#

Well, I guess maybe the way Balatro does it is good enough

hushed cradle
zealous glen
#

But you need to hardcode calls to things that can be listening

#

And you need to iterate over everything

#

Rather than only the things listening for a specific event

wintry solar
#

Month/day

hushed cradle
zealous glen
#

I think good approximations of Event systems in games are MTG's Stack, Hearthstone's Queue, and Marvel Snap's Nested Queue

wintry solar
#

That’s the proper way to write dates so they increase though

hushed cradle
#

okay thats actually a good point

static juniper
#

Y/M/D date format is clearly superior

hushed cradle
#

what

#

no

zealous glen
wintry solar
#

I think you can actually make listeners with balatros event manager

#

But I’d have to look into it more

zealous glen
#

I think D/M/Y is good for human use, while Y/M/D is good for machine use

zealous glen
wintry solar
#

You can make your own queues

zealous glen
#

Like you can't look at an Event object and deduce what contexts it touches

#

But vanilla doesn't support them

#

To be clearer, vanilla Jokers don't use them, etc.

wintry solar
#

Vanilla doesn’t do a lot of things it’s able to tbf

sharp phoenix
#

Can anyone point me in the right direction with a good example of code to add a new voucher, i've looked through the source code and some other mods and i'm a little lost right now

zealous glen
#

Betmma or Bunco I'd guess

#

I'd lean towards Bunco but Betmma has added a lot

sharp phoenix
#

Thanks I'll give them a look

zealous glen
#

Does anyone know how to add Perishable/Rental tooltips to Jokers?

#
loc_vars = function(self, info_queue, card)
  info_queue[#info_queue+1] = {key = 'e_negative', set = 'Edition', config = {extra = 1}}
  info_queue[#info_queue+1] = {key = 'perishable', set = 'Other'}
  info_queue[#info_queue+1] = {key = 'rental', set = 'Other'}
end

didn't work

shell tangle
zealous glen
#

The actual error is this (well, it would read "Perishable" instead of "Rental" but for this in particular I commented out the line with Perishable)

#

Okay I think I found how to do it but I'm not sure I understand why

shell tangle
#

What did you use?

zealous glen
#

I haven't tested but this is how vanilla has them

#

Compare 'negative' to 'perishable'

shell tangle
#

Weird. Hope it works, since I was looking through the init_item_prototypes() and I don't see anything about a stickers.

zealous glen
#

It works but Perishable shows the number of rounds remaining

#

Not sure I can natively disable it or if I'd need a custom tooltip

shell tangle
#

Probably a custom tooltip, why would it need to be disabled?

zealous glen
shell tangle
#

Makes sense, and a neat idea. I think the best way to go about it would be to make it custom, because I don't think there's a way to modify it without also modifying it for every other case.

zealous glen
#

Oh I got it wrong originally because I can't read

#

Anyways, yeah, I guess it'd need to be an original one

#

Although I could have a fake tooltip and copy the original tooltip sans the last line

#

Assuming the structure is the same across all languages

#

maybe

edgy reef
zealous glen
#

Speaking of which, I'll probably need to make a custom pool to select Jokers from

edgy reef
#

That's been a suggestion for a while but tbh they'll prob be baked into the labels idea which isn't currently planned as a SMODS 1.0 feature.

zealous glen
#

I think querying a database of objects for a pool is a good generic feature

zealous glen
#

And this is what it looks like when the counter advances

#

This is the effect, by the way. The text needs some work though

#

It's probably simpler to just make it hit the Joker to the right (if it's non-Negative)

shell tangle
#

I've figured out the way for custom tooltips, if you're still stuck on the info_queue stuff.

zealous glen
#

I mean I've made custom tooltips myself

shell tangle
#

Fair enough, I haven't before, so I'm only just now learning, since it does seem useful.

zealous glen
#

Maybe you have a better method

#

The Credits are very hacky

#

I basically have a fake tooltip which I overwrite into the tooltip I want

shell tangle
#

It's literally just throwing in a new line into localization, and then calling that as an info_queue. No idea how you changed the background color of the credits at all, and I am curious as to how.

edgy reef
#

It’s just specifying different colors than the normal tooltip.

zealous glen
edgy reef
#

iirc

zealous glen
zealous glen
#

🌽

#

Also I learned today how thunk checks if a card is in the collection or not, and it's much better than what I was doing (O(1) instead of O(n))

#

although I haven't yet updated anything to use their method

zealous glen
# zealous glen 2.

So I give a name to the tooltip which is a code that is unlikely to show up otherwise, then I replace that with the actual name localize('k_vic_credits'). The rest is taking the name and the description nodes and putting them inside an appropriate number of row UIElement objects that look like I want

#

It looks something like

#
  • Row
  • Row
    • Text
  • Row
    • Description nodes
shell tangle
#

Impressive, and, unfortunately complex for a simple gray background, but, yeah, figuring out that method could definitely let you do some other fun stuff.

sharp phoenix
#

So I've managed to create a new voucher which want to have as the next in line to Telescope - Observatory.

the voucher is created and purchasable and i think dependant on Observatory being purchased.

However I cannot figure out how to use the Card:Calculate_joker correctly to get it to use the new mult variable i've declared

any assistance greatly appreciated

zealous glen
#

also check out Stheno for the "fun stuff"

zealous glen
shell tangle
#

The code is taken directly from observatory, I'm looking through it myself to try to figure out why it wouldn't.

zealous glen
#

I mean I thought it did but their issue was having to hardcode the values

shell tangle
#

Also, I don't think it's using the prefix. The key of the voucher should actually be v_prefix_jameswebb, rather than just jameswebb, prefix is the first four letters of your mod ID in lowercase if you haven't specified a prefix, I believe, might be the mod name instead.

#

So, it'd be, if G.GAME.used_vouchers.v_prefix_jameswebb, and Xmult_mod should be G.P_CENTERS.v_prefix_jameswebb.config.extra, I think.

hushed cradle
#

any good example mods for adding enhancements?

mellow sable
#

Cryptid or Lobcorp

hushed cradle
#

thanks

zealous glen
#

L Corp has Enhancements?

sharp phoenix
mellow sable
#

Sorry I thought you said achievements

#

Then cryptid and ortalab (I think they have those?)

#

@hushed cradle

hushed cradle
#

thanks i was really confused, looking through cryptid lol

#

do i have to

#

hook

#

to smth to calculate it

mellow sable
#

probably not for basic effects

hushed cradle
#

its just theres no documentation so idk any of the methods

#

with cryptids echo it looks like you used a hook to calculate when it goes off

mellow sable
#

We did

#

You’d have to look at the game_object code but I think some basic effects work without hooks

hushed cradle
#

alright thanks

#

just realised why all my jokers were broken too

#

thank god

#

do you see how the makima joker goes again again again before the card scores

#

then hanging chad gets retriggered all at once

#

for the smods enhancement calc function is it similar to calc joker?

#

with context.other_card for example

hushed cradle
#

somehow my code worked perfectly

#

nvm it doesnt lmao

zealous glen
#

What do you think?

hushed cradle
#

looks pretty cool

#

not too broken either

#

does anyone know how i can make an event trigger after each card in the hand has been scored

zealous glen
#

Create the event after each card in hand has been scored

hushed cradle
#

its for an enhancement tho

#

atm im creating it in the calc function

#

just gonna patch instead

#

art is gonna stay like that its just so i could easily tell them apart

hushed cradle
#

anyone know how i fix the name thing

mellow sable
#

for some reason this UI ended up looking way better than I thought it would

hallow forge
#

a UI guide somewhere would be amazing

mellow sable
#

i actually used one to help me, lemme pull it up

hallow forge
#

ooooh

#

thanks math

mellow sable
#

idk why but the transparent buttons end up looking really nice with how the rest of the UI is set up

#

pretty much out of the box

mellow sable
#

it still took me an hour or two to figure out enough how to use it

#

but it works now i guess

#

I also somehow made this part before that guide, but this part is much easier

mellow sable
#

i still have to code the replay info tab later, i'll probably take inspiration from the win screen for what I put on there

#

also the code for it is really ugly

#

like probably much worse than thunk

#

but honestly in this case it's good to be like that, makes it harder for cheaters to mess with it

crisp coral
hushed cradle
#

glass but with less risk

glass scaffold
#

rarely do i agree with things being worse but this is a case where it's objective.

hushed cradle
#

it was 1 in 5

hushed cradle
queen canopy
#

Hi! Been having a couple issues getting my Joker's data (scaling mult) to save between opening and closing the game, I've added the code here I'd really appreciate any help 🙏 (for context this is just the code relevant to the Joker, not the entire mod)

hushed cradle
#

instead of self.config you want to use card.ability

#

in the calculate function

#

imagine the smods.joker is like a blueprint for the joker

#

so everytime you find it in game

#

or its created in game

#

its basically a copy of that template

#

at the moment youre modifying the template

#

so any of the same joker you get in the future will also be scaled up already

#

by modifying card.ability youre changing that one joker specifically

#

instead of the template for the joker

#

and when you exit a run the game saves each cards ability

#

so itll then save the scaled mult

#
SMODS.Joker{
    key = "five_leaf_clover",
    name = "Five-Leaf Clover",
    rarity = 1,
    discovered = true,
    pos = {x = 0, y = 0},
    cost = 5,
    blueprint_compat = true,

    config = {name = "Five-Leaf Clover", extra = {mult_growth = 5, plus_mult = 0}},

    loc_txt = {
        name = "Five-Leaf Clover",
        text = {
            "This Joker gains {C:red}+#1#{} Mult every",
            "time a {C:attention}Lucky{} card {C:green}successfully{}",
            "triggers",
            "{C:inactive}(Currently {C:red}+#2# {C:inactive}Mult)"
        }
    },

    atlas = "five_leaf_clover",

    loc_vars = function(self, info_queue, center)
return {vars = {card.ability.extra.mult_growth, card.ability.extra.plus_mult}}
end,

    calculate = function(self, card, context)
        if context.individual and context.other_card.lucky_trigger and not context.blueprint then
            card.ability.extra.plus_mult = card.ability.extra.plus_mult + card.ability.extra.mult_growth
            return {
                extra = {focus = card, message = localize('k_upgrade_ex'), colour = G.C.MULT},
                card = card
            }
        elseif context.cardarea == G.jokers and not context.before and not context.after then
            return {
                message = localize{type='variable',key='a_mult',vars={card.ability.extra.plus_mult}},
                mult_mod = card.ability.extra.plus_mult
            }
        end
    end```
#

that should work

#

also had to change it card.ability in loc vars because otherwise itll say currently +0 mult

#

as the template is at +0

crisp coral
hushed cradle
#

damn

#

nws

wintry solar
#

I think it’s a smods issue that’s fixed in a pr somewhere

queen canopy
hushed cradle
#

happy to help

#

especially josuke

hushed cradle
dim nimbus
#

how do i get this event to only destroy one card and not the full hand? or do i need to use a different function entirely

            G.E_MANAGER:add_event(Event({
            trigger = 'before',
            delay = 0.2,
            func = function() 
                for i=#G.play.cards, 1, -1 do
                local card = G.play.cards[i]
                    if card.ability.name == 'Glass Card' then 
                        card:shatter()
                    else
                    card:start_dissolve(nil, i == #G.play.cards)
                    end
                end
            return true end}))
wintry solar
#

Is it supposed to just destroy the first card?

dim nimbus
#

i can post the whole thing, its supposed to have a 1/8 chance of destroying a random card, and while it does activate 1/8 times when it does it destroys the whole hand

#
SMODS.Joker {  -- Mini Hakkero
    key = 'minihakkero',
    loc_txt = {
        name = 'Mini Hakkero',
        text = {'{C:green}#1# in #2#{} chance to {C:attention}destroy{}',
        'a random scored card.',
        'Permanently increase payout by {C:money}$#3#{}',
        'when card is destroyed.',
        '{C:inactive}(Currently earn{} {C:money}$#4#{} {C:inactive}at end of round.){}' }
    },
    config = {extra = {odds = 8, money_mod = 1, money = 1}},
    rarity = 3,
    pos = {x = 2,y = 0},
    atlas = 'jokeratlas',
    cost = 9,
    unlocked = true,
    discovered = true,
    blueprint_compat = false,
    loc_vars = function(self, info_queue, center)
        return {vars = {G.GAME.probabilities.normal, center.ability.extra.odds, center.ability.extra.money_mod, center.ability.extra.money}}
    end,
    calculate = function(self, card, context)
        if context.individual and pseudorandom('minihakkero') < G.GAME.probabilities.normal/self.config.extra.odds and context.cardarea == G.play and not context.blueprint and not context.repetition then
            card.ability.extra.money = card.ability.extra.money + card.ability.extra.money_mod
            card_eval_status_text(card, 'extra', nil, nil, nil, {message = localize('k_upgrade_ex')})
            G.E_MANAGER:add_event(Event({
            trigger = 'before',
            delay = 0.2,
            func = function() 
                for i=#G.play.cards, 1, -1 do
                local card = G.play.cards[i]
                    if card.ability.name == 'Glass Card' then 
                        card:shatter()
                    else
                    card:start_dissolve(nil, i == #G.play.cards)
                    end
                end
            return true end}))
        end
    end,
    calc_dollar_bonus = function(self, card)
        if card.ability.extra.money > 0 then
            return card.ability.extra.money
        end
    end
}
wintry solar
#

That’s because you loop through the entire hand and destroy them all

#

You’d need to choose a random card from the hand instead of using a loop

dim nimbus
#

for that i would need to use pseudorandom_element?

wintry solar
#

It’s something like that yeah

#

Might be pseudoelement

dim nimbus
#

hmmmm

#

well it works, sort of

wintry solar
#

What do you mean?

dim nimbus
#

it does destroy a random card in hand, but that card is irrelevant to whichever card upgrades the return on the joker

#

it does function perfectly fine though, or at least doesnt break

wintry solar
#

Oh is it supposed to only upgrade on destroy?

dim nimbus
#

yep

#

destroys a card and then upgrades

#

probably i can nest it in an if statement and itll work?

wintry solar
#

You’ve already got a selected card before this event

#

So just use that one

dim nimbus
#

hmm?

wintry solar
#

I think it’s context.other_card

opal spade
dim nimbus
#

so instead of context.individual use context.other_card

wintry solar
#

No

#

Use the other card to destroy

#

Instead of a random one

dim nimbus
#

now it destroys the card to the right of it whenever it activates

wintry solar
#

That’s a timing error then I think

hushed cradle
dim nimbus
#

its not entirely how i want it to end up looking, but as far as i can tell it works as intended

wintry solar
#

If you can video it and tell me what it should be like I can probably help further

dim nimbus
#

maybe some other time, it does work and thats whats most important right now

wintry solar
#

No no no aesthetics are always the most important

dim nimbus
#

i guess we can disagree on that lol
probably some other time, i want to start work on the joker art

wintry solar
#

Who cares if mechanics work as long as it’s pretty

dim nimbus
#

very true

#

thank you though!

hushed cradle
#
local reversed_fool = Ceres.SETTINGS.consumables.enabled and Ceres.SETTINGS.consumables.reversed_tarots.enabled and SMODS.Consumable{
    key = 'reversed_fool',
    set = 'reversed_tarot',
    config = {},
    atlas = 'reversed_tarots',
    pos = {
        x = 0,
        y = 0,
    },
    discovered = false or Ceres.SETTINGS.misc.discover_all.enabled,

    loc_vars = function(self, info_queue, card)
        local fool_c = G.GAME.last_reversed_tarot and G.P_CENTERS[G.GAME.last_reversed_tarot] or nil
        local last_reversed_tarot = fool_c and localize{type = 'name_text', key = fool_c.key, set = fool_c.set} or localize('k_none')
        local colour = (not fool_c or fool_c.name == 'The Fool') and G.C.RED or G.C.GREEN
        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 = ' '..last_reversed_tarot..' ', colour = G.C.UI.TEXT_LIGHT, scale = 0.3, shadow = true}},
                }}
            }}
        }
        if not (not fool_c or fool_c.name == 'The Fool Reversal') then
            info_queue[#info_queue+1] = fool_c
        end
        return {main_end = main_end}
    end,

    can_use = function(self, card)
        if (#G.consumeables.cards < G.consumeables.config.card_limit or card.area == G.consumeables) 
        and G.GAME.last_reversed_tarot and G.GAME.last_reversed_tarot ~= 'c_cere_reversed_fool' then
            return true
        else
            return false
        end
    end,

    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')
                local card = create_card('reversed_tarot', G.consumeables, nil, nil, nil, nil, G.GAME.last_reversed_tarot, 'reversed_fool')
                card:add_to_deck()
                G.consumeables:emplace(card)
                card:juice_up(0.3, 0.5)
            end
            return true end }))
        delay(0.6)
    end
}```
#

i have this code for a consumable

#

similar to the fool

#

but for this new consumable type i added

#

it works perfectly

#

even saves when you restart the game and everything

#

but for some reason when i hover over the card in the collection mid run the game just closes down

#

doesnt even give me an error or anything

#

just closes straight up

#

its kinda unsettling

#

okay it seems to just work now

#

not sure what was happening

#

i didnt even change anything

hushed cradle
#
local cobalt = Ceres.SETTINGS.enhancements.enabled and Ceres.SETTINGS.enhancements.illusion.enabled and SMODS.Enhancement{
    key = 'cobalt',
    pos = {
        x = 1,
        y = 0,
    },
    atlas = 'enhancement_atlas',
    config = {
        extra = 2,
    },

    loc_vars = function(self, info_queue)
        return {vars = {self.config.extra}}
    end,

    calculate = function(self, context, effect)
        if context.area == G.hand then
            return {
                message = 'X' .. self.config.extra,
                x_chips = self.config.extra,
                colour = G.C.CHIPS,
            }
        end
    end
}```
#

tryna get this enhancement to give x2 chips when held in hand

#

still not really sure how the calculate works for enhancements

zealous glen
#

I don’t think there’s any AFAIK

#

Unless they’ve since been added

wintry solar
#

it does

#

I don't know why people think ti doesnt

#

it has more robust calculation than editions

zealous glen
#

Last time I asked I was told none had been added

#

And so when I last checked

wintry solar
#

~I'm pretty sure they were merged with it 🤷‍♂️

zealous glen
#

That’s not what ejwu said when I asked

#

And they made it AFAIK

wintry solar
#

it's not complete, but it is there

hushed cradle
#

sooo, how do i make the card give x2 chips in hand lol

#

what do i return

wintry solar
#

do you have an x_chips module?

hushed cradle
#

yeah

wintry solar
#

should just be however x chips would normally work

#

I don't use x chips though

hushed cradle
#

for jokers its just

#
return {
                message = 'X' .. self.config.extra,
                x_chips = self.config.extra,
                colour = G.C.CHIPS,
            }```
zealous glen
#

Maybe the module doesn’t add the functionality to cards

wintry solar
#

but playing cards dont hit the same evaluationa s jokers

#

try xchips_mod

#

or x_chips_mod

hushed cradle
#

Xchip_mod

#

i will

zealous glen
#

I think those need to be collected per context

#

Does Xmult work?

hushed cradle
#

havent tried it admittedly

#

Xchip_mod doesnt, ill try x_mult

#

x_mult doesnt work

#

or Xmult_mod

wintry solar
#

is it actually hitting that block?

hushed cradle
#

yeah i had a print statement

#

oh do i need a card

#

that didnt seem to work

#

hmm

wintry solar
#

I don't remember quite how the calculate was patched in, and I don't have the code infront of me rn

hushed cradle
#

nws, ill figure it out eventually i think

wintry solar
#

take a look at your lovely dumps, you want state_events and common_events iirc

#

for evaluate_play and eval_card

hushed cradle
#

will do

#
if center.set == 'Enhanced' and center.calculate and type(center.calculate) == 'function' then 
            center:calculate(context, ret)
            enhancement_calculated = true
        end```
#

not really sure how im supposed to do anything tho

#

it doesnt take anything that i return it looks like

wintry solar
#

oh, try adding it to the effect table instead of returning

hushed cradle
#
calculate = function(self, context, effect)
        if context.area == G.hand then
            effect.x_chips = self.config.extra
            effect.message = 'X' .. self.config.extra
            effect.colour = G.C.CHIPS
            effect.card = context
            return effect
        end
    end```
#

did that but still nothing

wintry solar
#

so do like, lua effect.x_chips = { bla bla bla}

hushed cradle
#

unless thats formatted wrong?

#

ah

#

ill try that

#

for some reason i still struggle wrapping my head round editing tables like that

wintry solar
hushed cradle
#

it doesnt look like it

#

it just calls center:calc

wintry solar
#

not on the enhanccement ones, just the standard ones

hushed cradle
#

yeah

#
local h_mult = card:get_chip_h_mult()
        if h_mult > 0 then 
            ret.h_mult = h_mult
        end```
#

for example

wintry solar
#

yeah, so I think the effect.x_chips thing should work

hushed cradle
#

i got bad news

#
calculate = function(self, context, effect)
        if context.area == G.hand then
            effect.x_chips = {
                message = 'X' .. self.config.extra,
                x_chips = self.config.extra,
                colour = G.C.CHIPS,
                card = context,
            }
        end
    end```
#

unless im dumb

#

wait

#

maybe

#

actually i dont even know anymore

#
calculate = function(self, context, effect)
        if context.area == G.hand then
            effect.x_chips = {self.config.extra}
        end
    end```
#

not that either

#

def hitting that block

wintry solar
#

does x_mult work?

hushed cradle
#

ill try adding that to effect

#

nope

wintry solar
#

huh

#

can you throw a print(tprint(effect)) before and after where you change it?

hushed cradle
#

sure

#

do you want me to patch that in or just put it in my calc func

wintry solar
#

in your calc

hushed cradle
#

alright

wintry solar
#

that's not tprint?

hushed cradle
#

what is tprint?

wintry solar
#

it prints the table properly 😆

hushed cradle
#

oh mb lol

#

is that built in to lua?

opal spade
#

theres a function to print the table properly?

wintry solar
#

I think it's a thunk function

hushed cradle
#

oh wth

#

this is insane

wintry solar
#

it's very useful

opal spade
#

my world absolutely blown

wintry solar
#

yeah that's what I used to do

#

now I live for tprint

hushed cradle
#

💀

wintry solar
#

can you do a context one too out of interest?

hushed cradle
#

sure

wintry solar
#

becuase that looks an awful lot like the context table 💀

hushed cradle
#

sorry had to take a phone call

#

uh lemme print the other thing

#

oh

#

uh

#

isnt self supposed to be a darker blue?

#

or am i being dumb

#

lemme print the thing sorry

wintry solar
#

okay yeah I thought that might be it

#

change it to calculate = function(_, self, context, effect)

hushed cradle
#

okay lol

wary crescent
#

Redoing my 8s joker i had issues with yesterday, here is the Joker text: "This Joker Gains {X:mult,C:white}X#1#{} Mult",
"for each scored or discarded {C:attention}8{}.",
"{C:inactive}(Currently {X:mult,C:white}X#2#{C:inactive} Mult)",

hushed cradle
#

and im very dumb

wintry solar
#

oh?

hushed cradle
#

im using mathisfun's retrigger api thing and it overwrites the eval card func lol

wintry solar
#

oh

hushed cradle
#

yea

wintry solar
#

it fully rewrites it?

hushed cradle
#

yea

wintry solar
#

🙃

hushed cradle
#

yeah sorry for putting you through this

wary crescent
#

When y'all get a sec 🙂 how is scored and discarded written in the return ,vars = section? Bc it is connected to the same Xmult bonus number? "This Joker Gains {X:mult,C:white}X#1#{} Mult",
"for each scored or discarded {C:attention}8{}.",
"{C:inactive}(Currently {X:mult,C:white}X#2#{C:inactive} Mult)"

wintry solar
#

are you sure it overwrites it?

#

I don't see anything in the PR that changes eval_card

hushed cradle
#

mine might be outdated tbf

#

i downloaded it as soon as it got put on git lol

wintry solar
#

oh yeah that doesnt look like the one thats as a PR

wary crescent
#

Under loc vars, i am trying to set it up to give the same Xmult bonus for both scoring OR discarding a specific card rank

#

This Joker Gains {X:mult,C:white}X#1#{} Mult",
"for each scored or discarded {C:attention}8{}.",

hushed cradle
#
loc_vars = function()
  return {vars = {card.ability.increase or decrease or whatever you want}}
end```
wary crescent
#

So this is what im learning from: "loc_vars gives your loc_text variables to work with, in the format of #n#, n being the variable in order.
-- #1# is the first variable in vars, #2# the second, #3# the third, and so on." My #1# has two possible conditions and i wasnt sure how to go about putting it in the return

hushed cradle
#

yeah #1# and #2# etc are variables

#

you pass them into the description by returning {vars = {}}

#

where each variable is at the index or the corresponding number like you said

#

in your loc txt you can also do

#

'text blah blah #1#, and also #1#'

wintry solar
#

What do you mean by it has two possible conditions?

wary crescent
#

Scoring and discarding 8s give Xmult

#

its tied to the same variable

wintry solar
#

then whats the problem?

wary crescent
#

I wasnt sure if/how you could put multiple descriptions or functions under the same variable. Usually each var only has 1 ,

hushed cradle
wintry solar
#

oh I see, yeah you can use them as much as you like

wary crescent
hushed cradle
#

im done with this card

wintry solar
#

does it work now?

hushed cradle
#

no 😭

#

ive just had enough for the time being

wintry solar
#

I'll grab my dev machine and look at some of the ones I've got working

hushed cradle
#

i would greatly appreciate that 🙏

opal spade
#

is there a thread or a link to documentation of how to format uibox stuff properly

hushed cradle
#

your best bet is probably start praying

opal spade
#

i've already dabbled in ui stuff before, but it was through trial and error and i'd like to have more understanding of what im doing

wintry solar
#

I have a semi complete guide in here somewhere

opal spade
wintry solar
#

god this enhancement calc is kinda scuffed

#

it's only working because it gets caught by the joker calculation 🙃

#

I think I fixed it

#

there we go, thrown it in a PR for aure to look at later on 🙂

tidal edge
#

what code creates a specific joker again

wary crescent
#

How would you perform a card id check for a specific rank discarded. Like Hit the Road

regal wolf
wary crescent
#

yes plz

regal wolf
#
function Card:calculate_joker(context)
...
  elseif context.discard then            
    if self.ability.name == 'Hit the Road' and not context.other_card.debuff and context.other_card:get_id() == 11 and not context.blueprint then
      self.ability.x_mult = self.ability.x_mult + self.ability.extra
      return {
        message = localize{type='variable',key='a_xmult',vars={self.ability.x_mult}},
        colour = G.C.RED,
        delay = 0.45, 
        card = self
      }
    end
  ...
  end
end
wary crescent
#

I find it much easier to understand modded jokers than vanilla

limpid flint
regal wolf
#

formatted it for you, incase that helps at all.

wary crescent
#

yes

#

ty

wintry solar
#

ignore that part

wary crescent
#

ok

opal spade
#

if it says self. in the vanilla code then it should be card. in the equivalent smods function

wary crescent
#

ok I understand

wintry solar
#

but also anything that references the name is irrelevant in modded objects

sharp phoenix
#

Hey can anyone point me in the direction of a good tutorial on how to integrate GitHub into Visual Studio code, I'm a bit of a GitHub novice so not 100% how it all works either

wintry solar
#

vscode has git integration built in

limpid flint
#

Oh yeah I've seen it on the left panel

#

Never used it tho

sharp phoenix
sharp phoenix
wintry solar
#

I'm sure there'll be some good tutorials for it somewhere

limpid flint
#

I use vscode simply for the text editor

regal wolf
#

where ever possible try to optimize your work flow to not use a mouse haha

#

you can use git directly in a terminal from vsc, or you could open up your own git bash console specifically for git stuff

#

git lense is a good add on for vsc, esp if ur collaborating with others

#

ultimately the best workflow for you is whatever you are used to, and works best for you

sharp phoenix
regal wolf
#

Ok, let me dm you my personal style guide, and some a git guide i put together for contributors for another of my projects

hushed cradle
#

ik this wasnt done specifically for me but thank you

wary crescent
#

Would someone mind looking at my Joker again? Ive completely redesigned its effects and it is functioning as intended except im not getting a display in game for mult being upgraded OR a visual for when mult it is applied to my score.

wintry solar
#

The score one is because you have two messages in your return, I assume you get chips one?

wary crescent
#

Yes

wintry solar
#

You’ll need to call the mult message yourself

wary crescent
#

How do i do that

wintry solar
#

Use card_eval_status_text

maiden phoenix
#

Yea there can only be one message variable inside a return

wary crescent
#

ok i see

wintry solar
#

I’m not sure why the other message doesn’t work right now

#

But it’s probably something silly like the place it returns to doesn’t handle a message for you

#

So you could just throw your own in there instead too

wary crescent
#

Eh, can you show me how to do this?

#

sorry

opal spade
#

card_eval_status_text(card, eval_type, amt, percent, dir, extra)

#

you can enter nil instead of some of those to make it use the default option

#

extra is a table {colour = G.C.RED} and other stuff (lookin at the function rn)

#

eval_type can be 'debuff' 'chips' 'mult' 'x_mult' 'dollars' 'extra'

#

and a bunch of others

#

to change the text you need to make sure extra has message

#

card_eval_status_text is at functions/common_events.lua if you want to look at it

wary crescent
#

ty sm

opal spade
#

im currently losing my mind bcos im doing ui code and it actually works??

crisp coral
#

archipelagooooooo

opal spade
opal spade
crisp coral
#

it's the same as the mod name itself i think?

#

not sure if you can override that

night pagoda
#

How to apply modded sticker?

rough furnace
#

Honestly archipelago sounds fun to program

opal spade
#

stable rn but still missing challenges

opal spade
frosty dock
#

that's an artifact from before I added that smh

rough furnace
#

Does archipelago have some kind of in game chat?

#

Well anyways I was thinking I could make an aoi for mods to open the DebugPlus console in a special mode where they have full control of what happens when commands are run

#

So you could make it a chat box

#

Or I could make an evsk console

night pagoda
opal spade
#

all of the data the player needs is given to them either via ui or the love.print function

#

with love.print functions being phased out probably this update because they only use the default font

hushed cradle
#

is there a thread for archipelago?? dont know what it is but sounds cool lol

opal spade
#

there is but its inactive because most of the activity is on the archipelago after dark server (balatro cant be on the main server because pegi)

dim nimbus
opal spade
#

nop

#

at least as far as i know

dim nimbus
#

lol, looks like theyre going back and forth on it

#

the most unenthusiastic of battles. rating battles

hushed cradle
#

i know about 3 of those words

#

isnt pegi the guy that says pegi 18

hushed cradle
#
if context.repetition_only then
        if card.ability.set == 'Enhanced' and center.calculate and type(center.calculate) == 'function' then 
            center:calculate(context, ret)
            enhancement_calculated = true
        end
        local seals = card:calculate_seal(context) or {repetitions = 0}
        if seals then
            ret.seals = seals
        end
        return ret
    end```
#

had to tweak the eval_card a bit tho

#

apparently if it was calculating repetitions only then it was returning nothing because there are no seals

wintry solar
#

no that's the wrong block

hushed cradle
#

and then in eval play it tries a for loop for effects.seals.repetitions

#

so its not working?

wintry solar
#

thats the repetition bit

#

gimme 5

hushed cradle
#

it calculates repetition only in eval play anyway i think

#

i might be wrong tho

#
if reps[j] == 1 then 
                        --Check for hand doubling

                        --From Red seal
                        local eval = eval_card(G.hand.cards[i], {repetition_only = true,cardarea = G.hand, full_hand = G.play.cards, scoring_hand = scoring_hand, scoring_name = text, poker_hands = poker_hands, repetition = true, card_effects = effects})
                        if next(eval) and (next(effects[1]) or #effects > 1) then 
                            for h  = 1, eval.seals.repetitions do
                                reps[#reps+1] = eval
                            end
                        end```
#

when it was doing this

#

evals.seals was nil

#

so it tried indexing that for the h=1 loop thing

#

and it was crashing

#

so i put that or {repetitions = 0} so if theres no seals it doesnt crash

#

my card isnt actually calculated in that bit

#

im fairly sure

wintry solar
#

that bit shouldnt crash regardless

#

whats your calculate look like?

hushed cradle
#
calculate = function(self, context, effect)
        if context.cardarea == G.hand then
            effect.x_chips = self.config.extra
        end
    end```
wintry solar
#

do not context.repetition

hushed cradle
#

ah

#

thank you

#

didnt think i would have to for some reason

#

yep that works

#

ty

#

gonna blame my nap for me not thinking straight

#

thank you for all the help

#

slowly getting there

primal robin
#

What version of lua balatro use?

mellow sable
placid frigate
#

hey when i try to put my mod into the game I'm getting an error, is there an error dictionary or something to find what's wrong with it

shell tangle
#

I mean, the crash screen itself is probably as close to an error dictionary that you're going to get, what error are you getting?

placid frigate
#

I belive it's 317

#

it says around line 12 and 14 but they look fine too me

shell tangle
#

That helps quite little, since it varies both on code that's been injected, either from your mod or a different one, what file it's in, what kind of error it is, several things. Just specifying a line gives very little information. Can you take a screenshot of the error to start with?

placid frigate
#

I also copyed the the error if that's better

maiden phoenix
shell tangle
#

No, this appears to be his own mod, it's not by Cerlo.

shell tangle
# placid frigate I also copyed the the error if that's better

This one is a simple grammar error in your code, I believe you forgot a comma on line 13 most likely, maybe using a period instead, but, for future reference, I'll go ahead and break down how you can read it and how you can parse the information.

placid frigate
#

yes please

placid frigate
maiden phoenix
#

Oh, I suppose you're doing this on top of someone's code?

shell tangle
# placid frigate yes please

First thing, is that the top line is usually the most important piece of information. Where it happened is located in steamodded's loader.lua file, specifically, a function there failed. But, it gives more information right after that - notice the P5 Hero Joker.lua that comes right after the initial part, it's showing where the actual error is located.

shell tangle
# shell tangle First thing, is that the top line is usually the most important piece of informa...

It's showing loader first, because, the error happened while trying to load in the file.

After that, it gives a line - 14, a } is expected, and specifically to close line 12 This means that something is wrong with that list of items between two{}, and the first { starts at line 12, so work your way from there.

Commonly, it's an issue of not having commas, or closing brackets incorrectly. Without commas, rather than trying to say, a = 1, then b = 2, it's trying to do them in the exact same instance, and doesn't know what to do because they aren't separated, so it crashes.

Where it says near '.', which is a period, it's saying, there's no comma after the . to separate objects in the list, so it should've ended in the line after, at 14, instead of finding another object, which is probably the one located at line 14.

shell tangle
# shell tangle It's showing loader first, because, the error happened while trying to load in t...

For the stuff under, that's traceback - it shows what went wrong in the events leading up to the crash. A lot of the time, that much simpler line at the top gives you all the information you need, but when it doesn't provide enough information or context about what happened, that's when you go down to Traceback.

You don't have to completely parse everything, if you notice some key words, that can be enough to give you an idea of where to look for stuff.

Most commonly, I'll notice assert and xpcall errors there - Those are simple, stupid mistakes - usually if it mentions assert, that means I did something wrong grammar wise. If it's only xpcall, that often means something is wrong with files - double check file names, folder names, make sure they're referring to the right stuff in code, all that stuff.

If you want a look at an easy example, pay attention to the next time someone inevitably reports a crash for Cryptid when installing it.

You'll often see xpcall mentioned - that's usually because the folders for Cryptid have to be named Cryptid and Talisman, due to their specific custom way of injecting objects into the game. Since, if they aren't named that exactly, then, they can't find the files that are supposed to be loading in, hence the xpcall stuff in those tracebacks.

Apologies for the quite long wall of text, but, it is important information, so I hope it clears things up and makes modding easier in the long run.

placid frigate
#

No, thank you for helping, hopefully now I won't need to write in here with every little issue I have with my code

analog spoke
#

I'm finally getting to the point of making my own mod instead of just editing pre-existing jokers, I found that with this joker for some reason the vars in description aren't showing up in game (numbers being replaced by nil), how would I fix this?

shell tangle
#

Do you have any loc_vars?

analog spoke
#

I don't think so? this thing is pretty small
by "vars" I mean the extra config btw, in case those are something different

shell tangle
#

Recommending my example mod since it has comments that explain stuff, you can find the loc_vars at line 66, https://github.com/Steamopollys/Steamodded/blob/main/example_mods/Mods/ExampleJokersMod/ModdedVanilla.lua

But to break it down, loc_vars means localization variables, and it tells the loc_txt, localization text, what to replace those #1#s with, but since it doesn't have anything to replace them with, it's just saying that they're nil.

GitHub

A Balatro ModLoader. Contribute to Steamopollys/Steamodded development by creating an account on GitHub.

analog spoke
#

I will check it out! thank u! :>

limpid skiff
#

Hi, I just got started with modding by just making a simple joker, is there a way to manually add it to my jokers or to the shop so I don't have to keep rerolling to find it whenever I wanna test it?

hushed cradle
#

if you put _RELEASE_MODE = false in your mod then it enables debug mode

#

during a run if you go to the collection

#

hold tab to bring up the debug menu

#

then press 3 while hovering over any card to spawn it

limpid skiff
#

Ty!

hushed cradle
#

np

primal hawk
#

How would I put a card area inside a tab?

#

I currently have this code

return (create_UIBox_generic_options({
        back_func = 'exit_mods',
        contents = {
            {
                n = G.UIT.R,
                config = {
                    padding = 0,
                    align = "cm",
                    colour = G.C.CLEAR,
                },
                nodes = {
                    create_tabs({
                        snap_to_nav = true,
                        colour = G.C.BOOSTER,
                        tabs = {
                            {
                                label = 'Joker Order',
                                chosen = true,
                                tab_definition_function = function()
                                    return {
                                        config = {
                                            align = "cm",
                                            padding = 0.05,
                                            colour = G.C.CLEAR,
                                        },
                                        nodes = {
                                            object = config_joker_list
                                        }
                                    }
                                end
                            }
                        }
                    })
                }
            }
        }
    }))
hushed cradle
#

might want to look at what to collection does

#

i think i have some code ill try find

#
function s_create_card_display(args)
  args = args or {}
  args._type = args._type or 'Joker'
  args.col = args.col or 5
  args.row = args.row or 2
  args.specific_center = args.specific_center or nil
  local deck_tables = {}
  local cards_per_page = args.col*args.row
  local current_center = 0

  S.card_display = {}

  for i = 1, args.row do
    S.card_display[i] = CardArea(
      G.ROOM.T.x + 0.2*G.ROOM.T.w/args.row,G.ROOM.T.h,
      args.col*G.CARD_W,
      0.95*G.CARD_H, 
      {card_limit = args.col, type = 'title', highlight_limit = 0, collection = false})
    table.insert(deck_tables, 
    {n=G.UIT.R, config={align = "cm", padding = 0.07, no_fill = true}, nodes={
      {n=G.UIT.O, config={colour = G.C.CLEAR, object = S.card_display[i]}}
    }}
    )
  end

  if args.specific_center then
    local center = args.specific_center
    local card = Card(S.card_display[1].T.x + S.card_display[1].T.w/args.row, S.card_display[1].T.y, G.CARD_W, G.CARD_H, nil, center)
    if args._type == 'Joker' then
      card.sticker = get_joker_win_sticker(center)
    end
    S.card_display[1]:emplace(card)
    return {
      {n=G.UIT.R, config={align = "cm", r = 0.1, colour = G.C.CLEAR, emboss = 0.05}, nodes=deck_tables},
    }
  end

  for i = 1, args.row do
    for j = 1, args.col do
      current_center = current_center + 1
      local center = G.P_CENTER_POOLS[args._type][current_center + (S.current_page*(args.row*args.col))]
      if not center then break end
      local card = Card(S.card_display[i].T.x + S.card_display[i].T.w/args.row, S.card_display[i].T.y, G.CARD_W, G.CARD_H, G.P_CARDS.empty, center)
      card.s_stats = true
      if args._type == 'Joker' then
        card.sticker = get_joker_win_sticker(center)
      end
      S.card_display[i]:emplace(card)
    end
  end
  
  local t = {
    {n=G.UIT.R, config={align = "cm", r = 0.1, colour = G.C.CLEAR, emboss = 0.05}, nodes=deck_tables}, 
    {n=G.UIT.R, config={align = "cm",}, nodes = s_create_page_cycle({_type = args._type, colour = G.C.SECONDARY_SET.Planet, _type = args._type, row = args.row, col = args.col})}
  }
  return t
end```
#

this is really similar to what the game does for the collection i think

primal hawk
#

ty

hushed cradle
#

nws

#

youll have to change S.card_display and S.current_page and s_create_page_cycle tho

#

i think the game has a built in page cycle thing

primal hawk
#

ok thanks

#

ill try it out

hushed cradle
#

also this is the args i was giving it

#
{_type = 'Joker', col = 5, row = 2,}```
wary crescent
#

Wandering if its possible to create a Joker like Hallucination, with the 1 in 2 chance to create a card on Booster Pack open, BUT it selects randomly between specific a list of tarot/spectral cards?

primal hawk
hushed cradle
#

try having a root not in the tab def function, then create a ui box with the definition of the card display, and in the config you can set the parent to the root node in the tab

hushed cradle
hushed cradle
# primal hawk Hey, I tried adapting your code but the card area still appears outside the menu...
G.FUNCS.cere_change_page = function(e)
    local page_config, back = Ceres.PAGE_FUNCS[e.config.ref_page]()
    local option_box = G.OVERLAY_MENU:get_UIE_by_ID('tab_contents')
    option_box.config.object:remove()
    option_box.config.object = UIBox{
        definition = {
            n = G.UIT.ROOT,
            config = {
                emboss = 0.05,
                r = 0.1,
                minh = 8,
                minw = 7.3,
                align = 'cm',
                padding = 0.2,
                colour = G.C.BLACK,
            },
            nodes = Ceres.FUNCS.create_buttons(page_config, back),
        },
        config = {
            offset = {
                x = 0,
                y = 0,
            },
            parent = option_box,
        }
    }
    option_box.UIBox:recalculate()
end

Ceres.PAGE_FUNCS = {}

Ceres.PAGE_FUNCS.main = function()
    local ref_table = Ceres.SETTINGS
    local _buttons = {
        {label = 'Jokers', toggle_ref = ref_table.jokers, button_ref = 'cere_change_page', ref_page = 'jokers_rarities'},
        {label = 'Consumables', toggle_ref = ref_table.consumables, button_ref = 'cere_change_page', ref_page = 'consumables'},
        {label = 'Enhancements', toggle_ref = ref_table.enhancements, button_ref = 'cere_change_page', ref_page = 'enhancements'},
        {label = 'Blinds', toggle_ref = ref_table.blinds, button_ref = 'cere_change_page', ref_page = 'blinds'},
          {label = 'Suits', toggle_ref = ref_table.suits, button_ref = 'cere_change_page', ref_page = 'suits'},
          {label = 'Editions', toggle_ref = ref_table.editions, button_ref = 'cere_change_page', ref_page = 'editions'},
        {label = 'Miscellaneous', button_ref = 'cere_change_page', ref_page = 'misc', remove_enable = true,},
      }
    return _buttons, false
end```
#

might be helpful

primal hawk
#

thanks lemme try this rq

#

appreciate it

hushed cradle
#

nws

#

itll def need some adapting

#

most imporant bits are local option_box = G.OVERLAY_MENU:get_UIE_by_ID('tab_contents')

#

and config = { offset = { x = 0, y = 0, }, parent = option_box, }

#

probably

placid frigate
crisp coral
#

1.0.0

static juniper
#

Is there a way for a Joker to apply an effect multiple times on a card by itself? Like, let's say (hypothetically) I wanted a Joker to make a card have a 1/2 chance to give x1.5 Mult three times in a row (three rolls of 1/2, each of them having giving a seperate XMult). Is that possible?

hushed cradle
#

you could do events for eval messages maybe?

#

not really sure how youd make it give the x mult tho

static juniper
#

Okay different question, is there a way to get a Joker to retrigger another Joker? I know they can retrigger cards, but idk about Jokers.

hushed cradle
#

mathisfun wrote some api for that

#

i think you can find it in a pull request on the github

untold cloud
#

Getting a bug where when after I call go_to_menu all of the decks in the new run carousel turn into red decks. I've seen there's a bug reported for Steamodded that causes this, does anybody know why?

#

seems to have something to do with remove_all(G.STAGE_OBJECTS[G.STAGE]), but i dont know enough about the source code to understand why it only allows for the red deck to be selected

hushed cradle
#

no clue sorry

#

anyone know how i can get a name to show for this enhancement tho?

spiral stirrup
#

Sorry if this is a dumb question or asked many times before, but how would you set up a Joker that destroys all other Jokers except for themselves at start of blind? Been trying to do this as an experiment to learn lua but I absolutely think I'm missing something simple lol

#

I'm used to C#, lua is moon runes to me

hushed cradle
#

you can do if v ~= card then btw

#

if this is in the calculate function for your joker

#

then you have to use card

#

because self refers to the smods joker object thing

#

but card refers to the actual card in game

#

is the problem atm that its destroying every joker?

#

also after dissolving them youll want to do some other stuff to make sure theyre dead

#

ill see if i can find an example from the source code

#

nvm start_dissolve might be enough apparently

spiral stirrup
hushed cradle
#

what is the problem at the moment w your code??

#

what does it do

spiral stirrup
#

it just didn't do anything

hushed cradle
#

hm

#

could you send me the entire code bit for that joker

spiral stirrup
#

Okay yeah changing it from Self to Card actually fixed the problem!

hushed cradle
#

ay

#

nice

spiral stirrup
#

Nah don't worry this was all I needed help with atm lol

#

but thank ya, again, lua is new to me

hushed cradle
#

dont worry about it, when i started with balatro the most id done is a bit of python lol

#

so i had no clue what i was doing

static juniper
hushed cradle
#

yeah its just general retriggering i think

#

its pretty useful for a lot of stuff

#

shame it wasnt in this case tho

#

good luck

static juniper
#

I might just have to do the most blatant hard-coding in my life to get this effect to work.

static juniper
hushed cradle
#

maybe make the joker create its own invisible jokers that trigger on the card lol

static juniper
#

It took me a second to realize you meant Jokers that are invisible and not Invisible Jokers lol

hushed cradle
#

oops mb

#

yeah jokers you cant see

#

but theyre there doing stuff for sure

static juniper
#

Wouldn't that cause visual weirdness though with joker positioning and slot number?

hushed cradle
#

dont add them to deck or emplace them to G.jokers

#

and then they can just sit invisbly in the middle of the screen or wherever lol

static juniper
#

You lost me there, I don't really know how to use emplace. Plus, wouldn't I then need to make an invisible variant of every Joker I want to retrigger?

hushed cradle
#

emplace basically just puts the joker in a card area, like your hand or the joker area, or your deck

hushed cradle
#

maybe not the best idea then

#

youll never guess what the card next to it does

static juniper
#

Does it create a Green Save?

hushed cradle
#

that took me way too long to understand lol

#

almost

opal spade
#

doesn't balatro use "destroy" instead of "burn"?

#

also tbh having them copy the other cards in hand could be a rlly fun effect

hushed cradle
#

it does but i thought burn sounded cooler lol

#

that was my plan and then i got sidetracked and ended up w this

#

think i still wanna add an enhancement that does smth similar tho

hushed cradle
opal spade
#

what the flip