Two fixes from a review pass, plus the first tests to reach the post-run
recap at all.
The perishable caution said "gains die in 5 rounds" as a literal, but the
count is a game variable (G.GAME.perishable_rounds, game.lua:1961) and
vanilla's own sticker tooltip reads it (common_events.lua:3339). A challenge
or another mod moving it made the advisor state a wrong number, in the one
place this mod promises mechanical facts. It now reads perish_tally when the
sticker is already ticking, falls back to the game value, then to 5, and
says "1 round" rather than "1 rounds". The new tests fail against the old
literal, so this one was real.
The win screen's recap fallback tested only the second pcall return:
local _, inserted = pcall(insert_recap, ui, 'from_game_won', 2, 1)
if not inserted then ... win_cta ... end
The first return is the ok flag, so on a throw `inserted` holds the error
MESSAGE -- a truthy string -- and a failure reads as a success. That is a
trap worth removing, but being honest about its blast radius: it is NOT
currently reachable. Every way the first call can throw (recap_row, or
find_trail on a malformed tree) throws identically for win_cta, and the
table.insert cannot go out of bounds because spot.index indexes the very
list being inserted into. The whole suite passes with the old idiom. This
is a latent correctness fix, not a live bug -- it stops the trap biting if
insert_recap ever grows an anchor-specific failure.
The recap machinery (recap_row, find_trail, insert_recap) had no coverage.
It does now, including the win_cta fallback and the empty-run case.
925 tests pass on 5.4 and LuaJIT, 18/18 in the real game.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
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>
Localization was assessed and deliberately deferred: the mod's teaching
text lives in data blurbs (synergies.lua), so translating only the UI
chrome would produce a half-English interface; a real pass needs the
blurb schema redesigned around loc keys first.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
LuaJIT is the interpreter Balatro embeds, so it is the one that must
pass; the AUDIT.md diff check catches a synergies.lua change committed
without regenerating the map. Inert until the Gitea instance has an
Actions runner.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Perkeo duplicates a RANDOM held consumable (card.lua:2788), so Spectral
and Planet supply feed it exactly like Tarot supply always did - and the
spectral_gen theme gains the third member it needed (the Seance +
Perkeo negative-spectral archetype, now discoverable three ways).
The other five two-member themes (diamonds, hearts, four_kind, gold_gen,
stone_gen) are vanilla reality: no third joker exists. They get
FRAGILE_OK verdicts in audit.lua, rendered in AUDIT.md, and the audit
now fails on a fragile theme with no verdict - or a stale verdict for a
theme that grew - the same discipline as the silent-joker list.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Per-instance sticker cautions, unlike the per-joker CAUTIONS table: the
same joker is a fine buy without the sticker. Perishable debuffs a joker
for good after 5 rounds (Card:calculate_perishable, card.lua:2652), so a
perishable Ride the Bus or Hologram is investment about to be wiped --
the new SCALERS set in synergies.lua lists the jokers whose stored gains
die that way, excluding computed (Bull) and round-scoped (Hit the Road)
values and Egg, whose sell value survives a debuff. Rental charges
every round (card.lua:2647), which can eat a money joker's whole payout.
The scaler-key integrity test caught j_canio for j_caino on the first
run -- the misspelled vanilla keys strike again.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
A Planet card or Celestial Pack feeds Constellation; Arcana supply feeds
Fortune Teller; a Standard Pack permanently adds playing cards, which is
what card_gen wanters grow on -- and what shrinks Erosion, so that pack
gets a red caution when Erosion is owned (same verified mechanism as its
joker clashes, card.lua:4318). The tooltip stays silent when no owned
joker cares; Buffoon packs and vouchers are not advice material.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Card Sharp's x3 needs the same hand twice in ONE round -- it checks
played_this_round > 1 (card.lua:4465) and that counter resets each
round (state_events.lua:252). Burglar's +3 hands makes the replay
routine, so it gets the famous pair plus a hands_up want, same shape
as Burglar + Acrobat. The old verdict rejecting the Obelisk clash
stands; the audit entry now records both decisions.
Troubadour and Merry Andy join the cautions: their buried costs (one
fewer hand every round, one hand size) are the same shape as
Stuntman's existing warning.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Cross-checked all 150 jokers' tags against JOKER_EFFECTS.md and the dump:
- Drop the Vampire + Marble clash: Vampire gains xMult from any non-base
scoring card (card.lua:3907), Stones included, so Marble feeds it --
the same shape as the Midas Mask pair. The data even scored the duo
as a combo while warning against it. Stone-build harm stays covered
by the Vampire + Stone Joker line.
- Delayed Gratification wants discards_up: it pays per unused discard,
same shape as Banner.
- The Idol wants suit_flex: it checks is_suit (card.lua:3530), so
Smeared doubles its eligible cards, like Ancient Joker.
- 8 Ball wants retrig_scoring: the 1-in-4 rolls per scored 8 in the
individual pass (card.lua:3507), so retriggers re-roll it.
- Reword the suit give phrasings: nothing vanilla turns cards into a
suit, so 'turns cards into Spades' was a lie whenever Blackboard sat
across from a one-sided giver like Wrathful or Arrowhead.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
All 150 effects from balatrowiki.org, keyed by the real center keys
out of the game dump so the misspelled vanilla keys line up. Written
so tag reviews stop paraphrasing card behavior from memory - that is
how DNA briefly became a Pair enabler. The dump stays ground truth
for trigger conditions and pipeline timing.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
DNA only fires on the round's first hand and only for a single played
card (dump/card.lua:3956) - sacrificing a full hand to enable the
easiest hand in the game is not a synergy. Its trips/quads enabling
stays; that is what the one-copy-per-round loop actually builds toward.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The Water removes every discard at blind start (blind.lua:208, returned
only after the fight), idling discard granters and payoffs alike; The
Eye blocks a repeated hand type outright (blind.lua:189/577), giving a
one-hand build exactly one shot. BOSS_KILLS entries now carry a tag SET
and their own factual phrase, since "debuffs X" stopped being the only
true sentence. The Arm and The Ox were chased and rejected -- level
drain does not touch the planet jokers, and The Ox fires on the
player's own hand choice, the same reasoning that rejected the Card
Sharp/Obelisk clash -- recorded in the BOSS_KILLS comment so nobody
re-adds them.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
A Traps tab joins the synergy catalog: clash duos rendered like the
Combos tab, then the per-joker cautions, one pager spanning both page
kinds. Deliberately no discovery gating and no Progress entry -- the
Combos tab hides pairs to keep the fun of finding them, but a warning
locked behind fielding the bad pair would reward exactly what it warns
against. register_clash now maintains JCA.clash_list alongside the blurb
map so third-party clashes appear on the tab, re-registration updates
without duplicating, and blurbs are read back through clash_blurb at
build time so the newest wording always wins.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
A purchase always lands in the rightmost slot (buy_from_shop emplaces at
the end, button_callbacks.lua:2279), which the two copiers experience in
opposite ways: a bought Blueprint arrives copying nothing, a bought
Brainstorm copies the leftmost joker immediately. The buy-area tooltip
now says which, by name — JCA.copy_source_if_bought resolves the arrival
target (chaining through a leftmost copier exactly like copy_source),
and Blueprint's line names the joker to slot it against after buying.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
New integrations.lua, loaded pcall-wrapped at the end of main.lua;
every integration gates on its target's global so a missing mod is
silence. Both targets load before us by priority.
JokerDisplay: each known joker's display definition gains a live
'(2 combos)' counter in JD's own parenthetical reminder style --
the same partner count the hover tooltip names -- by appending a
dynamic element to reminder_text (created when absent) and wrapping
calc_function to fill card.joker_display_values. The wrapper pcalls
the original calc and blanks the text when the new jd_combos config
toggle is off, so it flips mid-run in both directions.
Too Many Jokers: registers the SEARCH_FIELD_FUNCS hook its api.md
documents, indexing every joker under its tag labels on both sides,
so the collection search finds jokers by 'money', 'Hearts',
'held-card retriggers'. Unknown centers stay nil.
The smoke scenario now asserts the JokerDisplay patch against the
real mod: 13/13 in-game.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The Mods symlink pointed at the repo's pre-rename home and the loader
skipped the mod without a word of complaint - every smoke check failed
with no JCA global before the link was repointed. Document that, the
BMM-disable marker that looks identical (.lovelyignore in the mod
folder), the real Steam library (/mnt/games), and where to re-clone
the smoke harness from.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
JCA.board_synergy sums every owned pair once; the emplace hook that
already drives discovery keeps the maximum in G.GAME.jca_peak_synergy
(it can only rise when a joker arrives). The post-run recap shows it,
now also on runs that fielded no famous pair.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Buy-area tooltips on suit-tagged jokers now show the deck ratio
('Your deck: 18 of 52 cards are Hearts', from card.base.suit -- the
field vanilla scores by) and hand-cluster jokers show the hand's level
when it has been built past 1. Pure context: the pairwise score never
reads the deck, the lines are capped at two, and iteration order is
fixed so the tooltip is stable across hovers.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Five bosses debuff a whole card class for the fight (game.lua P_BLINDS,
applied in Blind:debuff_card blind.lua:708): The Club/Goad/Head/Window
kill a suit, The Plant kills faces. A debuffed card scores nothing and
triggers nothing, so a shop joker wanting that class is a dead ante --
and one GIVING it is worse: Pareidolia under The Plant converts the
whole deck into debuffed faces, so givers warn too. JCA.upcoming_boss
reads G.GAME.round_resets and goes quiet once the boss is defeated.
Warning-only, never a score change, kept in learning mode.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
JCA.is_rental and JCA.is_perished read ability.rental and
ability.perishable/perish_tally, the same flags vanilla uses
(card.lua:684/676). The sell advisor now docks a rental 2 points (it
drains $3 every round, game.lua:1957) and treats a perished joker --
tally 0, debuffed permanently per card.lua:716 -- as dead weight that
outweighs any synergy score. Tooltips gain the mechanical facts:
countdown on ticking perishables, drain rate on rentals (shop phrasing
notes the $1 price), and 'Perished - it will never work again' instead
of the misleading 'does nothing this blind' on permanently dead cards.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
JCA.best_copy_target scans the board for copyable jokers (same truth
test as copy_source: blueprint_compat must be true, debuffed excluded,
other copiers excluded) and prefers the ones the database marks
copy_target. When a Blueprint or Brainstorm reports anything but 'ok',
the tooltip now adds the reorder that revives it: 'Fix: slot this just
left of Baron' / 'Fix: make Baron your leftmost joker'. Explanation,
not verdict, so learning mode keeps it.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
JCA.build_gaps() splits the board's tags into unmet wants (some joker
wants it, no OTHER joker gives it) and untapped hooks (given, nothing
pays it off). Rendered at the top of the in-run Combos overlay via
TAG_LABEL; engine roles stay excluded because they hint nothing.
Board slots are compared by index, not db-entry identity, so two
copies of one cluster joker correctly feed each other.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Business Card claimed a synergy with Hack, but Hack only retriggers
2s-5s and Business Card only pays on faces. The single retrig_scoring
tag lumped every retriggerer with every retrigger payoff.
retrig_scoring now means rank-agnostic (Dusk, Seltzer, Hanging Chad,
Splash). Hack gives retrig_low; Sock and Buskin gives retrig_faces.
Payoffs want only the scopes their ranks can reach: low-rank payoffs
(Even Steven, Odd Todd, Walkie Talkie, Fibonacci, Wee) add retrig_low,
face payoffs (Scary Face, Business, Photograph, Triboulet) add
retrig_faces, rank-agnostic ones (Hiker, the suit gems, The Idol) add
both. Scholar keeps only retrig_scoring - no retriggerer targets Aces.
This also kills Hack+Scholar, Hack+Scary Face, Hack+Photograph,
Hack+Triboulet, and Sock and Buskin vs every low-rank payoff.
Both new tags are full themes (catalog page, label, teaching line) so
Hack and Sock and Buskin stay visible in the Engine tab.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Four fixes, all traced to the running source.
* The sell advisor could tell you to sell an ETERNAL joker, which
Card:can_sell_card refuses outright (card.lua:1993). It now never names
one, though they still count toward the best score -- they are part of
the build you are keeping. Advice you cannot take is worse than none.
* Copiers CHAIN. SMODS.blueprint_effect recurses (utils.lua:2269) and
Blueprint's own blueprint_compat is true, so Blueprint -> Blueprint ->
Baron really does give two Barons. The tooltip used to answer "Copying
Blueprint", the one reply that helps nobody; it now walks the chain to
the joker actually copied and shows the route. Capped at the board size
like vanilla, and a Blueprint/Brainstorm ring resolves to nothing
instead of hanging.
* blueprint_compat is tested for TRUTH by vanilla (utils.lua:2254), not
for `~= false` as I had it. A modded joker that simply omits the flag is
NOT copyable, and this mod supports other mods' jokers by design, so
saying otherwise was a lie about someone else's content.
* A debuffed joker copies as nothing (same line), and a debuffed joker
does nothing at all this blind -- both now say so. Scoring is left
alone: a debuff is temporary and the advisor advises on the build, so
hiding a debuffed partner from "Active combos" would misrepresent it.
Also: never call SMODS.is_eternal() from an advisor path. It runs a full
SMODS.calculate_context per call (utils.lua:3079) -- far too heavy for a
hover -- and outside a live run it reported EVERY joker as Eternal, which
silently disabled the sell advisor. The smoke harness caught that; the
stub could not have.
721 pass locally, 12 in-game.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Every lead in AUDIT.md, traced to the running source and then settled.
Ceremonial Dagger DESTROYS the joker to its right at blind select
(card.lua:2945) and skips Eternals. The card says so; what it cannot say
is WHICH of your jokers is about to die. JCA.dagger_victim names it in
the tooltip -- "WILL DESTROY Baron at the next blind" -- while there is
still time to reorder, and stays quiet when the neighbour is Eternal.
Four jokers that scored 0 against all 150 now have partners, all verified
in card.lua rather than recalled:
* Burglar + Ramen: Ramen decays per discarded card and is DESTROYED at x1
(SMODS.destroy_cards). Burglar zeroes discards, so it never decays and
never dies. Same for Burglar + Green Joker, which is only punished by
discarding.
* Trading Card + Erosion: Erosion pays per card MISSING from the deck,
and Trading Card permanently destroys the card it discards.
* Chaos the Clown + Flash Card: a free reroll every shop, and a joker
that grows per reroll. Both were inert.
Erosion is the mirror of card_gen, so it now clashes with every joker
that permanently adds a card: DNA, Marble, Certificate.
Rejected, with the reasoning recorded so nobody "fixes" it later:
Card Sharp and Supernova reward replaying a hand and Obelisk punishes it,
but neither destroys the other's precondition -- the player's hand choice
does. That is an opposed incentive, not an opposed effect. The clash list
only earns trust while every entry is mechanical.
The audit's own silent-check was wrong: it flagged Square Joker, which
has been paired with Four Fingers all along, and I duplicated that pair
before the suite caught it. Silence now means no tags AND no famous pair.
711 pass locally, 10 in-game.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
test.lua is the gate -- it fails on what is broken. This is the map: it
reports what is merely suspicious, and (the actual point) it REMEMBERS.
32 jokers score ~0 against all 150: they give nothing but engine roles
and want nothing, so the advisor has nothing to say about them. For most
that is the correct answer, but I have now worked that question out from
scratch twice, and the reasoning evaporated both times. VERDICTS records
the conclusion for each one, so an audit only surfaces what is new.
It preserves the traps: Steel Joker and Glass Joker look exactly like
Blue Joker and Abstract Joker did, but they want Steel/Glass cards
specifically, and the only enhance_gen givers make Gold and Stone -- so
"fixing" them would invent a synergy that does not exist. That is written
down now instead of being rediscovered.
It also preserves the leads not yet chased (Ceremonial Dagger's
positional destroy, Erosion vs the card-adders, Burglar + Ramen and Green
Joker, Chaos + Flash Card), marked candidate because they are unverified.
A newly-silent joker lands in "never looked at" and fails the audit until
someone decides. AUDIT.md is committed so the map is readable without
running anything.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* The tooltip mis-taught every pure enabler. JCA.explain used a cluster
tag's `both` text whenever EITHER side was a full member, so Four
Fingers next to Runner claimed "both reward Straights" -- Four Fingers
does not reward Straights, it makes them reachable. `both` now requires
both sides, and every cluster tag carries give/want phrasings.
* Blue Joker and Abstract Joker scored 0 against all 150 jokers. Blue
Joker scales on #G.deck.cards, so it wants card_gen (DNA, Marble,
Certificate); Abstract scales on #G.jokers.cards, so it wants joker_gen
(Riff-Raff is +6 Mult on its own). Both verified in card.lua. Steel and
Glass Joker look like the same fix but are NOT: they need Steel/Glass
specifically, and the only enhance_gen givers make Gold and Stone, so
tagging them would invent a synergy that does not exist.
* Copy jokers are positional and the board order was there all along.
JCA.copy_source reports what an owned Blueprint/Brainstorm is actually
copying, including when the target is one of the 29 centers vanilla
refuses to copy (blueprint_compat = false), or when it is copying
nothing at all.
* The sell advisor now counts clashes when ranking what to cut. JCA.score
stays pure -- a trap must never make a card look like a combo -- but a
joker eating another one's payoff is exactly the one to sell.
The suite caught a real bug while writing this: `(mode == 'right') and
cards[idx+1] or cards[1]` falls through to the LEFTMOST joker when the
right-hand slot is nil, so a rightmost Blueprint would have named the
wrong card. 673 pass here, 9 in-game via the smoke harness.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Pushes authenticate through the global libsecret credential helper. The
tea login has auth_method oauth and no token at all, so it cannot create
repos or call the API - it was never what authenticated pushes.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The UI hooks are no longer an untested surface: BalatroSmoke boots the
real game and asserts the tooltip, clash, caution, discovery and card
materialize paths against live game state. All 7 pass.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Every entry here was checked against the running Steamodded source in
Mods/lovely/dump, not from memory.
The headline is a correction: the shipped CLASHES table warned that
Smeared Joker + Flower Pot "cannot make all four" suits. It is backwards.
Flower Pot fills its four suit slots with a greedy if/elseif chain
(card.lua:4256) and SMODS.smeared_check makes Hearts answer for Diamonds
and Spades for Clubs, so the second Heart falls through into the Diamond
slot: any two red + two black cards fill the pot. It is one of the
strongest combos in the game and the mod was steering players away from
it -- while simultaneously scoring the pair +2 via suit_flex, which
should have been the tell. It is now a famous pair.
New clashes (4 -> 22), all traced to the mechanism:
* Burglar zeroes discards_left on blind select (card.lua:2905), which
switches off every discard-gated joker for the rest of the run: Banner,
Delayed Gratification, Castle, Yorick, Hit the Road, Burnt, Mail-In
Rebate, Faceless, Trading Card, and it wipes the discards Drunkard and
Merry Andy hand you. Delayed Gratification is the cruellest -- "no
discards used" reads like a synergy, but the code also requires
discards_left > 0, so it pays exactly $0.
* Vampire strips enhancements in the `before` pass, ahead of the
per-card `individual` pass where enhancement payoffs are read, so it
eats what Golden Ticket, Glass, Stone Joker, Marble and Driver's
License are counting.
* Joker Stencil scales on EMPTY slots, so Riff-Raff and Abstract Joker
fight it.
* Splash makes every played card score, so a face card that used to be a
harmless kicker now resets Ride the Bus.
New cautions (3 -> 15): the positional traps (Blueprint dead in the
rightmost slot, Brainstorm in the leftmost), Mime doing nothing without a
held-card payoff, and the decay timers, whose numbers are exact from
game.lua (Ice Cream 100/5 = 20 hands, Popcorn 20/4 = 5 rounds).
Also corrects the stale game path in CLAUDE.md.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
There was no test file -- CLAUDE.md described the harness in prose and
referred to an "orphan-tag check" that did not exist. test.lua now stubs
the game globals and exercises the real engine, database and catalog tab
builders. It found three bugs on its first run:
* four_kind was the only theme in the catalog that could never be
discovered. Theme discovery needs a giver and a wanter on two DIFFERENT
fielded jokers, and The Family was its sole member -- so the Hands bar
and the overall Progress bar could never reach 100%. DNA is the real
enabler (copying a card is how you stack duplicate ranks, the only
vanilla route to a reliable Four of a Kind), so it now gives the
matching-hand tags as a pure enabler.
* hand_level was given by Space Joker and Burnt Joker and wanted by
nobody, so it could never contribute to a score and its "want" tooltip
text was unreachable. Nothing in vanilla rewards hand levels; the tag
is dropped rather than faked.
* Invisible Joker had no tags at all, so it scored 0 against the entire
game. Selling it duplicates one of your Jokers -- the same "stronger
target, better payoff" logic Blueprint and Brainstorm already model --
so it now wants copy_target.
The suite guards both directions of the tag contract (no orphan wants, no
dead gives), theme reachability, blurb widths, the inert-joker list, and
the card materialize fix from the previous commit.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Card rows in the Combos/theme tabs emplaced their cards without ever
calling Card:start_materialize, so cards popped in with no animation
when a tab opened or a page was switched. Every vanilla collection page
materializes as it emplaces (create_UIBox_your_collection_jokers).
Each card now materializes on emplace; begin_page() lets only the first
card of a page build play the sound, matching vanilla's i>1 or j>1
silence flag.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Other mods can teach the advisor their jokers (which otherwise score 0):
JCA.register(key, gives, wants, opts) adds a db entry with tag
validation — unknown tags are dropped with a log line so a typo can't
break anyone's load; opts.no_generic opts out of the mult/xmult
complement. register_pair adds a famous pair (discoverable, shown in
the Combos tab — its sort cache now invalidates via JCA._pairs_dirty)
and warns when a blurb exceeds the 34-char tooltip budget.
register_clash adds a warning-only trap line. Usage example in README.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Fifth catalog tab (also reachable from the in-run Combos button) showing
famous pairs fielded (x/31), per-group theme exploration bars
(Engine/Economy/Hands), pairs fielded in the current run, and an overall
discoveries tally with the how-to-discover hint. Text-only UI - no live
Card objects, no pager.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
JCA.weakest_link() scores each owned joker's total synergy against the
rest of the board (same capped scoring the shop advice uses) and returns
the lowest scorer — only when the board is full (that's when a cut
decision exists), has 3+ jokers, and one joker actually stands out
(uniform boards return nil). The owned-joker tooltip gains a red
'Weakest combo piece - a sell candidate' verdict line, suppressed in
learning mode like the other verdicts. Config toggle: sell_advisor.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Blueprint+Brainstorm, Mime+Raised Fist, Mime+Shoot the Moon,
Hack+Fibonacci, Photograph+Sock and Buskin, Smiley+Sock and Buskin,
Four Fingers+Shortcut, Four Fingers+Smeared, Smeared+Bloodstone,
Bull+To the Moon, Marble+Hologram, Egg+Campfire. All blurbs within the
34-char tooltip budget; discovery and the Combos tab pick them up
automatically.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
While hovering a shop/pack joker (or an owned one), the owned jokers it
synergizes with juice gently every ~0.9s, so the tooltip's partner names
are matched to cards by eye. Partner set is computed once per hover and
dropped when it ends. New config toggle: partner_highlight (default on);
stays active in learning mode since it explains rather than judges.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Warning-only data in synergies.lua — never touches the synergy score.
Tooltips gain a red 'Clashes - <owned joker>: <why>' line when a hovered
joker fights one you own (Ride the Bus + Pareidolia, Vampire vs Steel
Joker/Lucky Cat, Smeared vs Flower Pot), and a 'Caution:' line in the
buy area for jokers that harm a board you keep (Ceremonial Dagger,
Madness, Stencil). Learning mode keeps warnings — they teach.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Any xMult joker used to pulse 'recommended' on a board of four unrelated
+Mult/+Chips jokers: the generic engine-complement +1 in JCA.score
accumulated once per owned joker in partners_for, reaching the threshold
of 4 with zero listable partners (pairwise scores never hit 2), so the
card pulsed while its own tooltip said 'No synergy with your jokers.'
- partners_for now counts the generic +1 once per candidate. Tag/pair
scores are even, so the generic bonus can no longer flip a
recommendation at all; a pulsing card always has a nameable partner.
- Joker Stencil is the one xMult joker the generic rule actively
misreads (its multiplier grows with EMPTY slots), so entries support
{no_generic = true} which suppresses the rule in score() and the
generic fallback lines in explain(). Stencil also gains copy_target:
its real famous synergy is being copied by Blueprint/Brainstorm.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
A synergy theme counts as explored the first time two different
fielded jokers connect it (one gives the tag, one wants it; cluster
members carry both sides, so any two qualify). Explored themes
persist in config.themes_found, badge their catalog page with
"explored!", and feed a per-tab progress line. One toast per
emplacement, with famous pairs outranking themes.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Steamodded mod that teaches Balatro joker synergies: hover tooltips
explain why jokers combo (famous-pair blurbs + tag-based reasons),
combo discovery tracking with catalog grey-out and run recap, an
in-game synergy catalog with real card sprites, learning mode, and
a HUD shortcut.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>