608e23ed14
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
129 lines
7.1 KiB
Markdown
129 lines
7.1 KiB
Markdown
# CLAUDE.md
|
||
|
||
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
||
|
||
## Project
|
||
|
||
"Joker Combo Advisor" — a Steamodded (SMODS) mod for Balatro that scores shop/pack
|
||
jokers against the player's current jokers, shows a "Combo Advisor" tooltip on hover,
|
||
and pulses strongly recommended cards. Pure Lua, no build step.
|
||
|
||
## Commands
|
||
|
||
Syntax check everything:
|
||
|
||
```sh
|
||
luac -p main.lua synergies.lua config.lua
|
||
```
|
||
|
||
There is no test framework. Test the engine standalone by stubbing the game globals
|
||
before loading `main.lua` (the game itself cannot be launched headlessly here):
|
||
|
||
```sh
|
||
lua - <<'EOF'
|
||
SMODS = { load_file = function(f) return loadfile(f) end,
|
||
current_mod = { config = dofile('config.lua') } }
|
||
Card = { generate_UIBox_ability_table = function() end, update = function() end }
|
||
G = { UIT = {ROOT='ROOT', R='R', T='T'},
|
||
C = { UI = {TEXT_DARK='dark'}, GREEN='green', CLEAR='clear' },
|
||
STAGES = {RUN=1}, FUNCS = {} }
|
||
dofile('main.lua')
|
||
print(JCA.score('j_baron', 'j_mime')) -- pairwise score
|
||
-- simulate owned jokers via G.jokers.cards; each card needs
|
||
-- { config = { center = { key=..., name=..., set='Joker' } } }
|
||
EOF
|
||
```
|
||
|
||
Expected score tiers: famous explicit pair + tags ≈ 6, hand-type/suit cluster = 4,
|
||
single tag match = 2, generic mult+xmult complement = 1, unrelated = 0.
|
||
|
||
## In-game verification (this machine)
|
||
|
||
- The mod is **symlinked** into the live Mods folder:
|
||
`~/.local/share/Balatro/Mods/JokerComboAdvisor -> ~/Documents/Balatro Mod`.
|
||
Edits here go live on next game launch — do not copy files anywhere.
|
||
- Game: Steam/Proton at `/mnt/games/Steam/steamapps/common/Balatro` (note: second
|
||
Steam library, not the default paths). Lovely injector + Steamodded 1.0.0-beta
|
||
are already installed; mods are managed via Balatro Mod Manager.
|
||
- Crash/load logs: `~/.local/share/Balatro/Mods/lovely/log/` (newest file).
|
||
- Remote: `https://git.aleshym.co/funman300/JokerComboAdvisor` (private, user's
|
||
Gitea). Pushes authenticate via the repo-local credential helper, which
|
||
reads the token from `~/.config/tea/config.yml` at push time — never copy
|
||
the token into the repo or the remote URL. Commit per feature.
|
||
|
||
## Architecture
|
||
|
||
Three-layer split; `JCA` is the single global namespace:
|
||
|
||
- `synergies.lua` — data only. Returns `{ jokers = {...}, pairs = {...} }`. Each of
|
||
the 150 vanilla jokers gets `gives` (capabilities it adds) and `wants`
|
||
(capabilities that strengthen it) tag lists, converted to sets at the bottom of
|
||
the file. `PAIRS` lists famous explicit combos worth a flat +4.
|
||
- `main.lua` — engine + game integration:
|
||
- Scoring: `JCA.score(a, b)` = explicit pair bonus (+4) + 2 per give→want tag
|
||
match in each direction + 1 for the generic "+Mult/+Chips source with an
|
||
×Mult joker" complement. `JCA.partners_for(card)` aggregates over
|
||
`G.jokers.cards`; total ≥ `JCA.config.threshold` ⇒ recommended.
|
||
- UI hooks: wraps `Card:generate_UIBox_ability_table` to append a tooltip entry
|
||
to `aut.info` (format: array of rows-of-UIT-nodes plus a `.name` string —
|
||
must match what vanilla `info_tip_from_rows` expects), and wraps
|
||
`Card:update` for the ~2.5s pulse on recommended shop cards. Both hook bodies
|
||
are pcall-wrapped so a scoring bug can never crash a run — keep it that way.
|
||
- Config tab: registered on `SMODS.current_mod.config_tab`, guarded by
|
||
`if SMODS.current_mod` so standalone tests don't need full stubs.
|
||
- Synergy catalog: `SMODS.current_mod.extra_tabs` adds Combos/Engine/
|
||
Economy/Hands tabs that render real `Card` objects in `CardArea`s
|
||
(collection-style, so hover shows each joker's own tooltip). One theme
|
||
per page; pagers swap the `tab_contents` UIBox from an option-cycle
|
||
callback — the same pattern as SMODS's achievements tab. Because pages
|
||
contain live Card/CardArea objects, tab definitions must be rebuilt on
|
||
every call — never cache the node trees. The same tabs open in-run via
|
||
a "Combos" HUD button (wrapped `create_UIBox_HUD`, injected into the
|
||
`button_area` node under Run Info/Options); the pagers work there
|
||
because vanilla `create_tabs` also names its body `tab_contents`.
|
||
- Discovery: `CardArea:emplace` hook calls `JCA.check_discoveries` when a
|
||
joker lands in `G.jokers`; famous pairs fielded together persist in
|
||
`config.discovered` (saved immediately via `SMODS.save_mod_config`) and
|
||
per-run in `G.GAME.jca_run_combos`. Undiscovered pairs render face-down
|
||
with a `???` caption in the Combos tab; blurb replaces it once fielded.
|
||
- Post-run recap: wraps `create_UIBox_game_over`/`create_UIBox_win` and
|
||
inserts a "Combos fielded this run" row (from `G.GAME.jca_run_combos`)
|
||
relative to vanilla button ids (`from_game_over`; `from_game_won` or
|
||
`win_cta`) via a parent-trail walk — anchor depths differ per screen.
|
||
- Learning mode (`config.learning_mode`): suppresses the pulse and the
|
||
"Strong pick!" line but never the explanations. No-synergy buy-area
|
||
tooltips show "Looking for:"/"Offers:" hints from `TAG_LABEL`.
|
||
`synergies.lua` also exports `theme_info` (one teaching sentence per
|
||
catalog tag — keep ≤ ~90 chars and cover every THEME_TABS tag).
|
||
- The real (Lovely-patched) game source is readable at
|
||
`~/.local/share/Balatro/Mods/lovely/dump/` — check it before assuming
|
||
what a vanilla function returns or expects.
|
||
- `config.lua` — default settings (`pulse`, `owned_tooltip`, `threshold`).
|
||
Steamodded loads and persists this table itself; read it only via
|
||
`SMODS.current_mod.config` (exposed as `JCA.config`).
|
||
|
||
## Conventions and gotchas
|
||
|
||
- **Cluster tags** (hand types `pair`/`straight`/`flush`/…, suits, `planet`,
|
||
`spectral_gen`): put the same tag in *both* `gives` and `wants` of every member
|
||
so members mutually reinforce (2+2 = 4). Pure enablers (e.g. Four Fingers,
|
||
Smeared, Pareidolia) only `give`; pure payoffs only `want`.
|
||
- Every `wants` tag must be given by at least one joker — the standalone test's
|
||
orphan-tag check enforces this idea; re-run it after database edits.
|
||
- Tooltips teach: `JCA.explain(hovered, partner)` picks one reason per partner
|
||
— famous-pair blurb (third element of each `PAIRS` entry), else the first
|
||
`TAG_ORDER` match phrased from `TAG_TEXT` (give/want/both variants), else
|
||
the generic mult×xmult line. Every non-engine tag (anything besides
|
||
mult/chips/xmult) MUST have a `TAG_TEXT` entry; new `PAIRS` entries need a
|
||
blurb under ~34 chars so name + blurb fits one tooltip row.
|
||
- Vanilla center keys contain intentional misspellings/abbreviations — do not
|
||
"fix" them: `j_gluttenous_joker`, `j_selzer`, `j_ticket` (Golden Ticket),
|
||
`j_trousers` (Spare Trousers), `j_ring_master` (Showman), `j_caino` (Canio),
|
||
`j_delayed_grat`, `j_todo_list`.
|
||
- Unknown joker keys (from other mods) must keep scoring 0 — never index
|
||
`JCA.db[key]` without a nil guard.
|
||
- Display names go through `localize{type='name_text', set='Joker', ...}` with a
|
||
pcall + `center.name` fallback; anti-synergies are deliberately not modeled.
|
||
- The UI hooks are the only untested-in-game surface; if a tooltip regression is
|
||
reported, suspect the `aut.info` entry format first and check the lovely log.
|