Put the known traps in the catalog, as an open book
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>
This commit is contained in:
@@ -36,9 +36,12 @@ for _, p in ipairs(data.pairs) do
|
||||
JCA.pair_blurb[pair_key(p[1], p[2])] = p[3]
|
||||
end
|
||||
|
||||
-- Known traps: warning-only, never part of the synergy score.
|
||||
-- Known traps: warning-only, never part of the synergy score. The list is
|
||||
-- kept alongside the blurb map so the Traps tab can page over it; blurbs are
|
||||
-- always read back through clash_blurb so register_clash updates apply.
|
||||
JCA.clash_list = data.clashes or {}
|
||||
JCA.clash_blurb = {}
|
||||
for _, p in ipairs(data.clashes or {}) do
|
||||
for _, p in ipairs(JCA.clash_list) do
|
||||
JCA.clash_blurb[pair_key(p[1], p[2])] = p[3]
|
||||
end
|
||||
JCA.cautions = data.cautions or {}
|
||||
@@ -446,7 +449,12 @@ end
|
||||
function JCA.register_clash(a, b, warning)
|
||||
assert(type(a) == 'string' and type(b) == 'string',
|
||||
'JCA.register_clash: two joker keys required')
|
||||
JCA.clash_blurb[pair_key(a, b)] = warning
|
||||
local pk = pair_key(a, b)
|
||||
if not JCA.clash_blurb[pk] then
|
||||
JCA.clash_list[#JCA.clash_list + 1] = {a, b, warning}
|
||||
end
|
||||
JCA.clash_blurb[pk] = warning
|
||||
JCA._traps_dirty = true -- traps tab re-sorts on next open
|
||||
end
|
||||
|
||||
local function in_buy_area(card)
|
||||
@@ -1434,6 +1442,99 @@ local function combos_tab_def(page_no)
|
||||
colour = G.C.CLEAR, minw = 8.5, minh = 5.6}, nodes = nodes}
|
||||
end
|
||||
|
||||
-- Traps tab: the known clashes and cautions as an open book. 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 falling into the trap
|
||||
-- would reward fielding exactly the pairs the advisor exists to warn against.
|
||||
-- Clash pages come first (card duos, like Combos), then caution pages (single
|
||||
-- jokers); one pager spans both. Blurbs are read through clash_blurb /
|
||||
-- cautions at build time, so register_clash updates show without a rebuild.
|
||||
|
||||
local TRAPS_PER_PAGE = 6 -- 2 rows x 3 columns, clash and caution pages alike
|
||||
|
||||
-- One catalog column: the card(s) above the warning that explains the trap.
|
||||
local function trap_col(keys, blurb, wrap_width, max_lines)
|
||||
local col = {{n = G.UIT.R, config = {align = 'cm'},
|
||||
nodes = {card_row_node(keys, 0.62)}}}
|
||||
local cap = wrap_plain(blurb or '', wrap_width)
|
||||
for i = 1, math.min(#cap, max_lines) do
|
||||
col[#col + 1] = {n = G.UIT.R, config = {align = 'cm', padding = 0.01}, nodes = {
|
||||
{n = G.UIT.T, config = {text = cap[i], colour = G.C.RED, scale = 0.22}},
|
||||
}}
|
||||
end
|
||||
return {n = G.UIT.C, config = {align = 'cm', padding = 0.08}, nodes = col}
|
||||
end
|
||||
|
||||
local sorted_clashes, sorted_cautions
|
||||
local function traps_tab_def(page_no)
|
||||
begin_page()
|
||||
if not sorted_clashes or JCA._traps_dirty then
|
||||
JCA._traps_dirty = false
|
||||
sorted_clashes = {}
|
||||
for _, c in ipairs(JCA.clash_list) do
|
||||
sorted_clashes[#sorted_clashes + 1] = c
|
||||
end
|
||||
table.sort(sorted_clashes, function(a, b)
|
||||
local an, bn = name_of(a[1]), name_of(b[1])
|
||||
if an ~= bn then return an < bn end
|
||||
return name_of(a[2]) < name_of(b[2])
|
||||
end)
|
||||
sorted_cautions = {}
|
||||
for key in pairs(JCA.cautions) do
|
||||
sorted_cautions[#sorted_cautions + 1] = key
|
||||
end
|
||||
table.sort(sorted_cautions, function(a, b) return name_of(a) < name_of(b) end)
|
||||
end
|
||||
local clash_pages = math.ceil(#sorted_clashes / TRAPS_PER_PAGE)
|
||||
local caution_pages = math.ceil(#sorted_cautions / TRAPS_PER_PAGE)
|
||||
local pages = math.max(clash_pages + caution_pages, 1)
|
||||
page_no = math.min(page_no or 1, pages)
|
||||
|
||||
local nodes = {}
|
||||
if page_no <= clash_pages then
|
||||
add_caption(nodes, ('Clashes - %d known pairs where one eats the other\'s payoff')
|
||||
:format(#sorted_clashes))
|
||||
local first = (page_no - 1) * TRAPS_PER_PAGE
|
||||
for r = 0, 1 do
|
||||
local row = {n = G.UIT.R, config = {align = 'cm', padding = 0.05}, nodes = {}}
|
||||
for c = 1, 3 do
|
||||
local clash = sorted_clashes[first + r * 3 + c]
|
||||
if clash then
|
||||
row.nodes[#row.nodes + 1] = trap_col({clash[1], clash[2]},
|
||||
JCA.clash_blurb[pair_key(clash[1], clash[2])], 30, 2)
|
||||
end
|
||||
end
|
||||
if #row.nodes > 0 then nodes[#nodes + 1] = row end
|
||||
end
|
||||
else
|
||||
add_caption(nodes, ('Cautions - %d jokers that carry their own trap')
|
||||
:format(#sorted_cautions))
|
||||
local first = (page_no - clash_pages - 1) * TRAPS_PER_PAGE
|
||||
for r = 0, 1 do
|
||||
local row = {n = G.UIT.R, config = {align = 'cm', padding = 0.05}, nodes = {}}
|
||||
for c = 1, 3 do
|
||||
local key = sorted_cautions[first + r * 3 + c]
|
||||
if key then
|
||||
row.nodes[#row.nodes + 1] = trap_col({key}, JCA.cautions[key], 26, 3)
|
||||
end
|
||||
end
|
||||
if #row.nodes > 0 then nodes[#nodes + 1] = row end
|
||||
end
|
||||
end
|
||||
if pages > 1 then
|
||||
local options = {}
|
||||
for i = 1, clash_pages do
|
||||
options[#options + 1] = ('Clashes %d/%d'):format(i, clash_pages)
|
||||
end
|
||||
for i = 1, caution_pages do
|
||||
options[#options + 1] = ('Cautions %d/%d'):format(i, caution_pages)
|
||||
end
|
||||
nodes[#nodes + 1] = pager_row(options, page_no, 'jca_page_Traps')
|
||||
end
|
||||
return {n = G.UIT.ROOT, config = {align = 'tm', padding = 0.05,
|
||||
colour = G.C.CLEAR, minw = 8.5, minh = 5.6}, nodes = nodes}
|
||||
end
|
||||
|
||||
-- Progress tab: discovery completion at a glance. Text-only (no live Card
|
||||
-- objects), so unlike the other tabs nothing here would break if cached —
|
||||
-- but it is rebuilt each open like the rest, which keeps counts fresh.
|
||||
@@ -1517,6 +1618,7 @@ local function register_pager(name, def_fn)
|
||||
end
|
||||
|
||||
register_pager('Combos', combos_tab_def)
|
||||
register_pager('Traps', traps_tab_def)
|
||||
for _, group in ipairs(THEME_TABS) do
|
||||
register_pager(group.label, function(p) return theme_tab_def(group, p) end)
|
||||
end
|
||||
@@ -1533,6 +1635,10 @@ local function catalog_tabs()
|
||||
tab_definition_function = function() return theme_tab_def(t, 1) end,
|
||||
}
|
||||
end
|
||||
tabs[#tabs + 1] = {
|
||||
label = 'Traps',
|
||||
tab_definition_function = function() return traps_tab_def(1) end,
|
||||
}
|
||||
tabs[#tabs + 1] = {
|
||||
label = 'Progress',
|
||||
tab_definition_function = progress_tab_def,
|
||||
|
||||
Reference in New Issue
Block a user