#💻・modding-dev

1 messages · Page 52 of 1

sand rune
#

well you could bin the file loads off to threads, or have a worker system implemented to pick up a task w/e but then again I assume it just curries the path and injects it then cool

frosty dock
#

at the end all it does is that it just loads the file content as a string into a function

#

if you want a thread, make one

sand rune
#

im not saying that - i meant the internals of how smod loads file is unknown so asking a question to confirm behaviour is not out of the question and doesn't mean someone doesn't know how to code 😉

unkempt thicket
#

is there a way to check if a function was called with a specific wording like if self == context.other_card then (the example won't work because there is not a value for context.other_card in :get_id()) but can you somehow get the string version of the wording and not the actual data it holds?

frosty dock
#

not like it would be possible to look at the code for it

unkempt thicket
#

like the string "context.other_card"

brisk pond
#

my main mod file is just a loader for all the subfiles

unkempt thicket
#

the string of what :get_id() is getting the id of, just doing tostring() turns the data into a string but I want to get the words itself as a string

brisk pond
frosty dock
#

like you might want to pass an additional argument to get_id

rough furnace
unkempt thicket
tepid crow
#

You should probably explain why you're trying to do this, there might be a better way

unkempt thicket
# tepid crow You should probably explain why you're trying to do this, there might be a bette...

I am returning the id as a true statement when a certain joker is in hand, I am trying to make sure that when calculating poker hands it returns its base value. as when calculating poker hands, it uses hand[i]:get_id() while joker code uses context.other_card:get_id() (I am doing this because I want the joker to return 4 different ids when the player has the joker, and this code should make all cards count as any card which I can fine-tune from there)

wintry solar
#

why dont you just move the code of the function to your joker?

unkempt thicket
#

I'm not sure how I would change the id of each card inside of my joker

wintry solar
#

well your joker is called get_id right?

unkempt thicket
wintry solar
#

oh

rain imp
#

Do you know where the function for animation of a card scoring (the shake specifically) is located? Or at least which file has all the gameplay sequence in it?

frosty dock
rain imp
#

Wow, what a name though, thanks!

brisk pond
unkempt thicket
brisk pond
#

you can do that with this configuration

#

at least i think

#

im not near my pc now

frosty dock
rain imp
#

Or I guess there are no separate shake calls for different card types, the cards themselves call for it

frosty dock
#

it is used for all cards

nocturne garnet
#

what if every card was juiced up...

wintry solar
#

you can juice ui elements too ^^

rain imp
#

I figured, though now I need to find how to track the function without intruding it

#

debug.sethook doesn't feel right

wintry solar
#

what are you trying to do?

nocturne garnet
rain imp
wintry solar
#

a specific card or all cards?

rain imp
#

I mean a total of all the cards in a hand

#

Like, I sure can track when the hand is being played, in theory it should be "when card scores and hand is being played add +1"

tepid crow
rain imp
#

I know that I can do it inside of the animation function, but I want to avoid modifying such fundamental functions at all costs

wintry solar
#

I think that's probably the cleanest place to do it

#

you can check that it's a playing card that is being juiced and increment your counter

#

then reset it when a new hand is played

rain imp
#

Shame, but I understand

wintry solar
#

otherwise I think it would require a lot of injecting in evaluate_play and custom effects don't even need to be brought back into that stack

rain imp
#

I see, thank you!

tepid crow
night pagoda
#

What to use to save global variables inbetween runs? G.GAME.blablabla saves in the run, I need something that saves in the profile

frosty dock
#

ig you can use G.SETTINGS and call G:save_settings()

rough furnace
#

Settings saves across profiles iirc

wintry solar
#

you could maybe use config and save them by profile

frosty dock
#

there's also still the "per-profile mods" suggestion, but with having to restart to change applied mods it doesn't seem too useful and should just use --mod-dir right?

#

per-profile config might be something we want to allow

night pagoda
wintry solar
#

where do profiles save their unlocked state?

#

G.PROFILES[G.SETTINGS.profile].whatever_you_need_to_save might work?

night pagoda
#

will try, ty!

rough furnace
#

It just save all config to the profile folder?

#

If the user toggles something

wooden nexus
#

Alright. I need to figure out how to remove cards from a pack list and add them to a different one

brisk pond
#

where are all the playing cards in your deck stored?

zealous glen
#

Do you mean the deck or all that exist?

wintry solar
#

G.deck

#

I think

zealous glen
#

Again it depends if they mean the deck or all playing cards that exist

zealous glen
#

Cf. Blue Joker

#

For the other, I think it’s just G.playing_cards since it’s not a CardArea

#

Cf. Stone Joker

brisk pond
#

i mean the current deck

zealous glen
#

That doesn’t help still :<

#

Like the one you draw from?

#

At the moment you run the code?

brisk pond
zealous glen
#

The full deck or the deck? ^^’

zealous glen
#

Just look for the Joker closest to what you want

#

Blue or Stone

brisk pond
#

maybe it's easier if I explain my Joker idea

zealous glen
#

So Stone

#

Just look at Stone

#

I already explained above but I don’t have the code to double check so you can do it yourself

brisk pond
#

so i thought in context.first_hand_drawn to make a table of all ranks in your deck and then switching hand cards with the ones in the deck with high ranks

zealous glen
#

Oh I have something that draws a 3, and another that changes the deck order

zealous glen
#

Because the hand has already been drawn

#

Unless you want it to be an extra card

#

Like I did with my 3

#

If not, you can change the deck order

brisk pond
#

i don't know how the game handles the whole "drawing to hand" thing

zealous glen
# brisk pond i don't know how the game handles the whole "drawing to hand" thing

This hooks into the shuffle function to put a card of each Enhancement on top of the deck. Keep in mind it has no condition, so it'll always apply as is.

local shuffle_ref = CardArea.shuffle

function CardArea:shuffle(_seed)
    shuffle_ref(self, _seed)

    if false then
        local idx = #self.cards
        local seen = {}
        for j = #self.cards, 1, -1 do
            local card_j = self.cards[j]
            if card_j.ability.set == 'Enhanced' and not seen[card_j.ability.name] and j ~= idx then
                seen[card_j.ability.name] = true
                self.cards[idx], self.cards[j] = self.cards[j], self.cards[idx]
                idx = idx - 1
            end
        end
        self:set_ranks()
    end
end
#

well, actually I put an if false then so it never runs

#

If you want an extra card, you can do this

    calculate = function(self, card, context)
        if context.first_hand_drawn and #G.deck.cards > 0 then
            for i = 1, #G.deck.cards do
                local _card = G.deck.cards[i]
                sendDebugMessage("Sightseeing… "..tostring(_card:get_id()))
                if _card:get_id() == 3 then
                    card_eval_status_text(context.blueprint_card or card, 'extra', nil, nil, nil, {message = 'tour guide', colour = G.C.PURPLE})
                    G.E_MANAGER:add_event(Event({
                        trigger = 'before',
                        func = function()
                            if _card and not _card.removed and _card.area == G.deck then
                                draw_card(G.deck, G.hand, nil,'up', true, _card)
                            end
                            return true
                        end
                    }))
                    break
                end
            end
        end
    end
#

This draws a 3, as mentioned

brisk pond
zealous glen
#

what

#

oh

#

I mean, that is a hook

#

You can't hook without a hook

brisk pond
#

without lovely.toml?

zealous glen
#

I suppose you could change the hook into an injection

zealous glen
brisk pond
#

ok i'm reformuling

brisk pond
zealous glen
#

As is, it doesn't use any injections

brisk pond
#

ok

zealous glen
#

I don't know if that algorithm is a random uniform conditioned shuffle

#

I just programmed something that worked

#

I suspect it's not uniform but I haven't done the math

#

If anyone wants to I'm curious

#

The remaining cards could probably be shuffled again to make it uniform tho

zealous glen
#

I mean, I don't think that's my fault

#

There's no ipairs in that code

brisk pond
#

ok i got it to work

brisk pond
zealous glen
#

That's what I said

brisk pond
#

I'd want to make it so that in your first 8 cards, there is an Ace

zealous glen
#

That's the previous one

brisk pond
#

or a King if you don't have aces

zealous glen
#

I sent you both so you could choose whichever fit your idea best

#

And I explained what each did

brisk pond
#

ok thanks

brisk pond
zealous glen
#

take a break

#

drink water

#

eat pasta

#

go to sleep

brisk pond
#

I'm going to eat a pizza in 5 minutes

zealous glen
#

woohoo

brisk pond
#

fully stereotypical

zealous glen
#

Imagine a deck with two blue cards

#

After a shuffle, the distribution of the first blue card is decreasing, and the distribution of the second blue card is increasing

#

If I move the first blue card to the top, then the distribution of blue cards in the remainder is the distribution of the second card, which isn’t uniform

#

If you selected either the first or the second to be moved to the top, then I think it becomes uniform again

#

But I wonder if there’s a more efficient way of doing it. I mean, you could do a conditioned shuffle from the start

#

@brisk pond maybe implement this new version instead

brisk pond
#

I'll see that after the pizza

zealous glen
#

What do you think @mellow sable

brisk pond
#

maybe taking the card you want and putting it in first place, and then shuffling the remainder of the deck?

mellow sable
#

What are you trying to program?

zealous glen
mellow sable
#

There’s an effect similar to this planned for Cryptid but I didn’t code it yet

brisk pond
#

but i want a scalable solution

brisk pond
#

that works for "the first two cards" etc..

mellow sable
#

I’d imagine you have to hook the draw function to check highest rank using rank api

#

And pick a random one if tied

zealous glen
#

I think the remaining of the deck isn’t uniformly random

mellow sable
#

You could always reshuffle it again

zealous glen
#

So I sketched a proof showing my algorithm doesn’t quite keep the deck uniform

#

And I thought of an improvement

#

My algorithm just moves the first card to the top of the deck with a swap

#

Or draws the card directly to your hand

#

The solution is to go over the entire deck then choose one of the options randomly

mellow sable
#

Yeah I think that’s the best idea

zealous glen
#

Which should make the result uniform

#

But not be as computationally demanding

#

You can check my code above

#

Well, the original one

zealous glen
#

Below is the code that just draws it to your hand

mellow sable
#

Oh you’re hooking shuffle, interesting

#

I don’t rly have time to peruse the code but it might be better to hook the function that draws a card

brisk pond
#

what would you do?

zealous glen
# brisk pond but i want a scalable solution

You can use SMODS.find_card or whatever it was called, or a custom global variable, to keep track of how many instances there are.

Also, to make it linear in deck size, keep track of the [number of instances] highest ranked cards seem as you iterate over the deck. (Although I don’t think it’s linear in instance size; it’s probably the product of both)

zealous glen
zealous glen
#

The next option is to just overwrite the base behavior, but why do that when you can hook

mellow sable
brisk pond
#

what you think about this thought?
if context.first_hand_drawn then

  • get hand[1]
  • create a table D with all cards in the whole deck ordered by rank
  • swap hand[1] and D[1]
zealous glen
zealous glen
#

Also

#

As I said

brisk pond
#

that is not a problem

zealous glen
#

Ordering the deck is inefficient

#

You don’t need to do that

brisk pond
#

adding a message from the Joker and you see its ability happening with your eyes

#

im trying to not modify any game funcs

zealous glen
#

Also, your solution doesn’t account for drawing the higher rank cards and then replacing it with a lower rank

#

The first card is the highest ranked by default

brisk pond
#

to understand how Balatro code works:
there is this G.deck which is a table that contains all you playing cards, hand + deck.
is this true?

zealous glen
#

As I said before

#

G.deck is a CardArea

brisk pond
#

ok

#

does it contain hand + deck or only deck?

zealous glen
#

Neither

#

It contains the cards currently in your deck

brisk pond
#

ok

#

how do I access the cards in a cardarea?

#

just do G.deck[1]?

zealous glen
#

No

#

As I said, G.deck.cards

brisk pond
#

G.deck.cards[1]?

zealous glen
#

No

#

That’s one card

#

All the cards are in the table G.deck.cards

wintry solar
#

hooking and checking for your joker isn't really modifying game funcs

zealous glen
#

Cerlo on their way to not use Steamodded to avoid modifying base game functions

rough furnace
#

To be fair, DebugPlus does very minimal modifying of base game functions

#

The dev branch does a bit more

zealous glen
#

Cerlo on their way to play vanilla Balatro to avoid modifying base game functions

brisk pond
#

Cerlo on his way to disinstall Balatro to avoid modifying PC's code

zealous glen
#

Cerlo on their way to not update Balatro to avoid modifying base game functions

#

(Ultra orthodox Balatro traditionalist)

brisk pond
#

I'll just play on the first ever version of Balatro

restive osprey
#

Stupid question
How does one mod balatro?

frosty dock
restive osprey
#

michaelsoft defender says that lovely is a virus

zealous glen
#

You either trust michael or you trust the program you’re trying to download

#

Lovely does change the game’s code, which is used to mod the game, but is also how other virii can work

rough furnace
restive osprey
#

well it's going ok rn but I don't know what it means by the assets header in the steamodded install. i see the assets folder, but that doesn't have what i'm looking for

rain imp
#

Do I need to specify a folder when injecting into a file in a directory? Thus, to inject into functions\state_events.lua do I need to write
target = "state_events.lua"
or
target = "functions\state_events.lua"?

rough furnace
#

Yes

rain imp
#

Makes sense, thanks

#

Ah well, apparently not

rough furnace
#

Use a forward slash

rain imp
#

Bruh

#

Why is it like that

rough furnace
#

Cause that's the way any sane os works

#

Cause everything else uses \ as an escape character

#

So it gets annoying

rain imp
#

Alright

restive osprey
#

my moronic ass can not seem to find out how to put the Steamodded-1.x.x folder into the mods folder

zealous glen
#

Does the mods folder exist

restive osprey
#

yes, however i can not find a file explorer option to move the folder into the mods folder I have created in %AppData%/Balatro

zealous glen
#

You can click and drag

#

Or cut-paste

restive osprey
#

the folder is empty
it will not let me

zealous glen
#

wh

#

Why not

restive osprey
#

actually, I think it's just the search results won't come up with it
weird

zealous glen
#

AppData is a hidden folder

#

You need to configure a setting to be able to see it

brisk pond
wintry solar
#

I'm pretty sure there's a code for negative chips

brisk pond
#

it's just this

#

for context.individual

#

card_eval_status_text(card,'chips') does not add the functionality for negative chips

edgy reef
#

There’s a specific message type for minus chips iirc

maiden phoenix
#

And if it doesn't exists you can make your own too

brisk pond
#

yeah but you can't return it in context.individual

brisk pond
maiden phoenix
#

I don't see how adding a custom text would scramble the timing, unless you understood something else

brisk pond
#

you have to put your joker after the one to its left and before the one to its right

brisk pond
#

if you just display the message, then the joker acts before all the others

wintry solar
#

if you call card_eval_status_text does it not add it into the event queue?

brisk pond
#

i don't think because the game doesn't just read a joker and execute it. it reads it, adds its execution to a table and then executes all values in that table

#

i don't know if you can effectively solve this problem

zealous glen
#

I don’t understand your issue

#

The game works by doing that

#

Adding a custom text can’t scramble the timing

#

Otherwise the game wouldn’t work

languid mirage
#

changing chosen from vert to vert makes it non vert 😭

frosty dock
#

ah yes

languid mirage
#

I think it's because function is run and then chosen is set to true somewhere afterwards

#

but why it works for challenges I have no idea

#

uhh maybe somehow because challenges menu is an overlay

tepid crow
#

UI go brrr

wintry solar
#

Is challenges recalculated somewhere?

languid mirage
#

challenges dont have choice=true

#

but Idk how it works because when I do that the triangles never render

#

except for the initial one

#

and then clicking on the tab overrides it

#

actually nvm

#

only clicking off does that

#

hm

#

how does that make any sense

lament fjord
#

I'm looking at some code that's throwing an error... and somehow I'm getting a string when I'm trying to require a module?

languid mirage
narrow maple
#

how hard would it be to make a copy of the red deck and just make the joker slots be maxed at 99

lament fjord
#

Why the f am I getting duplicated lovely patch sections in my git rebase/merge flows

zealous glen
#

@mellow sable Thinking about it, when multiple effects are trying to draw cards, they don't know about each other, which can be an issue if the draw is inside of an event. I'm not sure what's the best way to deal with it.

Either setting a variable on the card to say it's supposed to be drawn, or rewriting/copy-paste-editing draw_card to make it instantaneous and not create an event

#

I wonder if the latter is generic enough, since you can do the search inside of an event, and thus in theory proc while Jokers are resolving and not outside of the event queue

#

I quickly set up the former before going to sleep which works locally, but might not work with other mods

#

What version of Lua does Balatro use?

mellow sable
#

LuaJIT, based on 5.1

zealous glen
# mellow sable LuaJIT, based on 5.1

I was also thinking about how to stack multiple deck reordering effects in an intuitive way, and I found one that uses a function introduced in 5.3, table.move

#

There's a solution with table.insert but I don't quite understand it

#

To be fair I don't know if I understand the one with table.move either

#

oh wait it just removes the element then adds it again

#

I wonder if that would mess up the deck by manipulating the card table directly 🤔

rain imp
#

In this piece of code in state_events.lua

                        --If chips added, do chip add event and add the chips to the total
                        if effects[ii].chips then 
                            if effects[ii].card then juice_card(effects[ii].card) end
                            hand_chips = mod_chips(hand_chips + effects[ii].chips)
                            update_hand_text({delay = 0}, {chips = hand_chips})
                            card_eval_status_text(scoring_hand[i], 'chips', effects[ii].chips, percent)
                        end```
when does the `if effects[ii].card then juice_card(effects[ii].card) end` trigger? It doesn't seem to be when a card juice_up animation is played even though it refers to it and everything else in the function is being triggered
tepid crow
#

it jiggles a card if the card exists, no?

rain imp
#

If a card has chips, yes, it should, but a debug message was never sent when the card played. Moreover, it should've triggered a whole chain of events with debug messages but none had been sent

#

And also, if I uncondition the string it crashes since it doesn't know from where to pull .card piece

tepid crow
#

Oh yeah I see what you mean

tepid crow
rain imp
#

But it uses this exact animation for almost everything, and there're no other places I've found where the scoring card specifically calls for it

tepid crow
#

assuming you are interested in jokers, :calculate_joker needs to return both a valid type (chips, mult, etc) and itself as card

#

And most jokers that you would expect to don't
One of the jokers that does is Lucky Cat for example

rain imp
#

Actually, I need jokers AND the cards in the played hand

tepid crow
#

oh wait, were you the person that was asking about juice_up before?

rain imp
#

Yes

tepid crow
#

I think you want Card:juice_up instead of juice_card?

rain imp
#

Oh, there's a huge chain of things it triggers across 4 files, including Card:juice_up

#

I'm away from pc so I can't recite

tepid crow
#

are you talking about juice_card?

rain imp
#

But there's a spaghetti being called by juice_card

tepid crow
#

because all juice_card does is call Card:juice_up:

function juice_card(card)
    G.E_MANAGER:add_event(Event({
        trigger = 'immediate',
        func = (function() card:juice_up(0.7);return true end)
    }))
end
rain imp
#

And Card:juice_up calls movable:juice_up

tepid crow
#

yes

#

I'm still telling you that you probably want Card:juice_up

rain imp
#

Actually what I did is I copied and inserted this whole chain of events as spice_up and so on with every single function having a debug message (including an initial if), and none were sent while visually everything is alright

tepid crow
#

wow the spelling of "juice" is starting to look weird

#

Card:juice_up seems to work fine for me (5 juices for the cards, 2 for the jokers)

#

lots of background noise though, mostly when hovering over stuff, so you'll have to filter for that

rain imp
#

Yea, that's why I need this exact procedure to be called

#

There's a whole block dedicated to specific card triggers

tepid crow
#

you're using juice_card instead of Card:juice_card to avoid the "background noise"?

rain imp
#

Correct

tepid crow
#

you're avoiding more than just the background noise

rain imp
#

Since even hovering over the cards calls for it

#

And that's excessive

tepid crow
#

Yes. You'll have to filter for that somehow

#

juice_card isn't called for scoring in hand for example

rain imp
#

Well, you see, if I count the triggers of the chips function it works like a clock, but it counts before the hand has even been played

#

I want it to count each time an animation is played, for gradual growth

#

Aesthetically pleasing

tepid crow
#

so what, it works fine, the timing is just off?

rain imp
#

That's why I'm trying to track each time an animation starts

#

It counts a slightly wrong thing

#

That's why I have recreated the whole juice_up chain of events so I can track each use of this exact animation procedure

tepid crow
#

right

rain imp
#

Which I will call only in specific cases like chip and mult card triggers

#

And it won't be called by hovering and stuff

tepid crow
#

all seems good, so I'm confused about what the issue is?

rain imp
#

It doesn't use the aforementioned if

#

Just ignores it

#

And idk from where else it calls for this animation if not from there

tepid crow
#

what do those ifs etc look like?

tepid crow
#

you're asking who else calls Card:juice_up if not juice_card?

rain imp
#

In this procedure, yes

#

There's just nothing else

tepid crow
#

right, slight issue with that, there's more than 1 or 2 instances of that happening:

willow quiver
#

Can anyone point to some examples of SMODS.load_file or NFS.load? I've tried both for organizing my mod into multiple joker files. But clearly I'm doing something wrong

rain imp
#

But when a card scores with its chips, only this procedure of play juice animation-count chips-add them to counter triggers

tepid crow
rain imp
#

Yup, since it sure isn't the dedicated string for that in its code

tepid crow
#

agreed

#

also why are you calling it a string lol

#

I keep thinking of a string literal "string"

rain imp
#

It is called line, innit?

#

Forgot the lingo

tepid crow
#

usually

#

self-taught?

rain imp
#

Half that, half just taught in a different language

#

As in Russian

tepid crow
#

ah

#

thought you meant Java or something for a sec

pseudo hazel
#

Just started the reading the docs for SMODS.Sound and have been trying to figure out how SMODS.Sound:create_replace_sound() works.

tepid crow
rain imp
#

Can't read them rn, I'll test them when I can

tepid crow
#

sure thing - you could also filter one of the calls of :juice_card and raise an error to get the full callstack

rain imp
#

Also we can go the opposite way and try safely unconditioning the line so it actually triggers

#

As right now it crashes, as I said

tepid crow
tepid crow
rain imp
#

So it actually triggers the recreated chain of events and counts it

tepid crow
#

sure, but isn't the error that card doesn't exist?

rain imp
#

...yes, which is suspicious

tepid crow
rain imp
#

So, as I understand, effects[ii].card just doesn't exist at all, and this is why the if never goes through

tepid crow
#

it does, but only if :calculate_joker returns it

#

which seems to be pretty rare

rain imp
#

Then the verdict is that it triggers the animation wherever else

#

Unpromising

tepid crow
#

well yeah

#

like I said (implied?) earlier, juice_card probably isn't what you want if you're looking for the played card triggers

rain imp
#

Alright

#

Well, I guess the next best guess is another instance of effects[ii].chips as it triggers with it at least

unkempt thicket
#

how do you make something give xchips

brisk pond
pseudo hazel
#

The game literally went nope

limpid flint
#

It's impossible to make a mod's header part update with variables right?

rough furnace
#

No, but you can probably mess with the variables that get parsed out in steamodded, although it's probsbky not supported

#

What do you want to do?

placid frigate
#

I tried putting my mod in the mods folder and when i launched the game it came back with this error, any way to fix this

rough furnace
#

Its a syntax error I believe

placid frigate
rough furnace
#

What's on and around line 33

placid frigate
#

it's a closing parentheses to complete local joker = SMODS.Joker:new(

glass scaffold
placid frigate
#

the whole code? i'm just trying to get the sprite working now

#

--- STEAMODDED HEADER
--- MOD_NAME: Phantom Theif
--- MOD_ID: JOKERP5!
--- MOD_AUTHOR: [GalaCad]
--- MOD_DESCRIPTION: Adds Joker from Persona 5 as a Joker
--- BADGE_COLOUR: #ff0000
--- DISPLAY_NAME: P5 Joker
--- PRIORITY: -100
---MOD CODE---
local localization = { -- Localization is a separate variable so it won't look cluttered when registering
name = "Polydactyly",
text = {
"Destorys Heart cards",
" and add {C:attention}X1 Mult to each played Hand",
}
}
local joker = SMODS.Joker:new(
"Phantom Theif", -- name
"p5", -- slug
{extra = 0}, -- config
{x = 0, y = 0}, -- spritePos
localization, -- loc_txt. The table we declared just now is passed through this
2, -- rarity, 2 is Uncommon
5, -- cost
true, -- unlocked
true, -- discovered
true, -- blueprint_compat
true, -- eternal_compat
"", -- effect
"P5 Joker Mod", -- atlas. The Joker will take its sprites from the atlas called "Balatro Joker Persona 5"
-- We do not declare a soul_pos because we do not need one
)
joker:register()
local sprite = SMODS.Sprite:new("P5 Joker Mod",SMODS.findModByID("P5 Joker Mod").path, p5card.png, 71, 95, type)
sprite:register()

placid frigate
glass scaffold
#

Try replacing the [ at line 18 with {

and ] with } at line 33

#

That may be causing that crash.

placid frigate
#

at line 18 it's "Phantom Theif", -- name and at line 33 it's joker:register()

glass scaffold
#

Also, maybe change joker:register to j_p5:register()

placid frigate
glass scaffold
#

I don't remember how to work with 0.9.8, maybe the wiki will have something we're missing.

placid frigate
#

or is it easier to update to 1.0.0?

glass scaffold
placid frigate
glass scaffold
#

If you want a code editor, go with VSCode and get Lua for it

#

This is 1.0.0 code

placid frigate
glass scaffold
placid frigate
#

oh ok, i was following that for my code

#

so i just need to download 1.0.0 and it should Theoretically work

glass scaffold
placid frigate
glass scaffold
#

Again, use the wiki for better results

placid frigate
glass scaffold
placid frigate
#

Thank you

#

I'm still pretty new to coding and wanted to try it for a game i enjoy

dawn oar
#

Anyone know how to change the keywords on the side

rain imp
#

There are some specific configs for colored text and such, but it is quite obvious

dawn oar
#

That's what I did
It didn't show up anywhere

rain imp
#

And you need to load it with a specific code that I can't provide rn

#

You can find it in any other text-changing mod and copy it from there, it is uniform

dawn oar
#

I'll take a look around then

wintry solar
#

@rain imp have you looked in card_eval_status_text? I’m pretty sure every card calls that when scored and is juiced in there. I think the one you were looking at is for extra juice ups that are needed for certain jokers

rain imp
#

Not yet, it was my next idea

#

I'm still away so let it be for now

wintry solar
#
card.T.y = self.T.y + (self.T.h/2 - card.T.h/2) - (#self.cards - k)*deck_height
#

how does this happen

languid mirage
#

what is the idea behind self t h/2 - self t h/2

#

ah nvm

#

I'm blind

frosty dock
languid mirage
#

I meant like big blind balatrojoker

#

but didn't see that, cool project

wintry solar
#

huh it doesn't actually make a difference though

#

oh there must be another function acting on these

#

there's no other reason it would have these huge gaps

languid mirage
#

isn't there hard set T

#

I remember something like that when working on cartomancer

wooden nexus
#

Crap

#

wrong copy

zealous glen
wintry solar
#

I'm using a CardArea at the moment

zealous glen
wintry solar
#

yeah

#

I figured configuring them like a deck would let them stack up quite nicely

#

but I'm getting this weird problem with the last few chips

zealous glen
#

Try copy-pasting the deck and see what it looks like?

wintry solar
#

oh I've fixed it

#

derp

#

I missed an argument copying the cardarea over

sand rune
#

for entering the blind/shop area where abouts does that often occur?

zealous glen
#

what

wintry solar
rough furnace
#

Nice

shell timber
#

perspective is a bit off but it looks cool

zealous glen
# wintry solar 👀

I can’t tell if the spacing is correct for all of them. But this is really cool

wintry solar
#

they're all equally spaced

zealous glen
#

How did you create the fake cards?

wintry solar
#

it's the same card as the deck just with the sprite changed

zealous glen
#

So you create playing cards but change their Sprites to chips?

wintry solar
#

yeah

#

or rather, the back of them

#

although I suppose now I have this cardarea working I can stop it having type = 'deck'

zealous glen
wintry solar
#

just the top one at the moment

#

I'm not sure if having it on all of them is a good idea, it would be difficult to get the one you're looking for when there's too many chips

mellow sable
#

Also gold stake isn’t shiny

wintry solar
#

adjusted the perspective a bit

rough furnace
#

I think the issue is we can see the top and side of the chips

wintry solar
#

hmmm

#

I think this scale and spacing works better?

#

32 chips for reference

#

any thoughts?

rough furnace
#

The chips are very thin

wintry solar
#

I am fairly limited of how I can manipulate the images

rough furnace
#

Move them closer because they are so thin

glass scaffold
#

More

MORE!

zealous glen
#

Make the chips thicker

rain imp
wintry solar
#

I like this one

rain imp
wintry solar
#

functions/common_events.lua

rain imp
#

bruh

rain imp
#

I checked it but forgot to cycle the search

rough furnace
regal wolf
#

I had a stroke reading this

rain imp
#

It worked

rough furnace
maiden phoenix
shell tangle
# wintry solar

How would these look if they're grouped together rather than cycling through the stakes?

sand rune
#

Can I do joker.edition to get the edition of joker to check if it is negative?

wintry solar
#

they just cycle as a proof of concept for when there's more stakes

#

for example, here are the cryptid stakes

limpid flint
#

That looks sick

unkempt thicket
#

How do you make a joker with XChips?

maiden phoenix
zealous glen
wintry solar
#

well if you can find a way to manipulate these into thicker chips please let me know and I'll try it in game

zealous glen
#

Can’t you scale them vertically?

#

I guess the relative size of the side to the top will still be the same

#

So I guess not without editing the sprites

#

Or using a shader idk

narrow maple
#

How hard is it to make a mod where theyre is a joker for every single card rank that states: Every scored card also counts as X rank

#

ex: you play a flush 5 of 4s but they also get scored as aces

narrow maple
#

oh XD

narrow maple
#

im using the themed joker mod & have a build that retriggers lucky 7s a bunch

#

but I'd like to have a joker that lets say makes those 7s also aces

#

yk?\

#

kinda like sock and buskin

#

where everything is a face

unkempt thicket
narrow maple
#

not quite

#

here ill show you

elfin fulcrum
#

i should ask, does anyone have a nice resource of the game's color palettes?

narrow maple
#

so Im playing a 5 of a kind with 7s

elfin fulcrum
#

i'm working on a very stupid resprite mod

narrow maple
#

but I have retriggers for aces I want to use

#

but my entire deck is 7s

#

so like a joker that makes all cards considered aces also

unkempt thicket
#

oh so 7s count as aces?

narrow maple
#

any card but yea

#

"scored cards also trigger played aces effects"

#

except replace aces with all 13 ranks

#

and a diff joker for each

unkempt thicket
#

it might not be possible to have a card as multiple ranks, i'm still trying to make one though

elfin fulcrum
wooden nexus
#

Question: Anyone having problem with trying to have a suit appear in only certain decks?

elfin fulcrum
#

i wanna cover every card in the game but for now i've only done the tarot cards due to all using the same palette

narrow maple
verbal surge
narrow maple
elfin fulcrum
#

hop on bloons

#

(i do not ship sunflower i just thought a hop on bloons reference would be funny so i turned it into a frame of that gif)

narrow maple
# narrow maple

also ignore how much shit is going on I have 16 mods installed

unkempt thicket
narrow maple
#

intresting

candid grail
#

Hello all! Has there been any work done on a true balatro randomizer? I'm a long time mod/game dev toying with the idea of randomizer development/archipelago support, but I'd hate to duplicate effort

elfin fulcrum
#

i'm now unstoppable

narrow maple
elfin fulcrum
#

now i can continue to do dumb shit like this but for all cards

narrow maple
#

although with that youd probably need to make a custom code for every mod you want to be compat with

brisk quartz
lunar parrot
#

Hi, is there any resource to get into balatro modding or should i just look at the game's code (if it is easily accesible) and go from there to make my own jokers and stuff ?

wooden nexus
lunar parrot
#

alright, gonna take a look at that, ty !

near ivy
gilded narwhal
#

Like you did with all the different stakes but do it with the same stake instead

#

(Total height of stake selection/number of unique stakes)/height of each stake = number of each one

zealous glen
wintry solar
narrow maple
#

is there a way to make seals stack?

#

like if you have a card gain purple it doesnt overwrite red

narrow maple
#

just wondering if it may be possible I do use that mod but it doesnt seem to work? it only shows 1 red seal being applied

tepid crow
#

I think the red seal count isnt shown in the sprites?

narrow maple
#

odd :p

tepid crow
#

Though if you have any questions about that mod specifically you should probably ask there, I've only heard of it in passing and I've never played with it

narrow maple
wooden nexus
#

Okay I gotta ask, how do I add onto a function without having to replace the entire thing?

tepid crow
narrow maple
#

thank you! ❤️

maiden phoenix
wooden nexus
#

idk how to do either

tepid crow
maiden phoenix
#

Example of an override from D6Jokers, basically you save the function, and call it in your new function

#

Lovely injection is fetching a specific pattern in the original source code and placing it before, at or after said pattern to add your code

wooden nexus
#

So let's start with the lovely inject

#

I have this

maiden phoenix
#

Yea?

wooden nexus
#

No Faces is the original

#

I'd want something like

#

position as after, the pattern as the no faces command and then the payload as my no suits thing

#

correct?

maiden phoenix
#

Yup

#

Don't forget the target as which lua file is it

wooden nexus
#

Gotcha

maiden phoenix
#

For your thing it would be "game.lua"

wooden nexus
#

I'm gonna have to do one for each suit to make it easy on me

#

is it messy code? Yeah. Does it matter? no

maiden phoenix
#

What are you trying to do out of curiosity?

wintry solar
#

you can put multiple lines in your payload

wooden nexus
#

I'm recreating the Dungeons and Degenerate Gamblers decks

maiden phoenix
#

Yea don't make a lovely patch for each line lol

wooden nexus
#

Yeah I'm gonna make one onl;y

#

I mean copying the if statement

#

the DNDG decks only use one suit each and then one will be something I do a long ways down the line

maiden phoenix
#

Do you want only one specific suit or one random suit?

wooden nexus
#

They have to be specific for this

maiden phoenix
#

Why not make (_s == "Hearts" or _s == "Clubs" or.. ? Unless I misunderstood

wooden nexus
#

hang on

maiden phoenix
#

Oooohhhh

wooden nexus
#

mhm

maiden phoenix
#

That's ambitious

wooden nexus
#

the last one is a variant of the Nothing suit where jack, queen, king, and ace are their glitch card

wooden nexus
maiden phoenix
#

I should really try DNDG

narrow maple
wooden nexus
#

DNDG actually is getting mod support too

zealous glen
#

I tried the demo but I didn’t get it

maiden phoenix
wooden nexus
#

Someone's making balatro in DNDG

zealous glen
#

Balatro was much easier to get

tepid crow
maiden phoenix
zealous glen
#

Even if I didn’t know Poker rules

narrow maple
wintry solar
#

isnt the way you're doing it just going to remove all the suits?

wooden nexus
#

Balatro is a harder game than DNDG. DNDG is literally just "Play Blackjack"

zealous glen
#

I know blackjack rules but I have no idea what the strategy is supposed to be like for DNDG

maiden phoenix
#

Aren't DNDG cards more cryptic than Balatro tho?

wooden nexus
#

Guess it just came easier to me then, idk

maiden phoenix
#

Idk never tried it just asking

zealous glen
maiden phoenix
#

Ive seen few videos of early gamas

maiden phoenix
tepid crow
zealous glen
#

At least the ones I saw

wooden nexus
#

Well here's the one balatro mod, but the newest version explains a lot more stuff

zealous glen
#

But I still didn’t get what my strategy was supposed to be like

maiden phoenix
#

Ok imma just shut up about DNDG I know nothing about it

zealous glen
#

I also know next to nothing

#

I played it a few months ago

wooden nexus
#

It has explanations now in effects so it's not as difficult

maiden phoenix
#

I've finally became one of em

wooden nexus
#

Example here

zealous glen
#

Math, math, and math

wooden nexus
#

Careful, you may accidentally call them meth

maiden phoenix
#

The three mathsqueteer

zealous glen
#

What do you mean accidentally?

wooden nexus
#

.

#

fair

zealous glen
maiden phoenix
#

Ye

#

I'm flexing my afk hours

zealous glen
#

I wonder what it looks like if I have VSC and Aseprite simultaneously

#

And Balatro

tepid crow
maiden phoenix
#

Like rn I have VSC and Balatro open

zealous glen
#

Including some whose art and concept were by Gaziter

wooden nexus
#

So is this good?

zealous glen
maiden phoenix
#

Or ask for sheet perm idk

zealous glen
#

@limpid flint

maiden phoenix
wooden nexus
#

it's going to, yeah

maiden phoenix
#

It doesn't work if it's in a lua one

zealous glen
#

I also have Baby’s Building a Tower to Space, but I’m not sure it’s a Space Joker

maiden phoenix
#

Looks good to me imo

wooden nexus
#

ok.

wintry solar
#

no

zealous glen
#

And I want to add Zodiac effects, which right now are Jokers but might become Consumables

wintry solar
#

you need to use ' not "

zealous glen
#

Sometimes doesn’t work

#

Idk why

maiden phoenix
#

Uh

zealous glen
#

So either single or triple quotes

wintry solar
#

you have to use ''' on multiline strings

wooden nexus
#

the example patch has """

zealous glen
#

I know Bunco does it

wooden nexus
#

idk

maiden phoenix
wintry solar
#

" only works on single line strings but not always

zealous glen
#

You might not need a patch

wintry solar
#

I think it breaks when there are quotes inside the payload that way

#

I just use ''' for everything now

zealous glen
#

Like _

#

Unrelated but I remember… either Julia or Python discussing how to parse strings with content like \\\\\

#

And all the issues that came with trying to do so

tepid crow
#

Sounds more like Julia? I dont recall python having anything like that unless it was minor or a long time ago

zealous glen
#

I’m like 99% sure it’s Julia but if I say anything incorrect a skeleton will jump out of the ground and eat me

tepid crow
#

Ah, the good old man-eating skeleton in the ground

languid mirage
#

I might've cooked

maiden phoenix
wooden nexus
maiden phoenix
#

3d joker!!!

#

Sick edition

wooden nexus
#

I did single quotes like everyone wanted

maiden phoenix
#

yea special characters needs a \ behind them iirc

#

I'd say check other mods' lovely patches

wooden nexus
#

so == is \=\=?

maiden phoenix
#

I'm not that well versed in lovely that much 😅

wooden nexus
#

I don't understand any of it in the .tomls i'm referencing

languid mirage
#

if you want to escape all regex special characters for the pattern just use this

wooden nexus
#

thanks

#

so i put my pattern in there?

languid mirage
#

yes

#

pattern before you added any escape characters

#

then just copy result from below

#

wait

#

why do you need regex

#

can you show what line are you trying to replace in the source code

#

you can use pattern patches for most use cases

#

if its a single line

wooden nexus
#

if self.GAME.starting_params.no_faces and (_r == 'K' or _r == 'Q' or _r == 'J') then keep = false end

#

I'm trying to add another if statement underneath it

languid mirage
#

I noticed on screenshot above you dont have patches.pattern specified as well

#

you dont need to escape any characters if you're just using pattern patch

#

pattern patch = matches 1 line

wooden nexus
#

So like that?

wintry solar
#

do the '''

wooden nexus
#

# Suit Remover
[[patches]]
[patches.pattern]
target = 'game.lua'
pattern 'if self.GAME.starting_params.no_faces and (_r == 'K' or _r == 'Q' or _r == 'J') then keep = false end'
position = 'after'
payload = '''
if self.GAME.starting_params.no_hearts and (_s == 'Hearts') then keep = false end
if self.GAME.starting_params.no_spades and (_s == 'Spades') then keep = false end
if self.GAME.starting_params.no_clubs and (_s == 'Clubs') then keep = false end
if self.GAME.starting_params.no_diamonds and (_s == 'Diamonds') then keep = false end
'''
match_indent = true```
wintry solar
#

you don't need to escape characters unless you're doing a regex patch

wooden nexus
wintry solar
#

no?

wooden nexus
#

oh you're talking there

wintry solar
#

also you miss an =

wooden nexus
#

Oh that must've gotten removed accidentally

#

also thought you mean by payload with '''

#

okay now what?

wintry solar
#

oh you need the header, hold on

#
[manifest]
version = "1.0.0"
dump_lua = true
priority = 0
#

put this at the top

wooden nexus
#

ok

#

thanks

#

and...

#

game loads

#

if it works, i should have 52 cards in my deck and 0 hearts

#

oh

#

I can figure it out somehow

#

.

#

I DID ALL THAT FOR NOTHING!??

#

Why? Why doesn't this work?

wintry solar
#

function Back:apply_to_run(self)

summer beacon
#

Why isn't my texture mod working ?

#

It's supposed to change the sprite of Jupiter.

wooden nexus
#

hang on

#

Launching What consumables

summer beacon
#

Fem for female, I already did fem jokers : (Here is a few as examples)

wooden nexus
#

Try this-

#

Oh

#

SMODS.Atlas({key = "Tarot", path = "custom_consumables.png", px = 71, py = 95, atlas_table = "ASSET_ATLAS"})

summer beacon
#

My mod uses bigger textures, can I replace the 71/95 by 142/190 ?

wooden nexus
#

sure

wooden nexus
#

Pretty neat

languid mirage
summer beacon
wooden nexus
#

There's an end at the bottom that got cropped off

languid mirage
#

if you go to mods/lovely/dump/back.lua

#

to line 202

#

what's there

#

it's probably apply_to_run but just in case

wooden nexus
#
function Back:apply_to_run()
    local obj = self.effect.center
    if obj.apply and type(obj.apply) == 'function' then
        obj:apply()
    end
languid mirage
#

seems weird that it would say self is nil

summer beacon
#

Now it crashes again but says :
[SMODS _ "core/game_object.lua"]:315: Failed to collect file data for Atlas femc_Tarot

wooden nexus
#

huh?

summer beacon
#

I put the code you send me right, right ?

wintry solar
#

in your back object

wooden nexus
#

ok

zealous glen
zealous glen
languid mirage
#

it's a vertex shader

#

unlike fragment shader, it changes the shape of the card

#

so you can go "out" of normal card boundaries

#

well, I'm saying shape

#

not really to any shape cuz plane is just 2 triangles

#

so there's not much you can do (I think)

#

but you can do some funky transformations for sure

wooden nexus
languid mirage
zealous glen
wooden nexus
#

oh

#

didn't know it updated to 1.0.0

zealous glen
primal robin
summer beacon
#

[SMODS _ "core/game_object.lua"]:315: Failed to collect file data for Atlas femc_Tarot
So does anyone knows why this error shows up ?

wooden nexus
#

not the mod

#

the code for it

zealous glen
wooden nexus
#

I mean...

#

The code for removing suits

#

I found a bunch for ADDING suits

summer beacon
#

Nevermind I found the error I confused a _ with a - 😛

zealous glen
night pagoda
#

huh?

wooden nexus
#

idk why you thought to ping them

night pagoda
#

I need more context

wooden nexus
#

Making code similar to abandoned but to remove suits

zealous glen
night pagoda
#

Uhhh, need to think about it

night pagoda
#

it just has in_pool value that prevents decks from adding the cards

zealous glen
#

See? I said it was a Steamodded feature

night pagoda
#

But I still want to think about this

#

Adding in_pool to vanilla suits doesn't sound very stable

#

Replacing*

wooden nexus
#

I'm just trying to figure out why the code isn't loading it

zealous glen
wooden nexus
#

like 3 times

night pagoda
#

I'm looking at the code and it seems that there's actually base game functionality for removing suits from the start of the run for the challenge decks; perhaps it could be incorporated here?

wooden nexus
#

what am I supposed to look for?

zealous glen
#

Like your patch

wintry solar
#

look for wahtever is causing your error

zealous glen
wooden nexus
shell tangle
#

I think the challenge thing is the current method they're going with, Checkered deck's is this, under apply_to_run()

    if self.name == 'Checkered Deck' then
        G.E_MANAGER:add_event(Event({
            func = function()
                for k, v in pairs(G.playing_cards) do
                    if v.base.suit == 'Clubs' then 
                        v:change_suit('Spades')
                    end
                    if v.base.suit == 'Diamonds' then 
                        v:change_suit('Hearts')
                    end
                end
            return true
            end
        }))
    end

So, maybe try replacing the starting_params things with something like this.

zealous glen
wooden nexus
#

Oh I am

#

I only have a patch for the starting params

#

which yes, it did work

wintry solar
#

what's on the line of your error?

wooden nexus
#

hang on, lemme mess with something

wooden nexus
#

Eh, lemme try something

wintry solar
#

sure

wooden nexus
#

do i need to make a second .toml?

wintry solar
#

no

wooden nexus
#

I give up

wintry solar
#

can you send your file?

wooden nexus
wintry solar
wooden nexus
#

What?

#

How?

wooden nexus
wintry solar
#

okay

#

remove that second patch in your toml file

wooden nexus
#

ok

wintry solar
#
SMODS.Back{
    name = "DNDG Test Deck",
    key = "DNDGTest",
    pos = {x = 6, y = 0},
    atlas = 'DnDG_Decks',
    loc_txt = {
        name ="DNDG Test Deck",
        text={
            "Test Deck",
        },
    },
    apply = function(self)
        G.GAME.starting_params.no_hearts = true
    end
}```
#

then use this instead

#

then in your toml, change the suit names to just the first letters

wooden nexus
#

It works

#

thanks

wintry solar
#

then if you need to do others, just add to the apply function

wooden nexus
#

gotcha

#

Idk what's going on but sometimes with selected back, it crashes

#

but i reload with no changes and it's fine

#

Not what i meant by a deck of nothing

wintry solar
#

whats the crash message?

wooden nexus
#

hang on

#

It's something about selected back

wintry solar
#

try viewed_back instead

wooden nexus
wooden nexus
#

lemme try with the key

shell tangle
#

Try putting the G.GAME.selected_back.effect.center.name == 'DNDG Test Deck' in the suit in parentheses?

wooden nexus
#

HOW DID THE PARENTHESIS WORK?

#

Also how do I set it up for key instead of name?

wintry solar
#

change name to key

wooden nexus
#

I tried and it didn't work

languid mirage
#

probably because you didnt change the string to the actual deck key?

wooden nexus
#

hang on

#

okay i must be seriously screwing up balatro because now i'm getting cloud errors

wintry solar
#

when are you getting this crash?

languid mirage
#

nil check selected_back?

wooden nexus
#

few seconds after loading the game

languid mirage
#

G.GAME.selected_back and (G.GAME.selected_back....your code) or false

wintry solar
#

return (G.GAME.selected_back and G.GAME.selected_back.effect.center.key == 'whatever your key is')

#

I don't know why in_pool is being called in the menu though

zealous glen
#

For the splash animation and the main menu

wintry solar
#

oh good shout

zealous glen
frosty dock
#

me when selected_back_key

zealous glen
#

me when Myst adds dancing isaac to L Corp

wooden nexus
#

When I wanted a deck of nothing, I didn't mean literally nothing

hallow forge
#

that;s a deck of nothing alright

zealous glen
#

Ah yes the

#

I love playing the in the stake

#

I always take to help me defeat

maiden phoenix
#

Can't wait to use "Death" to turn my into a

zealous glen
#

Why don’t you use to your into a

wooden nexus
#

So yeah, what's the problem?

zealous glen
#

The problem is

wintry solar
#

I don't think selected_back_key is actually a key funnily enough

#

can you throw a sendDebugMessage(G.GAME.selected_back_key) in that function?

wooden nexus
#

I went back to name instead of key

#

at least with name it works

frosty dock
#

oh right it's the name 💀

wintry solar
#

Is it not the center object?

#

it's also

#

never used

zealous glen
frosty dock
wintry solar
#

but it's the same as what gets sent to Back() which takes a center

#

two questions.
1 - should the tower of chips display tooltips on every chip, or just the top one?
2 - how is best to lay out the selection. I was thinking 3 rows of 6?

zealous glen
wintry solar
#

yeah the hitboxes are nowhere near at the moment 🙃

mellow sable
tidal maple
#

@wintry solar you can help me in dm?

night pagoda
#

why are the stakes look so squashed?

wintry solar
wintry solar
#

but yes, Math, they are automatic

mellow sable
#

Awesome

zealous glen
zealous glen
#

I wasn’t one of those people but

night pagoda
#

What was wrong before?

#

I don't like how this looks at all because it looks like someone just stretched the entire thing in photoshop

zealous glen
#

People complained about the angle?

wintry solar
zealous glen
#

I didn’t see an issue

wintry solar
#

this was how they started

wintry solar
wooden nexus
#

Welp. Next thing i gotta do is figure out how to make packs, how to remove cards from packs and keep cards from strength and such cards

tidal maple
#

@night pagoda can help me?

night pagoda
#

of course, what do you want?

zealous glen
wintry solar
#

he wants help modding a pirated copy of the game

tidal maple
#

make mods non steam balatro

wooden nexus
#

I don’t want them appearing in standard packs tho

zealous glen
#

Ah I see

night pagoda
#

I don't think there will be any differences in how to help tho

zealous glen
#

To add to your playing cards

tidal maple
#

can help with modding

#

in dm

night pagoda
#

pirated games usually do not change their behavior unless it's like an Electronic Arts game or something

hallow forge
#

you'd want to patch create_card

night pagoda
#

sure

tidal maple
#

Big thanks

wintry solar
#

this is better, right?