Skip to content

Commit f518048

Browse files
committed
More work
1 parent 698857f commit f518048

2 files changed

Lines changed: 178 additions & 78 deletions

File tree

README.md

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,28 @@
11
# cav
22

3-
**cav** is a Bash script that lets you get started with Balatro modding on Linux easily. :banana: :zap: :sparkles:
3+
**cav** is a Bash script for managing Balatro mods. :banana: :zap: :sparkles:
4+
5+
**Currently it only works on Linux.**
46

57
1. Install Balatro through Steam on Linux
68
2. Run `curl https://raw.githubusercontent.com/lynn/cav/refs/heads/main/cav > ~/.local/bin/cav`
79
3. Run `cav install` to install Lovely and Steamodded
810
4. Run `cav new` to start making a new mod!
11+
5. (WIP) Run `cav mods` to list available mods, and `cav add <xyz>` to install a mod.
912

1013
## Commands
1114

1215
| Command | Description |
1316
| ------- | ----------- |
1417
| `cav install` | Install [Lovely](https://github.com/ethangreen-dev/lovely-injector) and [Steamodded](https://github.com/Steamodded/smods). |
18+
| `cav install lovely` | Force-reinstall the latest version of Lovely. |
19+
| `cav install steamodded` | Force-reinstall the latest version of Steamodded. |
1520
| `cav list lovely` | List available versions of Lovely. |
1621
| `cav list steamodded` | List available versions of Steamodded. |
1722
| `cav status` | View installed versions of Lovely and Steamodded. |
1823
| `cav uninstall` | Uninstall Lovely and Steamodded. |
1924
| `cav new` | Create a new mod in the `Mods` folder. |
25+
| `cav mods` | List mods available for installation. |
26+
| `cav add <mod>` | Install a mod. |
27+
2028

cav

Lines changed: 169 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,31 @@ version="0.1.0"
77
cavendish="🍌⚡"
88
debugging=0
99

10-
warn() { echo "${WARN}[!]${NORMAL} $*" >&2; }
11-
error() { echo "${RED}[X]${NORMAL} $*" >&2; }
10+
declare -A mod_desc
11+
declare -A mod_url
12+
13+
mod_desc["Pampa"]='Adds 28 jokers by Pampa'
14+
mod_url["Pampa"]='https://github.com/batabata3/balatro-pampa-joker-pack.git'
15+
16+
mod_desc["CardSleeves"]='Adds sleeves as modifiers to decks'
17+
mod_url["CardSleeves"]='https://github.com/larswijn/CardSleeves.git'
18+
19+
mod_desc["paperback"]='Expands the base game in many ways'
20+
mod_url["paperback"]='https://github.com/Balatro-Paperback/paperback.git'
21+
22+
mod_desc["Cryptid"]='An unbalanced mod'
23+
mod_url["Cryptid"]='https://github.com/SpectralPack/Cryptid.git'
24+
25+
mod_desc["ExtraCredit"]='A small-scale vanilla-style content mod'
26+
mod_url["ExtraCredit"]='https://github.com/GuilloryCraft/ExtraCredit.git'
27+
28+
mod_desc["CozyStats"]='Stats'
29+
mod_url["CozyStats"]='https://github.com/NoxZet/CozyStats'
30+
31+
warn() { echo "${WARN} !${NORMAL} $*" >&2; }
32+
error() { echo "${RED} X${NORMAL} $*" >&2; }
1233
die() { error "$*" && exit 1; }
13-
debug() { if [ $debugging -eq 1 ]; then echo "${GREEN}[-]${NORMAL} $*" >&2; fi; }
34+
debug() { if [ $debugging -eq 1 ]; then echo "${GREEN} =${NORMAL} $*" >&2; fi; }
1435

1536
for cmd in curl unzip perl tput find grep; do
1637
if ! command -v "$cmd" > /dev/null; then
@@ -78,12 +99,14 @@ find_balatro() {
7899
if [ ! -d "$balatro_appdata_path" ]; then
79100
die "Balatro appdata directory not found at $balatro_appdata_path"
80101
fi
81-
export BALATRO_MODS="${balatro_appdata_path%/}/Mods"
102+
# export BALATRO_MODS="${balatro_appdata_path%/}/Mods"
82103
else
83104
balatro_appdata_path="${BALATRO_MODS%/Mods}"
84105
debug "Got balatro_appdata_path from BALATRO_MODS: ${HINT}$balatro_appdata_path${NORMAL}"
85106
fi
86107

108+
balatro_mods="${balatro_appdata_path%/}/Mods"
109+
87110
if ! [ -f "$balatro_appdata_path/settings.jkr" ]; then
88111
die "settings.jkr not found at ${HINT}$balatro_appdata_path/settings.jkr${NORMAL} (is Balatro installed?)"
89112
fi
@@ -94,7 +117,7 @@ find_balatro
94117
debug "Found Balatro at $balatro_path"
95118
debug "Found Balatro appdata at $balatro_appdata_path"
96119

97-
step() { echo "${HINT}${NORMAL} $1"; }
120+
step() { echo " ${HINT}${NORMAL} $1"; }
98121

99122
is_balatro_installed() {
100123
[ -f "$balatro_path/Balatro.exe" ]
@@ -121,74 +144,65 @@ lovely_version() {
121144
}
122145

123146
is_steamodded_installed() {
124-
[ -n "$(compgen -G "$BALATRO_MODS/Steamodded-smods-*")" ]
147+
[ -n "$(compgen -G "$balatro_mods/Steamodded-smods-*")" ]
125148
}
126149

127150
steamodded_version() {
128151
if ! is_steamodded_installed; then
129152
echo "${WARN}not installed${NORMAL}"
130153
else
131-
cat "$BALATRO_MODS"/Steamodded-smods-*/release.lua | sed 's/return \|"\|-STEAMODDED//g;s/~BETA/-beta/g' | tr -d ' ' || echo "unknown"
154+
cat "$balatro_mods"/Steamodded-smods-*/release.lua | sed 's/return \|"\|-STEAMODDED//g;s/~BETA/-beta/g' | tr -d ' ' || echo "unknown"
132155
fi
133156
}
134157

135158

136159
SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )
137160

138-
status() {
139-
echo ""
140-
echo " ${GREEN}${NORMAL} cav: ${HINT}$version${NORMAL}"
141-
if [ -n "$1" ]; then
142-
echo " ${DIM}└ installed at ${HINT}$SCRIPT_DIR${NORMAL}"
143-
echo ""
144-
fi
145-
if is_balatro_installed; then
146-
echo " ${GREEN}${NORMAL} Balatro: ${HINT}$(balatro_version)${NORMAL}"
147-
if [ -n "$1" ]; then
148-
echo " ${DIM}└ installed at ${HINT}$balatro_path${NORMAL}"
149-
echo ""
150-
fi
151-
else
152-
echo " ${RED}${NORMAL} Balatro: ${HINT}not installed${NORMAL}"
153-
if [ -n "$1" ]; then
154-
echo ""
155-
fi
156-
fi
157-
if is_lovely_installed; then
158-
echo " ${GREEN}${NORMAL} Lovely: ${HINT}$(lovely_version)${NORMAL}"
159-
if [ -n "$1" ]; then
160-
echo " ${DIM}└ installed at ${HINT}$balatro_path/version.dll${NORMAL}"
161-
echo ""
162-
fi
161+
print_component() {
162+
local name=$1
163+
local version=$2
164+
local path=$3
165+
local verbose=$4
166+
167+
if [ "$version" = "${WARN}not installed${NORMAL}" ]; then
168+
echo " ${RED}${NORMAL} $name: ${HINT}$version${NORMAL}"
163169
else
164-
echo " ${RED}${NORMAL} Lovely: ${HINT}not installed${NORMAL}"
165-
if [ -n "$1" ]; then
166-
echo ""
170+
echo " ${GREEN}${NORMAL} $name: ${HINT}$version${NORMAL}"
171+
if [ -n "$verbose" ] && [ -n "$path" ]; then
172+
echo " ${DIM}└ installed at ${HINT}$path${NORMAL}"
167173
fi
168174
fi
169-
if is_steamodded_installed; then
170-
echo " ${GREEN}${NORMAL} Steamodded: ${HINT}$(steamodded_version)${NORMAL}"
171-
steamodded_path=$(compgen -G "$BALATRO_MODS/Steamodded-smods-*" | head -n 1)
172-
if [ -n "$1" ]; then
173-
echo " ${DIM}└ installed at ${HINT}$steamodded_path${NORMAL}"
174-
echo ""
175-
fi
176-
else
177-
echo " ${RED}${NORMAL} Steamodded: ${HINT}not installed${NORMAL}"
178-
if [ -n "$1" ]; then
179-
echo ""
180-
fi
175+
if [ -n "$verbose" ]; then
176+
echo ""
181177
fi
182178
}
183179

180+
status() {
181+
local verbose=$1
182+
echo ""
183+
print_component "cav" "$version" "$SCRIPT_DIR" "$verbose"
184+
print_component "Balatro" "$(balatro_version)" "$balatro_path" "$verbose"
185+
print_component "Lovely" "$(lovely_version)" "$balatro_path/version.dll" "$verbose"
186+
187+
local steamodded_path=$(compgen -G "$balatro_mods/Steamodded-smods-*" | head -n 1)
188+
print_component "Steamodded" "$(steamodded_version)" "$steamodded_path" "$verbose"
189+
}
190+
184191
list_versions() {
185-
versions=$(curl -sL "https://api.github.com/repos/$1/releases" | grep -E '"tag_name"|"published_at"' | \
186-
paste -d ' ' - - | sed -E 's/.*"tag_name": "([^"]+)".*"published_at": "([^T]+)T.*/ \1 '"${DIM}"'(\2)'"${NORMAL}"'/')
187-
if [ -n "$2" ]; then
188-
echo "${versions// $2/${HIGHLIGHT}» $2${NORMAL}}"
189-
else
190-
echo "$versions"
191-
fi
192+
local repo=$1
193+
local current_version=$2
194+
195+
curl -sL "https://api.github.com/repos/$repo/releases" | \
196+
grep -E '"tag_name"|"published_at"' | \
197+
paste -d ' ' - - | \
198+
sed -E 's/.*"tag_name": "([^"]+)".*"published_at": "([^T]+)T.*/\1 \2/' | \
199+
while read -r version date; do
200+
if [ "$version" = "$current_version" ]; then
201+
printf " ${HIGHLIGHT}» %-16s${NORMAL} ${DIM}(%s)${NORMAL}\n" "$version" "$date"
202+
else
203+
printf " %-16s ${DIM}(%s)${NORMAL}\n" "$version" "$date"
204+
fi
205+
done
192206
}
193207

194208
list_lovely() {
@@ -207,6 +221,17 @@ list_steamodded() {
207221
echo ""
208222
}
209223

224+
list_mods() {
225+
echo "Available mods:"
226+
echo ""
227+
for mod in "${!mod_desc[@]}"; do
228+
status=$(if [ -d "$balatro_mods/$mod}" ]; then echo "[${GREEN}${NORMAL}]"; else echo "[ ]"; fi)
229+
printf " $status ${HINT}%-16s${NORMAL} ${mod_desc[$mod]}\n" "$mod"
230+
done
231+
echo ""
232+
echo "Run ${HINT}cav add <mod>${NORMAL} to install a mod."
233+
}
234+
210235
install_lovely() {
211236
step "Downloading Lovely..."
212237
lovely_url=$(curl -sL https://api.github.com/repos/ethangreen-dev/lovely-injector/releases/latest | grep -m1 -Eo 'https://[^#]+windows-msvc.zip' )
@@ -251,25 +276,56 @@ install_steamodded() {
251276
}
252277

253278
install() {
254-
if compgen -G "$BALATRO_MODS/Steamodded-smods*" > /dev/null; then
255-
error "Steamodded is already installed."
256-
die "To reinstall, please run ${HINT}cav uninstall${NORMAL} first."
257-
fi
279+
local component=$1
258280

259-
kill_steam
260-
localconfig_path=$(compgen -G "$steam_root/userdata/*/config/localconfig.vdf" | head -n 1)
281+
case "$component" in
282+
lovely)
283+
kill_steam
284+
localconfig_path=$(compgen -G "$steam_root/userdata/*/config/localconfig.vdf" | head -n 1)
285+
install_lovely
286+
echo ""
287+
echo "Done! $cavendish"
288+
status
289+
;;
290+
steamodded)
291+
# Remove existing Steamodded if present
292+
if compgen -G "$BALATRO_MODS/Steamodded-smods*" > /dev/null; then
293+
kill_steam
294+
warn "Removing existing Steamodded installation..."
295+
rm -rf "$BALATRO_MODS"/Steamodded-smods-*
296+
else
297+
kill_steam
298+
fi
299+
install_steamodded
300+
echo ""
301+
echo "Done! $cavendish"
302+
status
303+
;;
304+
"")
305+
if compgen -G "$BALATRO_MODS/Steamodded-smods*" > /dev/null; then
306+
error "Steamodded is already installed."
307+
die "To reinstall, please run ${HINT}cav uninstall${NORMAL} first, or use ${HINT}cav install steamodded${NORMAL} to force-reinstall."
308+
fi
261309

262-
install_lovely
263-
install_steamodded
310+
kill_steam
311+
localconfig_path=$(compgen -G "$steam_root/userdata/*/config/localconfig.vdf" | head -n 1)
264312

265-
echo ""
266-
echo "Done! $cavendish"
267-
status
268-
echo ""
269-
echo "You may find it useful to add these lines to your shell config:"
270-
echo ""
271-
echo "${HINT}export BALATRO_PATH=\"${balatro_path}\"${NORMAL}"
272-
echo "${HINT}export BALATRO_MODS=\"${balatro_appdata_path%/}/Mods\"${NORMAL}"
313+
install_lovely
314+
install_steamodded
315+
316+
echo ""
317+
echo "Done! $cavendish"
318+
status
319+
echo ""
320+
echo "You may find it useful to add these lines to your shell config:"
321+
echo ""
322+
echo "${HINT}export BALATRO_PATH=\"${balatro_path}\"${NORMAL}"
323+
echo "${HINT}export BALATRO_MODS=\"${balatro_appdata_path%/}/Mods\"${NORMAL}"
324+
;;
325+
*)
326+
die "Unknown component: ${HINT}$component${NORMAL}. Use 'lovely' or 'steamodded'."
327+
;;
328+
esac
273329
}
274330

275331
uninstall() {
@@ -311,6 +367,32 @@ random_noun() {
311367
echo "${nouns[$RANDOM % ${#nouns[@]}]}"
312368
}
313369

370+
get_mod() {
371+
local mod_name=$1
372+
373+
if [ -z "$mod_name" ]; then
374+
die "Please specify a mod name. Run ${HINT}cav mods${NORMAL} to see available mods."
375+
fi
376+
377+
if [ -z "${mod_url[$mod_name]}" ]; then
378+
die "Unknown mod: ${HINT}$mod_name${NORMAL}. Run ${HINT}cav mods${NORMAL} to see available mods."
379+
fi
380+
381+
if [ -d "$balatro_mods/$mod_name}" ]; then
382+
die "Mod ${HINT}$mod_name${NORMAL} is already installed."
383+
# confirm "Do you want to reinstall it?" || die "Aborting."
384+
# kill_steam
385+
# rm -rf "$balatro_mods/${mod_path[$mod_name]}"
386+
else
387+
kill_steam
388+
fi
389+
390+
git clone "${mod_url[$mod_name]}" "$balatro_mods/$mod_name"
391+
echo ""
392+
echo "Done! $cavendish"
393+
echo "Installed ${HINT}$mod_name${NORMAL}"
394+
}
395+
314396
create_project() {
315397
name="$(random_adjective) $(random_noun)"
316398
custom_name=$(ask "Enter a name for your mod: ${DIM}(how about $name?)${NORMAL} ")
@@ -376,12 +458,16 @@ SMODS.Back{
376458

377459
usage() {
378460
echo "" >&2
379-
echo " ${HINT}cav install ${NORMAL}Install the latest version of Lovely and Steamodded." >&2
380-
echo " ${HINT}cav status ${NORMAL}Show installed versions." >&2
381-
echo " ${HINT}cav list lovely ${NORMAL}List available versions of Lovely." >&2
382-
echo " ${HINT}cav list steamodded ${NORMAL}List available versions of Steamodded." >&2
383-
echo " ${HINT}cav uninstall ${NORMAL}Uninstall Lovely and Steamodded." >&2
384-
echo " ${HINT}cav new ${NORMAL}Create a new Balatro mod project." >&2
461+
echo " ${HINT}cav install ${NORMAL}Install the latest version of Lovely and Steamodded." >&2
462+
echo " ${HINT}cav install lovely ${NORMAL}Force-reinstall the latest version of Lovely." >&2
463+
echo " ${HINT}cav install steamodded ${NORMAL}Force-reinstall the latest version of Steamodded." >&2
464+
echo " ${HINT}cav status ${NORMAL}Show installed versions." >&2
465+
echo " ${HINT}cav list lovely ${NORMAL}List available versions of Lovely." >&2
466+
echo " ${HINT}cav list steamodded ${NORMAL}List available versions of Steamodded." >&2
467+
echo " ${HINT}cav mods ${NORMAL}List available mods." >&2
468+
echo " ${HINT}cav get <mod> ${NORMAL}Download a mod from the list." >&2
469+
echo " ${HINT}cav uninstall ${NORMAL}Uninstall Lovely and Steamodded." >&2
470+
echo " ${HINT}cav new ${NORMAL}Create a new Balatro mod project." >&2
385471
}
386472

387473
case "$1" in
@@ -410,14 +496,20 @@ case "$1" in
410496
esac
411497
;;
412498
install)
413-
install
499+
install "$2"
414500
;;
415501
uninstall)
416502
uninstall
417503
;;
418504
new)
419505
create_project
420506
;;
507+
mods)
508+
list_mods
509+
;;
510+
get )
511+
get_mod "$2"
512+
;;
421513
*)
422514
echo "Invalid command: $1" >&2
423515
usage

0 commit comments

Comments
 (0)