From ea8faac434adeaad6f2c118a4669d12d6f40ca8f Mon Sep 17 00:00:00 2001 From: funman300 Date: Tue, 14 Jul 2026 13:44:52 -0700 Subject: [PATCH] Respect the other two stickers: rentals bleed, perished jokers are dead 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 --- main.lua | 48 ++++++++++++++++++++++++++++++++++++++++++++++-- test.lua | 33 +++++++++++++++++++++++++++++++++ 2 files changed, 79 insertions(+), 2 deletions(-) diff --git a/main.lua b/main.lua index 00e4f60..8197624 100644 --- a/main.lua +++ b/main.lua @@ -258,6 +258,13 @@ end -- so it always breaks a tie against a clashing joker. local CLASH_PENALTY = 3 +-- Sticker weights, same philosophy: mechanical facts only. A rental bleeds +-- $3 every round, so between two equally-scoring jokers, cut the rental. A +-- PERISHED joker (perishable, tally at 0) is debuffed forever -- dead weight +-- at any synergy score, so its penalty dwarfs every possible total. +local RENTAL_PENALTY = 2 +local PERISHED_PENALTY = 1000 + local function keep_score(card, cards) local _, total = JCA.partners_for(card) local key = card.config.center.key @@ -267,6 +274,8 @@ local function keep_score(card, cards) total = total - CLASH_PENALTY end end + if JCA.is_rental(card) then total = total - RENTAL_PENALTY end + if JCA.is_perished(card) then total = total - PERISHED_PENALTY end return total end @@ -284,6 +293,22 @@ function JCA.is_eternal(card) return not not (card.ability and card.ability.eternal) end +-- Rental and Perishable are the other two stickers the advisor must respect, +-- and both are plain ability flags like eternal (card.lua:684, 676). A rental +-- drains G.GAME.rental_rate every round (card.lua:2646; $3, game.lua:1957). +-- A perishable joker counts perish_tally down each round (card.lua:2652) and +-- once it hits 0 the card is debuffed PERMANENTLY (card.lua:716) -- not "this +-- blind", forever. Eternal and perishable are mutually exclusive (set_perishable +-- bails on eternals), so a perished joker is always legal to sell. +function JCA.is_rental(card) + return not not (card.ability and card.ability.rental) +end + +function JCA.is_perished(card) + local a = card.ability + return not not (a and a.perishable and (a.perish_tally or 0) <= 0) +end + function JCA.weakest_link() if not (G.jokers and G.jokers.cards) then return nil end local cards = G.jokers.cards @@ -567,13 +592,32 @@ local function tooltip_rows(card) local caution = in_buy_area(card) and JCA.cautions[key] if caution then text_row('Caution: ' .. caution, G.C.RED) end + -- Sticker facts (any area -- higher stakes put stickers on shop jokers + -- too). Mechanical numbers from the game source, not judgements, so + -- learning mode keeps them. + if card.ability and card.ability.perishable and not JCA.is_perished(card) then + local n = card.ability.perish_tally or 0 + text_row(('Perishable - %d round%s before it dies') + :format(n, n == 1 and '' or 's'), G.C.RED) + end + if JCA.is_rental(card) then + local rate = (G.GAME and G.GAME.rental_rate) or 3 + text_row(in_buy_area(card) + and ('Rental - $1 now, but $' .. rate .. ' every round after') + or ('Rental - drains $' .. rate .. ' every round'), G.C.RED) + end + -- Positional jokers: say what this one is doing to its neighbour RIGHT NOW. -- The board order decides it, so only an owned joker has an answer. These are -- explanations, not verdicts, so learning mode keeps them. if card.area == G.jokers then - -- A debuffed joker does nothing at all this blind, whatever its combos say. + -- A debuffed joker does nothing at all this blind, whatever its combos + -- say -- and a PERISHED one is debuffed for good (card.lua:716), which + -- deserves the honest tense. if card.debuff then - text_row('Debuffed - does nothing this blind', G.C.RED) + text_row(JCA.is_perished(card) + and 'Perished - it will never work again' + or 'Debuffed - does nothing this blind', G.C.RED) end -- Copy jokers. A Blueprint in the wrong slot is a dead card that looks fine. diff --git a/test.lua b/test.lua index daceef6..067c9e0 100644 --- a/test.lua +++ b/test.lua @@ -382,6 +382,39 @@ ok(pick ~= et[1], 'but never an Eternal one, because it cannot be sold', pick and pick.config.center.key or 'nil') eq(pick, et[2], 'it falls through to the next sellable candidate instead') +-------------------------------------------------------------------------------- +section('Engine: the sell advisor weighs rentals and perished jokers') +-------------------------------------------------------------------------------- + +-- ability.rental drains $3 a round (card.lua:2646, game.lua:1957); a perishable +-- joker whose tally ran out is debuffed FOREVER (card.lua:716). Both are flags +-- on card.ability, read the same way as eternal. +local plain = field('j_baron', 'j_joker') +eq(JCA.is_rental(plain[1]), false, 'no sticker reads as no rental') +plain[1].ability = {rental = true} +eq(JCA.is_rental(plain[1]), true, 'ability.rental reads as a rental') +plain[2].ability = {perishable = true, perish_tally = 3} +eq(JCA.is_perished(plain[2]), false, 'a ticking perishable has not perished yet') +plain[2].ability.perish_tally = 0 +eq(JCA.is_perished(plain[2]), true, 'tally 0 means debuffed for good') + +-- Three mutually inert jokers tie at 0, so nothing stands out to cut -- +-- until one of them is a rental, which bleeds money every round. +local rent = field('j_joker', 'j_half', 'j_misprint') +G.jokers.config.card_limit = 3 +eq(JCA.weakest_link(), nil, 'an all-tied board flags nobody') +rent[2].ability = {rental = true} +eq(JCA.weakest_link(), rent[2], 'the rental loses the tie: it drains $3 a round') + +-- A perished joker is dead weight at ANY synergy score: it outweighs even a +-- famous-pair member against the board's zero-scorer. +local per = field('j_baron', 'j_mime', 'j_joker') +G.jokers.config.card_limit = 3 +eq(JCA.weakest_link(), per[3], 'normally the zero-scorer is the cut') +per[2].ability = {perishable = true, perish_tally = 0} +eq(JCA.weakest_link(), per[2], + 'but a perished joker is the cut at any score - it will never work again') + -------------------------------------------------------------------------------- section('Engine: Ceremonial Dagger names its victim') --------------------------------------------------------------------------------