319f79bc5d
Hovering a joker that is a fusion component now names the fusion it feeds,
its cost with discounts applied, and what is still missing. In the shop the
card is counted as if already bought, so it answers the buy-side question
("Buy to fuse into Diamond Bard ($12 to fuse)") instead of restating what
you already own.
The line worth having is the last one. Fusing CONSUMES its components --
fuse_card calls ingredient:remove() -- so every combo those jokers held with
the rest of the board dies with them, and the FUSE button cannot tell you
that. "Fusing drops combos with: Smeared" is computed from the surviving
board, and a component being spent is not counted as a loss.
Three things about their code shaped this:
* Card:get_card_fusion() would have answered most of it, but it drives its
price flicker with math.randomseed(love.timer.getTime() * 8). An advisor
reseeding Lua's RNG on every hover has no business doing that, so this
reads FusionJokers.fusions directly and mirrors their discount arithmetic
(flat then percentage, per-result and universal, floored, min $1).
* Recipes take repeated components -- their own debug fusion needs 3x Joker
-- so components are counted by quantity, not presence.
* Affordability is judged only on plain numbers: Fusion Jokers uses to_big,
and Talisman turns G.GAME.dollars into an object. Guessing wrong about
someone's money is worse than staying quiet, so the money line just
does not appear.
Ownership is board membership, not card.area, matching copy_source and
dagger_victim -- the area pointer answers differently for the same card
depending on who built it.
Verified against the shipped recipe table: all 15 resolve at the right cost,
and all 30 components are jokers the database already knows. Not installed
means silence, like every other integration.
918 tests pass on 5.4 and LuaJIT (31 new), and 18/18 in the real game.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
167 lines
8.8 KiB
Markdown
167 lines
8.8 KiB
Markdown
# Joker Combo Advisor
|
||
|
||
A [Steamodded](https://github.com/Steamodded/smods) mod for Balatro that
|
||
recommends joker combos while you play:
|
||
|
||
- **Hover any joker in the shop or a Buffoon Pack** and a *Combo Advisor*
|
||
tooltip appears next to its description, naming which of your current
|
||
jokers it synergizes with **and why** ("Mime: retriggers cards in hand"),
|
||
so you learn the mechanics rather than just the verdict. Famous pairs get
|
||
hand-written explanations; everything else is phrased from the synergy
|
||
tags. Jokers with no current synergy teach instead what to pair them with
|
||
("Looking for: Tarot cards" / "Offers: face cards"). Strong picks also get
|
||
a "Strong pick for this build!" callout and **pulse periodically** in the
|
||
shop so you can spot them without hovering.
|
||
- **Combo discovery**: the first time you field a famous pair together, a
|
||
"New combo!" toast fires and the pair is permanently inked into the
|
||
catalog — until then its cards sit face-down with a "???" caption, so
|
||
filling in the Combos tab becomes its own collection hunt.
|
||
- **Theme discovery**: assembling any synergy theme for the first time —
|
||
one joker that provides it plus another that wants it — fires a
|
||
"Theme explored!" toast, badges that theme's catalog page, and counts
|
||
toward a per-tab progress line ("3 of 10 themes explored").
|
||
- **Anti-synergy warnings**: known traps get a red "Clashes" line when a
|
||
hovered joker fights one you own (Ride the Bus + Pareidolia, Vampire
|
||
vs Steel Joker…), and destructive jokers (Ceremonial Dagger, Madness,
|
||
Stencil) carry a "Caution" line in the shop. Warnings never change the
|
||
score — they just stop a bad buy.
|
||
- **Consumable advice**: shop consumables and booster packs get the same
|
||
treatment — a Planet card or Celestial Pack names the owned jokers it
|
||
feeds ("Feeds: Constellation"), Arcana supply points at Fortune Teller,
|
||
and a Standard Pack warns you when it would shrink Erosion's Mult. Quiet
|
||
unless one of your jokers actually cares.
|
||
- **Sticker cautions**: a perishable sticker on a scaling joker (Ride the
|
||
Bus, Hologram…) means its gains die with the 5-round timer, and a rental
|
||
sticker on a money joker can eat its whole payout — both get a red
|
||
caution in the buy area, per card, only when the sticker is present.
|
||
- **Partner highlight**: while you hover a joker, the owned jokers it
|
||
combos with pulse gently on the board, so the names in the tooltip are
|
||
matched to cards by eye.
|
||
- **Hover a joker you own** to see its active combos with the rest of your
|
||
lineup — and, when your board is full, a "weakest combo piece" flag on
|
||
the joker contributing least to the build, so you know the natural cut
|
||
when something better shows up.
|
||
- **Browse the synergy catalog** any time: a purple **Combos** button in the
|
||
run HUD (under Run Info / Options) opens it in an overlay, and the same
|
||
tabs live under Mods > Combo Advisor. The *Combos* tab shows the famous
|
||
pairs as card duos with their explanation once fielded; *Engine* /
|
||
*Economy* / *Hands* tabs page through each synergy theme with a one-line
|
||
explanation of the mechanic and its **Sources** and **Payoffs** as real
|
||
cards — hover any card for its normal description tooltip. All of it is generated
|
||
from the same database the advisor scores with. Jokers you haven't
|
||
discovered yet show face-down (no spoilers) unless the profile has used
|
||
the game's own **Unlock All** setting, in which case everything is shown.
|
||
- **Progress tab**: completion bars for famous pairs fielded and themes
|
||
explored per group, plus the pairs fielded in the current run.
|
||
- **Post-run combo recap**: the win and game-over screens list the famous
|
||
pairs you fielded during that run.
|
||
|
||
## How recommendations work
|
||
|
||
`synergies.lua` tags all 150 vanilla jokers with what they *give* a build
|
||
(e.g. `retrig_scoring`, `gold_gen`, `xmult`) and what they *want*
|
||
(e.g. `faces`, `probability`, `money`). Two jokers score:
|
||
|
||
| Signal | Points |
|
||
| --- | --- |
|
||
| Famous explicit pair (Baron + Mime, Midas Mask + Golden Ticket, …) | +4 |
|
||
| Each *gives* tag the other *wants* (both directions) | +2 |
|
||
| Generic engine complement (+Mult/+Chips source with an ×Mult joker) | +1 |
|
||
|
||
A shop joker is *recommended* (pulses) when its total score against your
|
||
owned jokers reaches the configured threshold (default 4). Partners with a
|
||
pairwise score of 2+ are listed in the tooltip, strongest first. The
|
||
generic +1 counts once per shop candidate — not once per owned joker — so
|
||
it can nudge a near-miss over the line but never carry a recommendation
|
||
by itself; a pulsing card always has at least one nameable partner.
|
||
|
||
## Settings
|
||
|
||
In-game: **Mods > Combo Advisor > Config**. Changes persist across sessions.
|
||
|
||
- **Pulse recommended jokers in shop** — turn the visual pulse on/off.
|
||
- **Show combo tooltip on owned jokers** — the "Active combos" section on
|
||
jokers you already hold.
|
||
- **Highlight combo partners on hover** — the on-board partner pulse.
|
||
- **Flag weakest joker when board is full** — the sell advisor line.
|
||
- **Learning mode** — hides the verdicts (pulse, "Strong pick!", the
|
||
weakest-joker flag) while keeping all the explanations and warnings, for
|
||
players who want to learn the synergies rather than be told what to buy.
|
||
- **Touch mode** — larger advice text and buttons for phone screens.
|
||
- **Recommendation threshold** — 2 (chatty) to 8 (only slam-dunk combos).
|
||
|
||
Defaults live in `config.lua`.
|
||
|
||
## Works with other mods
|
||
|
||
Each integration gates on the other mod's presence — if it isn't installed,
|
||
nothing happens and nothing is logged.
|
||
|
||
- **[Fusion Jokers](https://github.com/wingedcatgirl/Fusion-Jokers)** — hover
|
||
any joker that is a fusion component and the advisor names the fusion it
|
||
feeds, its cost (discounts included), and what you are still missing:
|
||
*"Fuses into Diamond Bard ($12) — still needs Rough Gem"*. In the shop it
|
||
answers the buy-side question instead: *"Buy to fuse into Diamond Bard
|
||
($12 to fuse)"*. Because fusing **consumes** its components, a ready fusion
|
||
also tells you what you'd give up — *"Fusing drops combos with: Smeared"* —
|
||
which is the one thing the FUSE button can't tell you itself.
|
||
- **[JokerDisplay](https://github.nh6574.com/JokerDisplay)** — a live
|
||
`(2 combos)` counter under every joker, toggleable in the config tab.
|
||
- **Too Many Jokers** — its collection search also matches synergy tags, so you
|
||
can search for `money`, `Hearts`, or `held-card retriggers`.
|
||
|
||
## Installation
|
||
|
||
1. Install [Lovely](https://github.com/ethangreen-dev/lovely-injector) and
|
||
[Steamodded](https://github.com/Steamodded/smods) (follow their READMEs).
|
||
2. Copy this whole folder into your Balatro `Mods` directory:
|
||
- **Windows:** `%AppData%/Balatro/Mods/`
|
||
- **Linux (Steam/Proton):**
|
||
`~/.local/share/Steam/steamapps/compatdata/2379780/pfx/drive_c/users/steamuser/AppData/Roaming/Balatro/Mods/`
|
||
(or the same path under `~/.steam/steam/`, or
|
||
`~/.var/app/com.valvesoftware.Steam/.local/share/Steam/...` for Flatpak Steam)
|
||
- **macOS:** `~/Library/Application Support/Balatro/Mods/`
|
||
3. Launch Balatro. The mod should appear in the in-game **Mods** menu.
|
||
If something goes wrong, check the Lovely log
|
||
(`Mods/lovely/log/` next to the game, or the console window on launch).
|
||
|
||
## Extending it
|
||
|
||
- **Add or fix a synergy:** edit `synergies.lua`. Give a joker a tag with
|
||
`j('j_key', {gives...}, {wants...})`, or add a famous pair to the `PAIRS`
|
||
table at the bottom. Modded jokers can be added the same way using their
|
||
full key (e.g. `j_mymod_cooljoker`) — unknown jokers simply score 0.
|
||
- **From another mod:** if your mod adds jokers, teach the advisor about
|
||
them at load time (any point after Combo Advisor has loaded):
|
||
|
||
```lua
|
||
if JCA then
|
||
-- gives/wants use the tag names from synergies.lua
|
||
JCA.register('j_mymod_cooljoker', {'xmult'}, {'faces'})
|
||
-- optional: opts {no_generic = true} if the generic
|
||
-- "+Mult feeds the multiplier" logic misreads your joker
|
||
JCA.register('j_mymod_slotless', {'xmult'}, nil, {no_generic = true})
|
||
-- famous pair (+4, discoverable, shown in the Combos tab);
|
||
-- keep the blurb under ~34 characters
|
||
JCA.register_pair('j_mymod_cooljoker', 'j_pareidolia',
|
||
'every card becomes a face for it')
|
||
-- known trap: warning-only red tooltip line
|
||
JCA.register_clash('j_mymod_cooljoker', 'j_vampire',
|
||
'drains the enhancements it needs')
|
||
end
|
||
```
|
||
|
||
Unknown tags are dropped with a `JCA:` log line instead of erroring, so
|
||
a typo in your registration can never break a load.
|
||
- **Tune sensitivity:** use the in-game Config tab, or change the defaults
|
||
in `config.lua`.
|
||
|
||
## Files
|
||
|
||
| File | Purpose |
|
||
| --- | --- |
|
||
| `JokerComboAdvisor.json` | Steamodded mod metadata |
|
||
| `main.lua` | Scoring engine, hover tooltip, shop pulse, config tab |
|
||
| `synergies.lua` | Tag database for all 150 vanilla jokers + famous pairs |
|
||
| `config.lua` | Default settings (persisted by Steamodded once changed in-game) |
|