Answer the fuse button: advise on fusions when Fusion Jokers is installed
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>
This commit is contained in:
@@ -208,7 +208,8 @@ Three-layer split; `JCA` is the single global namespace:
|
||||
- `integrations.lua` — cross-mod integrations, loaded at the very end of
|
||||
`main.lua` (pcall-wrapped; counts land in `JCA.integrations`). Every
|
||||
integration gates on its target's global — a missing mod is silence — and
|
||||
both targets load before us (JokerDisplay priority -1e9, TMJ -1000, ours 0):
|
||||
every target loads before us (JokerDisplay priority -1e9, Fusion Jokers
|
||||
-10000, TMJ -1000, ours 0):
|
||||
- **JokerDisplay**: appends a `(2 combos)` counter element to each known
|
||||
joker's `reminder_text` (created when absent) and wraps the definition's
|
||||
`calc_function` to fill `card.joker_display_values.jca_combos` from
|
||||
@@ -219,6 +220,22 @@ Three-layer split; `JCA` is the single global namespace:
|
||||
api.md's documented hook) returning the joker's `TAG_LABEL`s on both
|
||||
sides, so the collection search finds jokers by 'money', 'Hearts',
|
||||
'held-card retriggers'... Unknown centers return nil, never an error.
|
||||
- **Fusion Jokers**: installs `JCA.fusion_plans(card)`, which reports every
|
||||
fusion a joker is a component of — result, discounted cost, what is still
|
||||
missing, and **what fusing would cost you in combos**. That last part is
|
||||
the reason the integration exists: `fuse_card` calls `ingredient:remove()`
|
||||
on the components, so their partnerships with the rest of the board die,
|
||||
and nothing but this advisor knows what those were. Rendered in
|
||||
`tooltip_rows` (max 2 plans), ready fusions sorted first.
|
||||
Read the recipe table (`FusionJokers.fusions`, an array with the
|
||||
`ingredience[component][result]` reverse index hung off the same table) —
|
||||
**never call `Card:get_card_fusion()`**: it drives its price flicker with
|
||||
`math.randomseed(love.timer.getTime() * 8)`, so an advisor calling it
|
||||
would reseed Lua's RNG on every hover. Cost mirrors their arithmetic
|
||||
(flat then percentage discount, per-result and universal, floored, min $1)
|
||||
and affordability is only judged on plain numbers, because Talisman turns
|
||||
`G.GAME.dollars` into a big-number object. Components are counted by
|
||||
**quantity**, not presence — their own debug recipe needs 3× Joker.
|
||||
The smoke scenario asserts the JokerDisplay patch against the real mod.
|
||||
|
||||
## Conventions and gotchas
|
||||
@@ -290,6 +307,13 @@ partner, the clash and caution lines, discovery on emplace, and — the reason i
|
||||
exists — that catalog cards really do `start_materialize` on tab build *and* on
|
||||
page switch (asserted via `card.dissolve > 0` in the live UIBox).
|
||||
|
||||
It is also the only place the cross-mod integrations meet the real thing: the
|
||||
JokerDisplay counter against vanilla definitions, and the Fusion Jokers advice
|
||||
against the **real** recipe table (`test.lua` can only drive `JCA.fusion_plans`
|
||||
against a stub). Both scenarios no-op with a pass when their mod is absent, so
|
||||
the harness stays green on a clean install. Note a fake run starts at **$0** —
|
||||
a fusion affordability assertion has to set `G.GAME.dollars` itself.
|
||||
|
||||
The harness suppresses every save path and verifies from outside that no discovery
|
||||
state leaked, so running it can never spoil the catalog. Do not "fix" that by
|
||||
reversing writes after the fact — a crashed scenario would skip the reversal.
|
||||
|
||||
Reference in New Issue
Block a user