Move the synergy catalog from the run HUD to the pause menu
The purple Combos button lived in the run HUD button_area; move it to a "Combos" row in the pause menu (Escape > under Settings, before restart_button) so the play screen stays clear. Same overlay, same tabs; Mods > Combo Advisor is unchanged. restart_button is an id vanilla only builds during a run, so the row never reaches the main menu. Because the pause menu builds its node list as a table constructor with conditional entries (an unseeded run leaves a hole at the seed row's index), find_trail/insert_node walk and shift by explicit numeric key rather than ipairs/table.insert. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -561,6 +561,79 @@ end
|
||||
|
||||
-- Hover tooltip ---------------------------------------------------------------
|
||||
|
||||
-- Tooltip width -----------------------------------------------------------
|
||||
-- Advice rows are plain strings, but the hover box grows to fit the WIDEST of
|
||||
-- them: vanilla's info_tip_from_rows sets no `maxw` on its rows
|
||||
-- (UI_definitions.lua:1124), unlike desc_from_rows which accepts one. So a
|
||||
-- single long row (a fusion recipe, a copy chain, a boss caution) pushes the
|
||||
-- whole box off the edge of the screen, and counting characters cannot catch
|
||||
-- it -- the same 60 characters are 40% wider in touch mode and wider again in
|
||||
-- a CJK font.
|
||||
--
|
||||
-- So measure, with the same arithmetic the engine uses (engine/ui.lua:144):
|
||||
-- units = FONT:getWidth(s) * squish * scale * FONTSCALE / TILESIZE
|
||||
-- G.TILESCALE appears in both the numerator and the denominator there and
|
||||
-- cancels, which is worth stating plainly: Balatro's layout is
|
||||
-- resolution-INDEPENDENT. love.resize keeps G.ROOM.T.w pinned to the constant
|
||||
-- G.TILE_W (main.lua:1331, globals.lua:282 = 20) and only changes how many
|
||||
-- pixels a unit is worth. A bigger window does not buy the tooltip more room,
|
||||
-- so the budget below is a fraction of the room, not of the screen.
|
||||
local FALLBACK_UNITS_PER_CHAR = 0.41 -- m6x11plus, measured; headless only
|
||||
local ROW_WIDTH_FRACTION = 0.24 -- ~4.8 of the room's 20 units
|
||||
|
||||
-- Width of `str` at `scale`, in game units.
|
||||
function JCA.text_width(str, scale)
|
||||
local f = G.LANG and G.LANG.font
|
||||
if f and f.FONT and G.TILESIZE then
|
||||
local ok, w = pcall(function()
|
||||
return f.FONT:getWidth(str) * (f.squish or 1) * scale
|
||||
* (f.FONTSCALE or 0.1) / G.TILESIZE
|
||||
end)
|
||||
if ok and type(w) == 'number' and w > 0 then return w end
|
||||
end
|
||||
-- Outside the game (tests) there is no font: fall back to a per-character
|
||||
-- estimate so callers still get proportional, deterministic answers.
|
||||
return #tostring(str) * scale * FALLBACK_UNITS_PER_CHAR
|
||||
end
|
||||
|
||||
-- How wide one advice row may be, in game units. Read from the room rather
|
||||
-- than hardcoded, so it still holds if a future Balatro changes G.TILE_W.
|
||||
function JCA.row_width_budget()
|
||||
local room = G.ROOM and G.ROOM.T and G.ROOM.T.w
|
||||
return (type(room) == 'number' and room > 0 and room or 20) * ROW_WIDTH_FRACTION
|
||||
end
|
||||
|
||||
-- Word-wrap `str` to the measured budget, returning finished lines with the
|
||||
-- hanging `indent` already applied to continuations.
|
||||
--
|
||||
-- The indent is RESERVED while wrapping, not bolted on afterwards. Adding it
|
||||
-- after the fact is exactly how a line that just fits becomes one that does
|
||||
-- not, and it is a quiet two-space overflow that no amount of eyeballing
|
||||
-- catches -- the suite caught it here.
|
||||
--
|
||||
-- A single word wider than the budget keeps its own over-long line rather than
|
||||
-- being cut mid-word: a clipped joker name is worse than a slightly wide row.
|
||||
function JCA.wrap_measured(str, scale, budget, indent)
|
||||
budget = budget or JCA.row_width_budget()
|
||||
indent = indent == nil and ' ' or indent
|
||||
local pad = indent ~= '' and JCA.text_width(indent, scale) or 0
|
||||
local lines, line = {}, nil
|
||||
for word in tostring(str):gmatch('%S+') do
|
||||
local nxt = line and (line .. ' ' .. word) or word
|
||||
local limit = #lines == 0 and budget or budget - pad
|
||||
if line and JCA.text_width(nxt, scale) > limit then
|
||||
lines[#lines + 1] = line
|
||||
line = word
|
||||
else
|
||||
line = nxt
|
||||
end
|
||||
end
|
||||
if line then lines[#lines + 1] = line end
|
||||
if #lines == 0 then return {''} end
|
||||
for i = 2, #lines do lines[i] = indent .. lines[i] end
|
||||
return lines
|
||||
end
|
||||
|
||||
-- Wrap partner names into short tooltip lines.
|
||||
local function wrap_names(partners, max_width, max_lines)
|
||||
local lines, line = {}, nil
|
||||
@@ -581,91 +654,6 @@ local function wrap_names(partners, max_width, max_lines)
|
||||
return lines
|
||||
end
|
||||
|
||||
-- Every advice line has to fit the shop's left gutter. The hover info popup is
|
||||
-- right-anchored to the card and grows LEFTWARD with no screen clamp -- vanilla
|
||||
-- align_h_popup leaves lr_clamp off (UI_definitions.lua) -- so its width is set
|
||||
-- by its single widest row, and a row wider than the gutter runs off the left
|
||||
-- edge and clips (the reported bug: a long "Fuses into X - still needs Y" line).
|
||||
-- Keep every rendered line at or under this many characters.
|
||||
local ROW_BUDGET = 36
|
||||
JCA._row_budget = ROW_BUDGET
|
||||
|
||||
-- Word-wrap a sentence to <= budget chars per line, breaking only on spaces so a
|
||||
-- joker name is never split. An overlong single word is left whole (better one
|
||||
-- wide line than a mangled name).
|
||||
local function wrap_text(str, budget)
|
||||
str = tostring(str)
|
||||
-- Preserve a leading indent (callers pass " " to mark continuation lines);
|
||||
-- gmatch would otherwise drop it. The indent counts against the budget.
|
||||
local indent = str:match('^%s+') or ''
|
||||
local body_budget = budget - #indent
|
||||
local lines, line = {}, nil
|
||||
for word in str:gmatch('%S+') do
|
||||
local nxt = line and (line .. ' ' .. word) or word
|
||||
if line and #nxt > body_budget then
|
||||
lines[#lines + 1] = line
|
||||
line = word
|
||||
else
|
||||
line = nxt
|
||||
end
|
||||
end
|
||||
lines[#lines + 1] = line or ''
|
||||
lines[1] = indent .. lines[1]
|
||||
return lines
|
||||
end
|
||||
JCA._wrap_text = wrap_text
|
||||
|
||||
-- Dynamic width. The gutter a card leaves for its tooltip depends on where the
|
||||
-- card sits -- vanilla opens the popup to the LEFT of a card past 40% of the
|
||||
-- room and to the RIGHT of the rest (show_infotip, UI_definitions.lua) -- and on
|
||||
-- the room width, which is a CONSTANT in game units whatever the window size
|
||||
-- (love.resize only rescales pixels, main.lua:1304). So we size the wrap budget
|
||||
-- to the real gutter: roomy for a far-side card, tight near the flip point, and
|
||||
-- it tracks a room-widening/ultrawide mod for free. Anything missing (headless
|
||||
-- tests) falls back to the fixed budget.
|
||||
local ROW_BUDGET_MIN = 24
|
||||
local ROW_BUDGET_MAX = 60
|
||||
-- The card's OWN description popup sits between our tooltip and the card, eating
|
||||
-- into the gutter. A joker's desc box is ~3.1-3.3 units wide; we subtract 4.6 so
|
||||
-- the budget always UNDER-estimates the true capacity -- with the smoke-measured
|
||||
-- metric (roomw 20, ~0.137 units/char at scale 0.32) that margin keeps every
|
||||
-- card, even one right at the 40% flip, clear of the edge. dbg() logs the inputs.
|
||||
local MAIN_BOX_UNITS = 4.6
|
||||
|
||||
-- Width of one average character in room units at a text scale, from the game's
|
||||
-- own text-measuring formula (engine/ui.lua:143) -- exact and window-size
|
||||
-- independent. nil when the font is not loaded (standalone tests).
|
||||
local function unit_char_width(scale)
|
||||
local f = G.LANG and G.LANG.font
|
||||
local FONT = f and f.FONT
|
||||
if not (FONT and FONT.getWidth and G.TILESIZE) then return nil end
|
||||
local sample = 'abcdefghijklmnopqrstuvwxyz ,.-()ABCDEFGHIJKLMNOPQRSTUVWXYZ'
|
||||
local ok, px = pcall(function()
|
||||
return FONT:getWidth(sample) * (f.squish or 1) * scale * (f.FONTSCALE or 1)
|
||||
end)
|
||||
if not (ok and type(px) == 'number' and px > 0) then return nil end
|
||||
return px / G.TILESIZE / #sample
|
||||
end
|
||||
|
||||
-- Characters that fit one tooltip row for THIS card, clamped to a sane range.
|
||||
local function row_budget(card)
|
||||
local room = G.ROOM and G.ROOM.T
|
||||
local T = card and card.T
|
||||
if not (room and T and T.x and room.w) then return ROW_BUDGET end
|
||||
-- Measure at our widest row scale (0.32) so the budget is safe for every row.
|
||||
local cw = unit_char_width(0.32)
|
||||
if not cw or cw <= 0 then return ROW_BUDGET end
|
||||
local gutter = (T.x > room.w * 0.4) and T.x
|
||||
or (room.w - (T.x + (T.w or 0)))
|
||||
gutter = gutter - MAIN_BOX_UNITS
|
||||
-- *1.08: a hair of slack so a line of wider-than-average glyphs still fits.
|
||||
local budget = math.floor(gutter / (cw * 1.08))
|
||||
dbg('row_budget x=', T.x, 'roomw=', room.w, 'cw=', cw,
|
||||
'gutter=', gutter, '->', budget)
|
||||
return math.max(ROW_BUDGET_MIN, math.min(ROW_BUDGET_MAX, budget))
|
||||
end
|
||||
JCA._row_budget_for = row_budget
|
||||
|
||||
-- Short theme nouns for the "Looking for:"/"Offers:" hints (engine roles
|
||||
-- mult/chips/xmult are deliberately absent — they hint nothing specific).
|
||||
local TAG_LABEL = {
|
||||
@@ -767,35 +755,43 @@ local function tooltip_rows(card)
|
||||
local S = JCA.config.touch_mode and 1.4 or 1
|
||||
local key = card.config.center.key
|
||||
local partners, total = JCA.partners_for(card)
|
||||
local budget = row_budget(card)
|
||||
|
||||
local rows = {}
|
||||
local function text_row(str, colour)
|
||||
for _, line in ipairs(wrap_text(str, budget)) do
|
||||
-- Every row goes through here, so the width bound holds for all of them --
|
||||
-- including rows added later by someone who never read this comment.
|
||||
-- Continuations are indented so a wrapped sentence reads as one thought.
|
||||
local function text_row(str, colour, scale)
|
||||
scale = scale or 0.32 * S
|
||||
for _, line in ipairs(JCA.wrap_measured(str, scale)) do
|
||||
rows[#rows + 1] = {{n = G.UIT.T, config = {
|
||||
text = line, colour = colour or G.C.UI.TEXT_DARK, scale = 0.32 * S,
|
||||
text = line, colour = colour or G.C.UI.TEXT_DARK, scale = scale,
|
||||
}}}
|
||||
end
|
||||
end
|
||||
-- A coloured label followed by a body sentence ("Baron: retriggers Kings").
|
||||
-- Kept on one row when the pair fits; otherwise the label takes its own row
|
||||
-- and the body wraps beneath it, indented -- so neither a long joker name
|
||||
-- nor a long blurb can push the row off the screen edge.
|
||||
local function seg_row(label, label_colour, body)
|
||||
if #label + #body <= budget then
|
||||
|
||||
-- Two-tone rows ("Mime: retriggers cards in hand"). Kept on one line while
|
||||
-- the pair fits; once it does not, the label takes its own line and the
|
||||
-- body wraps beneath it, which keeps the colour split readable instead of
|
||||
-- splitting a coloured phrase mid-word across rows.
|
||||
local function labelled_row(label, label_colour, body)
|
||||
local scale = 0.3 * S
|
||||
local budget = JCA.row_width_budget()
|
||||
if JCA.text_width(label .. body, scale) <= budget then
|
||||
rows[#rows + 1] = {
|
||||
{n = G.UIT.T, config = {text = label,
|
||||
colour = label_colour, scale = 0.3 * S}},
|
||||
{n = G.UIT.T, config = {text = label, colour = label_colour, scale = scale}},
|
||||
{n = G.UIT.T, config = {text = body,
|
||||
colour = G.C.UI.TEXT_DARK, scale = 0.3 * S}},
|
||||
colour = G.C.UI.TEXT_DARK, scale = scale}},
|
||||
}
|
||||
return
|
||||
end
|
||||
rows[#rows + 1] = {{n = G.UIT.T, config = {
|
||||
text = label, colour = label_colour, scale = 0.3 * S}}}
|
||||
for _, line in ipairs(wrap_text(body, budget - 2)) do
|
||||
rows[#rows + 1] = {{n = G.UIT.T, config = {
|
||||
text = ' ' .. line, colour = G.C.UI.TEXT_DARK, scale = 0.3 * S}}}
|
||||
text = label, colour = label_colour, scale = scale}}}
|
||||
-- The label owns the line above, so EVERY body line is indented here,
|
||||
-- not just continuations -- reserve the indent for all of them.
|
||||
local pad = JCA.text_width(' ', scale)
|
||||
for _, line in ipairs(JCA.wrap_measured(body, scale, budget - pad, '')) do
|
||||
rows[#rows + 1] = {{n = G.UIT.T, config = {text = ' ' .. line,
|
||||
colour = G.C.UI.TEXT_DARK, scale = scale}}}
|
||||
end
|
||||
end
|
||||
|
||||
@@ -810,12 +806,11 @@ local function tooltip_rows(card)
|
||||
labels, prefix = tag_labels(entry.gives), 'Offers: '
|
||||
end
|
||||
if #labels > 0 then
|
||||
for i, l in ipairs(wrap_names(labels, budget - #prefix, 2)) do
|
||||
rows[#rows + 1] = {{n = G.UIT.T, config = {
|
||||
text = (i == 1 and prefix or ' ') .. l,
|
||||
colour = G.C.UI.TEXT_DARK, scale = 0.3 * S,
|
||||
}}}
|
||||
end
|
||||
-- One sentence, measured-wrapped, instead of a guessed
|
||||
-- character count: text_row already bounds the width.
|
||||
local names = {}
|
||||
for _, l in ipairs(labels) do names[#names + 1] = l.name end
|
||||
text_row(prefix .. table.concat(names, ', '), nil, 0.3 * S)
|
||||
end
|
||||
end
|
||||
else
|
||||
@@ -828,14 +823,12 @@ local function tooltip_rows(card)
|
||||
for i = 1, shown do
|
||||
local p = partners[i]
|
||||
local reason = JCA.explain(key, p.key) or 'shared build theme'
|
||||
seg_row(p.name .. ': ', G.C.RED, reason)
|
||||
labelled_row(p.name .. ': ', G.C.RED, reason)
|
||||
end
|
||||
if #partners > shown then
|
||||
local rest = {}
|
||||
for i = shown + 1, #partners do rest[#rest + 1] = partners[i] end
|
||||
for i, l in ipairs(wrap_names(rest, budget - 6, 2)) do
|
||||
text_row((i == 1 and 'also: ' or ' ') .. l, G.C.UI.TEXT_DARK)
|
||||
end
|
||||
for i = shown + 1, #partners do rest[#rest + 1] = partners[i].name end
|
||||
text_row('also: ' .. table.concat(rest, ', '), G.C.UI.TEXT_DARK)
|
||||
end
|
||||
if in_buy_area(card) and total >= JCA.config.threshold
|
||||
and not JCA.config.learning_mode then
|
||||
@@ -849,7 +842,7 @@ local function tooltip_rows(card)
|
||||
if owned ~= card and owned.config.center.set == 'Joker' then
|
||||
local warn = JCA.clash_blurb[pair_key(key, owned.config.center.key)]
|
||||
if warn then
|
||||
seg_row('Clashes - ' .. name_of(owned.config.center.key) .. ': ',
|
||||
labelled_row('Clashes - ' .. name_of(owned.config.center.key) .. ': ',
|
||||
G.C.RED, warn)
|
||||
end
|
||||
end
|
||||
@@ -931,11 +924,9 @@ local function tooltip_rows(card)
|
||||
end
|
||||
if #p.loses > 0 then
|
||||
local names = {}
|
||||
for _, k in ipairs(p.loses) do names[#names + 1] = {name = name_of(k)} end
|
||||
text_row('Fusing drops combos with:', G.C.RED)
|
||||
for _, l in ipairs(wrap_names(names, budget - 2, 2)) do
|
||||
text_row(' ' .. l, G.C.RED)
|
||||
end
|
||||
for _, k in ipairs(p.loses) do names[#names + 1] = name_of(k) end
|
||||
text_row('Fusing drops combos with: ' .. table.concat(names, ', '),
|
||||
G.C.RED)
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -1080,7 +1071,13 @@ local function tooltip_rows(card)
|
||||
rows.name = 'Combo Advisor'
|
||||
return rows
|
||||
end
|
||||
JCA._tooltip_rows = tooltip_rows -- for the row-width test; not a public API
|
||||
|
||||
-- Test seam. This is the widest branch set in the mod and the only one the
|
||||
-- headless suite could not reach, so it is deliberately exported: test.lua
|
||||
-- uses it to assert that no advice row can ever exceed the width budget, on
|
||||
-- any board, in either text scale. Not part of the public API -- other mods
|
||||
-- should read partners_for/explain instead.
|
||||
JCA._tooltip_rows = tooltip_rows
|
||||
|
||||
-- The consumable counterpart of tooltip_rows: names the owned jokers a
|
||||
-- Planet/Tarot/Spectral card or its pack feeds. Quiet unless an owned joker
|
||||
@@ -1089,19 +1086,21 @@ local function consumable_rows(card)
|
||||
local S = JCA.config.touch_mode and 1.4 or 1
|
||||
local fed, tag = JCA.consumable_partners(card)
|
||||
if not tag then return nil end
|
||||
local budget = row_budget(card)
|
||||
local rows = {}
|
||||
-- Measured-wrapped like tooltip_rows: a long "Feeds:" list would push the
|
||||
-- consumable's box off the screen exactly the same way.
|
||||
local function text_row(str, colour)
|
||||
for _, line in ipairs(wrap_text(str, budget)) do
|
||||
local scale = 0.32 * S
|
||||
for _, line in ipairs(JCA.wrap_measured(str, scale)) do
|
||||
rows[#rows + 1] = {{n = G.UIT.T, config = {
|
||||
text = line, colour = colour or G.C.UI.TEXT_DARK, scale = 0.32 * S,
|
||||
text = line, colour = colour or G.C.UI.TEXT_DARK, scale = scale,
|
||||
}}}
|
||||
end
|
||||
end
|
||||
if #fed > 0 then
|
||||
for i, l in ipairs(wrap_names(fed, budget - 7, 2)) do
|
||||
text_row((i == 1 and 'Feeds: ' or ' ') .. l)
|
||||
end
|
||||
local names = {}
|
||||
for _, f in ipairs(fed) do names[#names + 1] = f.name end
|
||||
text_row('Feeds: ' .. table.concat(names, ', '))
|
||||
end
|
||||
-- A Standard Pack permanently adds playing cards, and Erosion is paid per
|
||||
-- card MISSING from the deck (card.lua:4318) -- the same verified mechanism
|
||||
@@ -1353,19 +1352,46 @@ local function recap_row()
|
||||
return {n = G.UIT.R, config = {align = 'cm', padding = 0.06}, nodes = rows}
|
||||
end
|
||||
|
||||
-- Vanilla node lists have HOLES in them: they are built as table constructors
|
||||
-- with conditional entries (`G.GAME.seeded and current_seed or nil`,
|
||||
-- UI_definitions.lua:2469), so index 2 can be empty with real nodes above it.
|
||||
-- Vanilla never notices because it walks them with pairs (engine/ui.lua:273) --
|
||||
-- but ipairs stops dead at the first hole, and table.insert/# are undefined on
|
||||
-- one. So walk and insert by explicit index, bounded by the highest numeric key.
|
||||
local function last_index(t)
|
||||
local n = 0
|
||||
for k in pairs(t) do
|
||||
if type(k) == 'number' and k > n then n = k end
|
||||
end
|
||||
return n
|
||||
end
|
||||
|
||||
-- Trail of {node, index} pairs from `node` down to the child carrying the id,
|
||||
-- so callers can insert siblings at any ancestor level.
|
||||
local function find_trail(node, id, trail)
|
||||
trail = trail or {}
|
||||
for i, child in ipairs(node.nodes or {}) do
|
||||
trail[#trail + 1] = {node = node, index = i}
|
||||
if child.config and child.config.id == id then return trail end
|
||||
local hit = find_trail(child, id, trail)
|
||||
if hit then return hit end
|
||||
trail[#trail] = nil
|
||||
local nodes = node.nodes or {}
|
||||
for i = 1, last_index(nodes) do
|
||||
local child = nodes[i]
|
||||
if type(child) == 'table' then
|
||||
trail[#trail + 1] = {node = node, index = i}
|
||||
if child.config and child.config.id == id then return trail end
|
||||
local hit = find_trail(child, id, trail)
|
||||
if hit then return hit end
|
||||
trail[#trail] = nil
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- table.insert's shift, done by hand so a hole above `index` shifts as a hole
|
||||
-- instead of truncating everything past it.
|
||||
local function insert_node(nodes, index, row)
|
||||
for i = last_index(nodes), index, -1 do
|
||||
nodes[i + 1] = nodes[i]
|
||||
end
|
||||
nodes[index] = row
|
||||
end
|
||||
|
||||
-- Insert the recap `up` ancestor levels above the anchor's parent, before
|
||||
-- (offset 0) or after (offset 1) that branch. Returns true on success.
|
||||
local function insert_recap(ui, id, up, offset)
|
||||
@@ -1374,7 +1400,7 @@ local function insert_recap(ui, id, up, offset)
|
||||
local trail = find_trail(ui, id)
|
||||
if not trail or #trail <= up then return end
|
||||
local spot = trail[#trail - up]
|
||||
table.insert(spot.node.nodes, spot.index + offset, row)
|
||||
insert_node(spot.node.nodes, spot.index + offset, row)
|
||||
return true
|
||||
end
|
||||
|
||||
@@ -1938,10 +1964,10 @@ if SMODS.current_mod then
|
||||
SMODS.current_mod.extra_tabs = catalog_tabs
|
||||
end
|
||||
|
||||
-- In-run "Combos" HUD button ---------------------------------------------------
|
||||
-- Adds a third button under Run Info / Options that opens the synergy catalog
|
||||
-- in an overlay. The overlay reuses catalog_tabs(); the page cyclers work
|
||||
-- unchanged because vanilla create_tabs also names its body 'tab_contents'.
|
||||
-- In-run "Combos" menu entry ----------------------------------------------------
|
||||
-- Adds a row to the pause menu (Escape), under Settings, that opens the synergy
|
||||
-- catalog in an overlay. The overlay reuses catalog_tabs(); the page cyclers
|
||||
-- work unchanged because vanilla create_tabs also names its body 'tab_contents'.
|
||||
|
||||
-- Shopping list shown at the top of the in-run overlay: the tags the board
|
||||
-- wants but nothing else on it gives, and the hooks it gives that nothing
|
||||
@@ -1983,32 +2009,27 @@ G.FUNCS.jca_open_catalog = function()
|
||||
})}
|
||||
end
|
||||
|
||||
local function find_by_id(node, id)
|
||||
if node.config and node.config.id == id then return node end
|
||||
for _, child in ipairs(node.nodes or {}) do
|
||||
local hit = find_by_id(child, id)
|
||||
if hit then return hit end
|
||||
end
|
||||
end
|
||||
|
||||
if create_UIBox_HUD then
|
||||
local orig_hud = create_UIBox_HUD
|
||||
function create_UIBox_HUD(...)
|
||||
local hud = orig_hud(...)
|
||||
local ok, area = pcall(find_by_id, hud, 'button_area')
|
||||
if ok and area and area.nodes then
|
||||
local B = JCA.config.touch_mode and 1.5 or 1
|
||||
area.nodes[#area.nodes + 1] =
|
||||
{n = G.UIT.R, config = {align = 'cm', minh = 0.9 * B, minw = 1.5 * B,
|
||||
padding = 0.05, r = 0.1, hover = true, colour = G.C.PURPLE,
|
||||
button = 'jca_open_catalog', shadow = true}, nodes = {
|
||||
{n = G.UIT.C, config = {align = 'cm', maxw = 1.4 * B}, nodes = {
|
||||
{n = G.UIT.T, config = {text = 'Combos', scale = 0.38 * B,
|
||||
colour = G.C.UI.TEXT_LIGHT, shadow = true}},
|
||||
}},
|
||||
}}
|
||||
end
|
||||
return hud
|
||||
-- Anchored on 'restart_button' (Start New Run), which create_UIBox_options only
|
||||
-- builds during a run (UI_definitions.lua:2439) -- so the entry never shows up on
|
||||
-- the main menu, where there is no board to advise on. UIBox_button hangs the id
|
||||
-- on the inner node, so the row to insert beside is one level up the trail, the
|
||||
-- same walk the post-run recap does.
|
||||
if create_UIBox_options and UIBox_button then
|
||||
local orig_options = create_UIBox_options
|
||||
function create_UIBox_options(...)
|
||||
local ui = orig_options(...)
|
||||
pcall(function()
|
||||
local trail = find_trail(ui, 'restart_button')
|
||||
if not trail or #trail < 2 then return end
|
||||
local spot = trail[#trail - 1]
|
||||
insert_node(spot.node.nodes, spot.index, UIBox_button{
|
||||
id = 'jca_combos_button',
|
||||
label = {'Combos'},
|
||||
button = 'jca_open_catalog',
|
||||
minw = 5,
|
||||
})
|
||||
end)
|
||||
return ui
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
Reference in New Issue
Block a user