To add new pokeballs, just add to PokeballDB in startup_scripts/CobblemonBalls.js, then add the new sprites/models associated in the assets folders.
This is for 1.20, and while it might work on 1.21 with minimal changes, you'd likely need to use KubeJS Reflected. If you try this, you're on your own.
The zip contains the startup script that creates and registers the balls, a client tooltip script, and the assets required to get give the balls item, model and gui textures.
The held pokeball model works fine, as does the GUI and Item sprites, however, there is a visual bug where thrown pokeball entities (EmptyPokeBallEntity) uses the fallback model: The normal pokeball.
This does not effect the functionality of custom balls at all.
I think the reason for this is that PokeBallRenderer tries to get a pokeball model from PokeBallModelRepository using the ResourceLocation as a key as a association between the pokeball model and the pokeball type.
Thing is, the PokeBallModelRepository list of pokeballs are hard coded as a series of individual declarations instead of a iteration of the Pokeballs list.
We can set up our own model using PokeBallModelRepository.INSTANCE.getPosers.put().
But this doesn't seem to be enough to make EmptyPokeBallEntities show a new ball, or didn't work with how I tried implementing it in the past. Fallback would still be used.
Moreover, the posers list gets wiped whenever resources gets reloaded and it reloads from the internal hardcoded list, and I'm unsure what to do about that since hooking onto ClientResourcesReloadedEvent doesn't seem to be working here in KJS, unless I'm a idiot and did it wrong.
Could just regularly check if the list contains our entries and put them in if not, but that seems like a gross workaround.
At this point, I've been trying to figure this out for awhile now, and I'm giving up on it. ¯_(ツ)_/¯
If anyone else can figure it out, let me know!