#BalaUI - A set of utilities for creating mods

1 messages · Page 1 of 1 (latest)

pastel shoal
#

Website: https://www.balaui.dev/
Source: https://github.com/cozyGalvinism/BalaUI

This is a small web app which allows for easily creating Balatro objects using Steamodded with an easy UI (at the moment jokers and soon™️ also other moddable items like consumables, vouchers etc.
Right now, it's limited to jokers but I plan to add more types as time goes on and add more and more stuff to it to make modding just a little easier!

There might be a few bugs in the initial version, especially with the formatted text display for loc_txt, but I think it's stable enough to be used reasonably.

GitHub

A set of modding utilities for Balatro and Steamodded - cozyGalvinism/BalaUI

arctic star
#

this is cool

pastel shoal
#

Thanks!

mystic helm
#

There's something you should be aware of, if perishable and eternal don't have a value they'll default to true iirc

pastel shoal
#

Value as in cost?

mystic helm
#

No like if "perish_compat = false" doesn't exist then perish_compat is true by default

pastel shoal
#

Ah

#

I see

#

I'll make them true by default as well then

mystic helm
#

So whether you set "perish_compat = true" or nothing it will be true, same for eternal

pastel shoal
#

Fixed!

shy blade
#

we got MCreator equivalent for balatro early 😭

pastel shoal
#

ikr?

mystic helm
#

Is there a reason for variables not being registered in loc_vars yet?

pastel shoal
#

wdym?

#

I guess I didn't think about to put a stub there

#

Since usually loc_vars are computed anyways

rain ocean
#

not sure on the use of the name setting either, I'm not sure that's intended to be used

pastel shoal
#

The game uses it to identify some jokers, tbh I use it in the same way for my jokers

#
-- snip
        if context.using_consumeable and card.ability.name == 'Can Town' and context.consumeable.ability.name == '!addcan' then
            G.E_MANAGER:add_event(Event({
                func = function()
                    card_eval_status_text(card, 'extra', nil, nil, nil, {
                        message = localize{
                            type = 'variable',
                            key = 'a_mult',
                            vars = { cans }
                        },
                        mult_mod = cans * 2
                    })
                    return true
                end
            }))
            -- return {calculated = true}
        end
-- snip
rain ocean
#

it's not necessary in custom jokers though

pastel shoal
#

No and the web app also doesn't make it necessary to be filled (none of the fields really)

rain ocean
#

I was just thinking it might be confusing for new people to have two different name fields and one that doesn't seem to change anything on the output display

pastel shoal
#

Hmm

#

Yeah actually, I think I confused myself with that yesterday

#

Wondering why nothing changed

#

I think I can also reorder and rename the fields in the UI then

#

To just "Name" and "Description"

rain ocean
#

Yeah I think that would work better

#

like in your code example there, the card.ability.name is ALWAYS 'Can Town' in that function as it's only ever called on that object anyway

pastel shoal
#

Yeah, true

#

I don't even know why I put it there, I think I just mimicked the way vanilla does joker calcs

rain ocean
#

Yeah vanilla does it because every card is thrown into the calculate joker tower

#

But modded ones aren’t, they just have their own function called

split temple
#

yeah names were problematic to keep in Steamodded due to overlaps, and keys had to be decollisioned anyways so we just stuck with that

#

So while name isn't fully redundant due to being used in vanilla, it's to be disregarded

shy blade
#

btw personally we shouldnt be encouraging putting loc_txts directly to the main lua files

#

like if there are new mods i truly hope they use the new method

split temple
#

right, this is also something I need to concern myself with for the wiki

shy blade
#

🫡🙏

pastel shoal
#

So what is the right way to do it?

shy blade
#

lua files in YourMod/localization, with language codes as their names like en-us.lua, fr.lua, etc.

#

you can check lobotomy corp and d6 jokers' repo for examples

pastel shoal
#

Hmm... wondering how exactly to display that on the page tbh

#

Maybe a zip download?

#

Because having multiple code previews will confuse people

shy blade
#

yea maybe. and you can make the default name default.lua. and yes default.lua works as a default language file, a fallback (maybe)

pastel shoal
pastel shoal
shy blade
#

ykw i kinda wanna suggest an individual tool for mod localizing

pastel shoal
#

I will add that too

#

It's in my planned features

shy blade
#

this is gonna be a bless

pastel shoal
#

Looks like this in the Joker creator atm

#

why isn't localization done via JSON anyways?

#

Oh well

#

Would be a lot easier to parse tbh

shy blade
#

if its in json i can just straight upload the loc files to lokalise then here we go

pastel shoal
#

I mean, I guess we could have both, if the file ends in lua, load via Lua, if the file ends in json, parse with JSON and load like Lua

#

Maybe I'll try and make a PR for Steamodded later

#

Depends on how much time I have

shy blade
#

thanks in advance 🙏

pastel shoal
mystic helm
#

is the site down?

pastel shoal
#

No, shouldn't be

#

Unless GH is having issues

south cloak
#

turn off your dark mode

pastel shoal
#

(since it's GH Pages)

mystic helm
#

Yea that did the trick lol

pastel shoal
#

I wonder if I can add dark mode compat somehow

mystic helm
#

Ok so like, what I meant earlier with loc_vars is that, in order for the #1# to change, it needs to fetch said data in loc_vars in this example here I'm getting the number from the joker config. Is it something you considered the user should do itself or just missed out on it?

pastel shoal
#

The way I understood it was that you use loc_vars if you have variables that can change during gameplay. If you don't have that (for example your joker always gives +4 Mult), you can just hardcode that into the description. I can add a simple return like return { vars = { '4' } } and a comment before that saying "calculate variables here" or something like that

mystic helm
#

Yea but magic number is bad practice

pastel shoal
#

I guess the tool is pretty much meant as a starting point, I can add that to the loc_vars function, but I won't be able to do the same for calculate, since there's no way for me to write that

#

If you have a suggestion for how the loc_vars function might be implemented better for new users, I'd be open to that too

mystic helm
#

Yea I can kinda understand for calculate, the way I saw is that maybe you could let variable have a key and value, and put said key and value inside the "return { vars = {extra = {..." in loc_vars as well as in "config = {..."

So for example I could have a variable named "mult" which is 4, so it will add the line config = {extra = {mult = 4}}, and return {vars = {card.ability.extra.mult}} inside "loc_vars"

#

Ofc you'd keep the #n# fetching the variable value too.

pastel shoal
#

Ahhhh now I understand

#

Yeah okay, I'll add that

mystic helm
#

Oh also, unlocked is true by default if it doesn't exists

split temple
pastel shoal
#

👀

#

I'll create a PR

#

PR created! Hopefully it gets merged :D

pastel shoal
#

peak comedy

mystic helm
#

Neat!

pastel shoal
#

Pushed!

#

Thinking about it, isn't discovered also true by default?

mystic helm
#

I saw, why not make the the "Locale" parameter a selector between each possible entry instead of having to look it up and write it?

mystic helm
pastel shoal
#

Ah

#

Couldn't you technically translate the entire game to add a new translation and therefore a new locale?

#

But if not, what are the possible entries then?

mystic helm
mystic helm
pastel shoal
#

That's... an interesting list of locales

#

Some have _ some have -

#

Some only have the language part

mystic helm
#

Also this feels silly

pastel shoal
#

Yeah, that's default flex behavior

#

Need to shrink it

mystic helm
#

Sometime I click on the empty area by accident and I wonder where the line went

pastel shoal
#

Yeah, I'll quickly patch that

pastel shoal
mystic helm
#

The only files in the localization folder so yup

pastel shoal
#

Okay, gotcha

#

Right patched and dropdown implemented

pastel shoal
#

Gonna add that

#

Or rather push that

#

So you can share your jokers

#

The code is basically just a representation of the fields:

{
   "key":"balatroDiscord",
   "rarity":1,
   "discovered":false,
   "unlocked":true,
   "atlas":"",
   "posX":0,
   "posY":0,
   "locName":"Hello Balatro Discord",
   "locText":"Bananas",
   "cost":0,
   "blueprintCompat":false,
   "eternalCompat":true,
   "perishableCompat":true,
   "previewVariables":[
      
   ],
   "localization":[
      
   ]
}
dark steppe
#

Very cool!

shy blade
pastel shoal
#

Right, gonna work on consumables today

pastel shoal
#

Still some more work to do but

#

Really good that I turned everything into components, barely have to write any code

#

Aaaaaand I think that's done

pastel shoal
#

So I tried adding metadata but Discord isn't displaying it yet. Ideadlly it should show the name and description of the joker (without formatting) but oh well

dark steppe
#

No cap, you're making me wanna learn lua just to make mods.

pastel shoal
#

Glad you like it!

pastel shoal
#

Working on providing i18n support for the site now, since there are so many locales for the game

#

Also that way, I don't hardcode a specific language

shy blade
#

BRUH i literally just about to say that i forked the repo and started doing locale

pastel shoal
#

xD

#

Well, better sooner than later

#

The later I do it, the more I will have to change in the application

shy blade
#

so itd be better to have a per-lang font support bc zh/kr/jp dont use m4x11plus

pastel shoal
#

Yeah

#

Because language itself is easy

#

Font might be tricky

shy blade
#

yea

pastel shoal
#

Looks excessive, probably is

shy blade
#

little suggestion: {format:format} doesnt actually require a {} to close it if:
"{C:attention}the line ends"
but now it has problem displaying it correctly

pastel shoal
#

Wait, formatting only lasts until the end of the line?

rain ocean
#

yup

#

it's parsed line by line so it resets formatting each time

pastel shoal
#

sigh

#

I'll fix that some time later

split temple
#

Also {} isn't any sort of close tag, it also just resets the formatting

#

you can just as well stuff some other things in there

#

"{C:attention}One color {C:green}and another"

pastel shoal
#

That I know

#

But the reset by line thing is something I haven't considered before

shy blade
#

a variable cant be used more than once in the desc?

pastel shoal
#

That should work

#

huh

#

It doesn't

#

Odd

#

I'll patch that

shy blade
#

i'll prolly send random thoughts here when using it but just so you know none of them means to hurry

pastel shoal
#

That's fine!

shy blade
#

this one might be tricky but here are examples in mods like cryptid:
support for custom color and custom rarity, and a color picker to help with it.
i know its wild and im sorry balatrojoker

#

but since we are still implementing basic features this would def be a low-priority thing

pastel shoal
#

i18n support is pushed

#

Still need to figure out how to do the fonts though

#

I'd say if you add translation for a language that requires a font, add it to the fonts dir, add a custom CSS class and I'll think of a way to apply it depending on the locale

pastel shoal
#

It's gonna be on the waiting list though

pastel shoal
pastel shoal
#

Also added a German translation

#

nobody:
literally nobody:
consumable in german:

shy blade
#

below-average long german word:

pastel shoal
#

My best guess why it IS Verbrauchsgegenstand is because it's a machine translation. I think a better translation would be something like "Verbräuchliches"

#

Which would pretty much be a 1:1 translation

split temple
#

that or "Verbrauchbares"

pastel shoal
#

Yeah, that sounds simpler actually

pastel shoal
#

Playing around with Vercel for deployment

#

Preview works there since SSR is enabled

#

(ssr being server-side rendering)

#

GH Pages can't do that since the entire app runs as a single page, so it cannot display dynamic content inside the static page (which is needed for meta tags)

pastel shoal
#

Right, I changed the hosting to Vercel now since it also integrates better with SvelteKit!

#

This one's more permanent though

weak galleon
#

You know what would be really cool (but probably really hard to add)

#

a way to make menu UI visually to turn it into code that you can use in a mod

#

I would use that for like every menu I create

frosty kraken
#

Hiya, really appreciate someone making a utility like this MarineAhoyThumbsUp

#

was wondering if parts of the code not updating like name and description is a known WIP issue or if it's just being weird on my end

pastel shoal
#

Didn't really think of that tbh

#

If you download the mod as zip, you can see the localization folder

frosty kraken
#

oh, duh
yeah now I've downloaded it I see

pastel shoal
#

It's a bit difficult to show multiple files because they take up so much space on the page

frosty kraken
#

yeah I wouldn't worry, it's just me being the perfect idiot proof tester lmao
like maybe a line of text above saying the lua below doesn't show everything like name and desc would be worth adding

pastel shoal
#

Yeah

#

I'm looking into adding more code previews somehow... or put them all in a pop up with a guide or something

rain ocean
#

Is there a way to add the different files as tabs?

pastel shoal
#

🤔

#

Never tried it but

#

I think I can make that

#

A tabview

frosty kraken
#

mhm
I'd say the main two things that imo are missing rn is if you could highlight text and auto add the different text changers in descriptions like {C:inactive}, {C:money} etc
and having preset variables, like if Name was a dropdown list of all the base game options like Mult, Chips etc and then a Custom option at the bottom if that's still needed

rain ocean
#

It’ll keep the space the same and probably be fairly intuitive for people to notice there’s more than one file

#

Variables don’t have any preset options, they can be named anything, that’s the point of them, no?

frosty kraken
#

I thought mult and chips were global variables MarineThink2

#

could be wrong, I'm still green to modding this game

pastel shoal
#

afaik, they're just config values that vanilla cards use

#

And for mods, I kinda assumed that it's good practice if they used extra and stored their variables in there

#

like that

#

I mean in general, it doesn't quite matter anyways

rain ocean
#

Storing in extra is preferable, yeah

pastel shoal
#

You still need to add them to loc_vars

#

And to display them you still need to apply the placeholders

#

And lastly you'll need to calculate the cards as well

#

So in the grand scheme of things, I don't think the name of variables matters

#

Or rather the location

frosty kraken
#

the split between basic funtions being carried out in config and more specific conditions being done in calculate still throws me for a loop

#

like "if card is spade then add mult 4"

rain ocean
#

Nothing is done in config

pastel shoal
#

^

#

Everything is done in calculate

#

Or use

#

(for consumables)

#

You basically need to tell the game what should happen, because you might as well want to add custom effects to a basic card ^^

frosty kraken
#

idk why this is one game that I still just don't get the modding fundamentals for lmao MintDerp

rain ocean
#

There’s just not a lot of documentation at the moment so you kind of have to just figure it out yourself

pastel shoal
#

config is pretty much just a blueprint for actual cards where you define default values for the state of the card, it gets copied into card.ability, where the game (or you) can change the state freely

#

You can kind of imagine it as a way to define custom fields on the card

#

Here's an example:

SMODS.Joker{
    key = 'cantown',
    name = 'Can Town',
    rarity = 4,
    discovered = true,
    atlas = 'myatlas',
    pos = { x = 1, y = 0 },
    cost = 10,
    config = { extra = { cans = 0 } },
    loc_txt = {
        name = 'Can Town',
        text = {
            'This Joker gains {C:mult}+2 Mult{}',
            'for every Can {C:attention}added{}.',
            '{C:inactive}(Currently {C:mult}#1#{C:inactive} #2#)'
        }
    },
    loc_vars = function(self, info_queue, card)
        local can_word = 'Cans'
        local cans = card.ability.extra.cans
        if cans == 1 then
            can_word = 'Can'
        end
        return { vars = { cans, can_word } }
    end,
    calculate = function(self, card, context)
        local cans = card.ability.extra.cans

        if context.cardarea == G.jokers and context.joker_main and cans > 0 then
            return {
                message = localize{
                    type = 'variable',
                    key = 'a_mult',
                    vars = { cans * 2 }
                },
                mult_mod = cans
            }
        end
    end
}
#

Although this doesn't use config

#

There we go

#

This basically adds a joker called Can Town to the game. If you buy the joker, it starts out with 0 cans. You can increase or decrease the cans in any way you want (for that specific joker, I actually increase it every time you use a certain consumable)

rain ocean
pastel shoal
#

But in the scoring phase, the only thing that tells the game what should happen is this:

return {
                message = localize{
                    type = 'variable',
                    key = 'a_mult',
                    vars = { cans * 2 }
                },
                mult_mod = cans
            }
rain ocean
pastel shoal
#

Yeah

frosty kraken
#

I just kinda suck at internalizing raw code and implementing it myself honestly Lol
bethesda stuff and mario 64 which are my main modding games have a lot of 3D realtime visual feedback
a blank lua page even tho everything just being text should be easier just ends up feeling like

rain ocean
#

You just have to keep reloading the game!

#

Use save states and it’s actually pretty quick

pastel shoal
#

^ that pretty much... you end up closing and reopening the game a lot

frosty kraken
pastel shoal
#

I wish there was a hot reload options for mods "indev"

split temple
#

uh

#

we just got rid of that

#

well it's always been broken tbf

pastel shoal
#

I don't think we could reload specific mods, right? Since they're all loaded at the beginning of the game and stuff

split temple
#

you were able to reload all mods by holding M or disabling/enabling a mod

pastel shoal
#

I guess reloading is a big issue for any dynamic system... I remember the hell that broke loose when you reloaded Bukkit plugins

#

Some were fine, others just shat the bed

split temple
#

but it didn't properly reset the environment because it can't deal with modules in global scope

#

basically it would work fine so long as you don't modify any functions :/

pastel shoal
#

Ah gotcha... hmm... I suppose there's no way to create a snapshot of the game's entire state before any mods are loaded and just... reset it to that state

split temple
#

not really

frosty kraken
pastel shoal
#

Made a tab view

#

Better

#

Perhaps I should turn this into a component

pastel shoal
#

presto!

#

Although technically

#

Now that we have JSON localization support

#

I can make this even easier

#

Looks kinda wonky

pastel shoal
#

Kinda goes wonky on 2 locales already

#

huzzah!

#

Right, localization is now switched to JSON entirely

#

Just got a great idea! The share codes could be used to combine multiple jokers etc into a single mod

#

So technically, I could just add a "project manager", which really just lets you fill out the mod information, then you enter a list of share codes and it will show you what jokers, consumables etc you would add and let you download that

#

And the best part: It'd still all be client-side

pastel shoal
#

Tabs are now live!

#

I will add compression for the share codes to make them shorter

#

Or at least try to

pastel shoal
#

Well, I managed to reduce the character size a little bit but not much

#

I dunno if that's worth it

mystic helm
#

If this voucher doesnt make me happy Ill find you irl

pastel shoal
pastel shoal
#

I'm on vacation at the moment by the way, so I'll check out the post here some time during next week

pastel shoal
#

I am back and I will start working on the deck creator today

#

After that, we're gonna get into the atlas builder

#

And then, hopefully, a fully fledged mod builder

#

(without functions, obviously)

shy blade
#

finally we'll need an AI pro to train a model based off all mods available to generate calculations from prompts and start scamming ppl with 5 tiers of premium plans balatrojoker

pastel shoal
#

don't spoil my plans

shy blade
#

WOAH LMAO

pastel shoal
#

Can't wait for the ad playing before you can copy or download the mod x)

#

Remember the old adfly pages?

shy blade
#

YOUR MOD IS ALMOST READY ... 38 SECONDS..

pastel shoal
#

Add an artificial progress bar, which stops at 90% and then asks if you want to buy premium for faster mod downloads

#

Honestly I'd kinda be tempted to make this as an April Fools thing

rain ocean
#

I would be very interested to see how you implement a mod builder into this. Would it just be building the header?

pastel shoal
#

Nah, it'd allow you to input the share codes of jokers, vouchers etc, ask for mod name, mod ID etc and then let you download that package

#

So you'd end up being able to build the entire mod on that site (minus images because that would actually result in me having to host images and I'm not gonna deal with that)

#

Maybe I could allow image links

#

So it would download the images from said links and put them in asset folders

rain ocean
#

Interesting

abstract snow
#

oh this is quite the useful tool

#

god bless

pastel shoal
#

Almost done

#

Need to do the localization for the new nav item

#

But after that

#

I think it should be ready

crisp sky
#

Need tutorial actually

pastel shoal
#

What for specifically?

crisp sky
#

No disregard what I said

pastel shoal
#

Fair enough

#

Time to add the image scaler and atlas builder

#

Dunno exactly how yet

#

But I'll figure something out

pastel shoal
#

Image scaling works, now trying to see how to best add the atlas builder itself

#

Once I'm done with this, I'll see if I can add a good localization editor

fast geyser
#

oh nice

#

are you using canvas for this?

pastel shoal
#

No, sharp

#

I found that to be working quite nicely at the moment

fast geyser
#

oh is it a backend thing?

pastel shoal
#

sharp is only used on the server-side, though it could see complications when deploying (since libvips is required)

#

Yeah

fast geyser
#

makes sense

pastel shoal
#

Alternatively, I could write a small WASM binary to resize and build the atlas

fast geyser
#

was thinking it was frotend only

pastel shoal
#

That could run directly in the browser

#

But we'll see how well this works

fast geyser
#

I think cavas's api is good enough for this job

#

I've used it in the past but that was years ago

#

but you should just be able to make the canvas the right sizw for your atlas and then load the images and just stick them on there

#

and there was a script somewhere to reaize usinf nearest neighbor for the 2x

fiery basalt
#

oh my god, there's a german translation for the site too?

pastel shoal
#

Yup

#

Provided by yours truly

#

Right now it's controlled by the PC's configured locale