From 2b860cc899ad9393780d3439542b0fd855451413 Mon Sep 17 00:00:00 2001 From: Brian Fertig Date: Fri, 11 Sep 2026 22:09:49 -0600 Subject: [PATCH] Move the live NET readout from the panel center onto the tab sub-bar MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The pending trade's credit delta now sits at the right end of the Buy/Sell + Buyback sub-bar, past a hairline divider — tabs AND the credit math on one console, one glance apart (entry state dims it to 0 cr). The center column keeps Confirm/Clear (re-centered); the buyback panel's Confirm moves up to sit just below its rows. Knobs: data/shopui.json → preview (bar sizing, zeroColor, divider). Docs: docs/ECONOMY.md §12 mockups + bullets follow the new layout. Tests: dev/shop-ui.test.mjs asserts the bar's NET readout mirrors the pending trade (+N cr while picking, 0 cr after Confirm). --- data/shopui.json | 15 +++-- dev/shop-ui.test.mjs | 7 ++ docs/ECONOMY.md | 37 ++++++----- js/ui/shop/ShopView.js | 141 ++++++++++++++++++++++++++--------------- 4 files changed, 126 insertions(+), 74 deletions(-) diff --git a/data/shopui.json b/data/shopui.json index ca86464..a3e150a 100644 --- a/data/shopui.json +++ b/data/shopui.json @@ -1,9 +1,9 @@ { - "_comment": "SHOP UI — the buy/sell (and buyback) screen over the shop clip (data/shopui.json). LAYOUT: the two 4×4 grids sit side by side (SELL = the player's cargo bays on the LEFT, BUY = the shop's stock on the RIGHT), a center column carries the live NET preview + Confirm/Clear, and a tab bar up top toggles Buy/Sell ↔ Buyback (Buyback is disabled until the player has SOLD something this session). The panels are semi-transparent so the shop clip shows through; items + text are full alpha. CELLS show the good's ICON only — the name + price appear in a hover TOOLTIP (→ tooltip) instead of over the icon. GEOMETRY (slot/gap/gridCols/gridRows/centerW) is primitives the code (js/ui/shop/ShopView.js) lays out against the live scene size — not pixel positions. The ECONOMY itself (prices, stock) is Economy.js + data/goods.json + data/economy.json; this file is only presentation.", + "_comment": "SHOP UI — the buy/sell (and buyback) screen over the shop clip (data/shopui.json). LAYOUT: the two 4×4 grids sit side by side (SELL = the player's cargo bays on the LEFT, BUY = the shop's stock on the RIGHT), Confirm/Clear sit in the center column, the live NET-credits readout rides on the TAB SUB-BAR (right of the Buy/Sell + Buyback buttons, past a hairline divider), and the tab sub-bar toggles Buy/Sell ↔ Buyback (Buyback is disabled until the player has SOLD something this session). The panels are semi-transparent so the shop clip shows through; items + text are full alpha. CELLS show the good's ICON only — the name + price appear in a hover TOOLTIP (→ tooltip) instead of over the icon. GEOMETRY (slot/gap/gridCols/gridRows/centerW) is primitives the code (js/ui/shop/ShopView.js) lays out against the live scene size — not pixel positions. The ECONOMY itself (prices, stock) is Economy.js + data/goods.json + data/economy.json; this file is only presentation.", "enabled": true, "tabs": { - "_comment": "The tab sub-bar (two MenuButtons, the deck's cyberpunk style — js/ui/MenuButton.js). Buy/Sell opens the trade panel; Buyback opens the buyback panel (goods sold this session, session-local only). Size/palette come from data/save.json → subBar + colors (the Menu sub-bar recipe); this section just holds the labels + Buy/Sell accent. The sub-bar rests above the command deck, anchored to the deck's Shop button (SurfaceScene.shopAnchor).", + "_comment": "The tab sub-bar (two MenuButtons, the deck's cyberpunk style — js/ui/MenuButton.js), EXTENDED at its right end by the live NET readout (→ preview) past a hairline divider. Buy/Sell opens the trade panel; Buyback opens the buyback panel (goods sold this session, session-local only). Size/palette come from data/save.json → subBar + colors (the Menu sub-bar recipe); this section just holds the labels + Buy/Sell accent. The sub-bar rests above the command deck, anchored to the deck's Shop button (SurfaceScene.shopAnchor).", "buySell": "Buy/Sell", "buyback": "Buyback", "w": 184, "h": 46, "gap": 14, @@ -61,13 +61,16 @@ }, "preview": { - "_comment": "The live NET-credits readout in the center column. Positive (you're up) = green, negative (you'd spend past your credits / it's a net spend) = the value shown; the Confirm button is disabled while the trade is invalid (can't go negative, hold overflows).", + "_comment": "The live NET-credits readout — on the TAB SUB-BAR, right of the Buy/Sell + Buyback buttons, past a hairline divider (→ divider): tabs AND the pending trade's credit delta on one console, one glance apart. Positive (you're up) = green, negative (a net spend) = the value shown in magenta, zero = dim (entry state / nothing selected). The value is right-aligned at the bar's end so longer numbers (+1,240 cr) never resize the bar. Confirm is disabled while the trade is invalid (can't go negative, hold overflows).", "label": "NET", "labelColor": "#7d92c4", - "labelFontSize": 13, - "valueFontSize": 34, + "labelFontSize": 12, + "valueFontSize": 22, + "zeroColor": "#7d92c4", "posColor": "#7ce8a4", - "negColor": "#ff2d6f" + "negColor": "#ff2d6f", + "gap": 12, + "divider": { "width": 2, "height": 26, "color": "#22405f", "alpha": 0.9 } }, "buttons": { diff --git a/dev/shop-ui.test.mjs b/dev/shop-ui.test.mjs index cd2ad31..5dd186b 100644 --- a/dev/shop-ui.test.mjs +++ b/dev/shop-ui.test.mjs @@ -333,12 +333,19 @@ console.log('\n== SELL — check a bay, Confirm earns credits + empties the hold assert.equal(pv.net, sellA, '… net = the sale (nothing bought)'); assert.equal(pv.valid, true, '… and it\u2019s legal'); + // The live NET readout rides on the TAB SUB-BAR (not the center column) — + // it mirrors the pending trade's credit delta, one glance from the tabs. + assert.ok(view._netValue, 'the sub-bar carries the NET value readout'); + assert.ok(view._netLabel, '… with the NET label'); + assert.equal(view._netValue.text, `+${sellA} cr`, '… it shows the pending +cr'); + const before = ship.credits; assert.equal(view.confirm(), true, 'Confirm goes through'); assert.equal(ship.credits, before + sellA, 'credits rise by the sale'); assert.deepEqual(ship.cargo.snapshot(), [B], 'A leaves the hold, B stays'); assert.equal(view.isBuybackEnabled(), true, '… and A is now buyback-eligible'); assert.equal(gs.refreshed, 1, 'the credits HUD was refreshed'); + assert.equal(view._netValue.text, '0 cr', '… and the bar’s NET settles back to 0 cr'); } console.log('SELL: OK'); diff --git a/docs/ECONOMY.md b/docs/ECONOMY.md index b89a572..bc1076d 100644 --- a/docs/ECONOMY.md +++ b/docs/ECONOMY.md @@ -298,7 +298,8 @@ existing `data/actionbar.json` slot); the shop clip is already live, and the scr it. It's one component — `js/ui/shop/ShopView.js` — composing two `TradeGrid`s. **Entry state (showcase the clip).** When you first open the shop, **only the tab sub-bar** -is up — the cyberpunk **MenuButton** pair `[ BUY/SELL | BUYBACK ]` (Buyback grayed out), the +is up — the cyberpunk **MenuButton** pair `[ BUY/SELL | BUYBACK ]` (Buyback grayed out), +extended at its right end by the live **NET** readout (dim `0 cr` until you start picking), the same style + placement as the deck's **Menu** sub-bar (`js/ui/MenuSubBar.js`). It rests **above the command deck**, right-aligned to the deck's **Shop** button — so it reads as the same family of UI as the Menu drawer. The screen stays open on the shop video so the clip plays. Clicking @@ -308,24 +309,23 @@ of UI as the Menu drawer. The screen stays open on the shop video so the clip pl # first click — shop video + the tab sub-bar (rests above the deck): ┌─────────────────────────────────────────────────────────────────┐ │ (clip plays) │ -│ ┌─────────────────────────┐ │ -│ │ BUY/SELL │ BUYBACK │ ← sub-bar │ -│ └─────────────────────────┘ │ +│ ┌──────────────────────────────────┐ │ +│ │ BUY/SELL │ BUYBACK │ NET 0 cr │ ← sub-bar │ +│ └──────────────────────────────────┘ │ ├─────────────────────────────────────────────────────────────────┤ │ SHOP │ BUILD │ SHIP │ QUESTS │ TAKE OFF │ MENU ← command deck│ └─────────────────────────────────────────────────────────────────┘ # after clicking Buy/Sell — the trade panel reveals (middle of the screen): ┌─────────────────────────────────────────────────────────────────┐ -│ ┌SELL 4×4┐ NET +420 cr ┌BUY 4×4┐ │ -│ │ bays │ (green ≥ 0 / │ stock │ │ -│ │(cargo) │ red < 0) │(12–16) │ │ -│ └────────┘ [ CONFIRM ] └────────┘ │ -│ [ CLEAR ] │ +│ ┌SELL 4×4┐ [ CONFIRM ] ┌BUY 4×4┐ │ +│ │ bays │ [ CLEAR ] │ stock │ │ +│ │(cargo) │ │(12–16) │ │ +│ └────────┘ └────────┘ │ ├─────────────────────────────────────────────────────────────────┤ -│ ┌─────────────────────────┐ │ -│ │ BUY/SELL │ BUYBACK │ ← sub-bar │ -│ └─────────────────────────┘ │ +│ ┌──────────────────────────────────┐ │ +│ │ BUY/SELL │ BUYBACK │ NET +420 cr │ ← sub-bar │ +│ └──────────────────────────────────┘ │ ├─────────────────────────────────────────────────────────────────┤ │ SHOP │ BUILD │ SHIP │ QUESTS │ TAKE OFF │ MENU ← command deck│ └─────────────────────────────────────────────────────────────────┘ @@ -335,9 +335,11 @@ of UI as the Menu drawer. The screen stays open on the shop video so the clip pl cyberpunk style: cut-corner panel, neon edge, hover sweep, press punch) in a console-blue panel that rests above the command deck, anchored to the Shop button — the exact recipe of the Menu sub-bar. Buyback is **disabled** (grayed) until you've sold something this visit. -- **Confirm / Clear** — the same `MenuButton` style; Confirm is **disabled** (grayed) while - the trade is invalid (can't go negative, hold would overflow), and both appear once you start - picking. + The bar is **extended** past a hairline divider to carry the live **NET** readout — + one glance sees the tabs *and* the pending trade's credit delta. +- **Confirm / Clear** — the same `MenuButton` style, in the center column between the grids; + Confirm is **disabled** (grayed) while the trade is invalid (can't go negative, hold would + overflow), and both appear once you start picking. - **SELL (left)** — your **cargo bays**. 16 cells (the fixed grid); the first `cargoHold` are live bays (filled with a good, or empty), the rest are **locked** (the hold's upgrade @@ -345,8 +347,9 @@ of UI as the Menu drawer. The screen stays open on the shop video so the clip pl quantity is simply the number of its bays that are checked. - **BUY (right)** — the shop's **stock** (12–16 distinct goods from `Economy.shopFor`). Each cell has a **−/qty/+ stepper**; buy as many as you like (capped by credits + hold room). -- **NET preview (center)** — the live credit delta. Green when you're up (a net sale), red when - you'd spend. Updates as you check bays / step goods. +- **NET readout (tab sub-bar)** — the live credit delta, right of the tabs past the divider. + Green when you're up (a net sale), red when you'd spend, dim at zero. Updates as you check + bays / step goods (it's the same `preview()` the Confirm button is gated on). - **SELL → BUY is atomic** — check bays to sell *and* step goods to buy, then one Confirm executes it: credits `+ sale − buy`, cargo `− sold + bought`, in that order (free room first). Selling to free a bay for a buy lands in the same Confirm. diff --git a/js/ui/shop/ShopView.js b/js/ui/shop/ShopView.js index 4af98a0..f01c938 100644 --- a/js/ui/shop/ShopView.js +++ b/js/ui/shop/ShopView.js @@ -1,22 +1,24 @@ /** * ShopView — the buy/sell (and buyback) screen over the shop clip video. * - * ┌─────────────────────────────────────────────────────────────┐ - * │ [ Buy/Sell | Buyback ] ← tabs │ - * │ │ - * │ ┌SELL 4×4┐ ┌ NET ┐ ┌BUY 4×4┐ │ - * │ │ bays │ │ +420 │ │ stock │ │ - * │ │ (cargo)│ │ cr │ │ (12–16)│ │ - * │ └────────┘ │[Conf]│ └────────┘ │ - * │ │[Clear]│ │ - * └─────────────────────────────────────────────────────────────┘ + * ┌────────────────────────────────────────────────────────────┐ + * │ ┌SELL 4×4┐ [ Confirm ] ┌BUY 4×4┐ │ + * │ │ bays │ [ Clear ] │ stock │ │ + * │ │ (cargo)│ │ (12–16)│ │ + * │ └────────┘ └────────┘ │ + * └────────────────────────────────────────────────────────────┘ + * ┌────────────────────────────────────────────────────────────┐ + * │ BUY/SELL BUYBACK │ NET +420 cr ← tab sub-bar │ + * └────────────────────────────────────────────────────────────┘ * * The two sides are TradeGrids (js/ui/shop/TradeGrid.js): * · SELL (left) — the player's cargo bays. Tap a filled bay to check it * off (a bay = 1 unit). Locked bays are the hold's upgrade room. * · BUY (right) — the shop's stock, each with a −/qty/+ stepper. - * The live NET preview (green ≥ 0, red < 0) and Confirm/Clear live in the - * center; Confirm is disabled while the trade is invalid (can't go negative, + * The live NET preview (green ≥ 0, red < 0) sits on the TAB SUB-BAR, right + * of the Buy/Sell + Buyback buttons past a hairline divider (tabs AND the + * pending trade's credit math on one console); Confirm/Clear stay in the + * center. Confirm is disabled while the trade is invalid (can't go negative, * hold would overflow). SELL→BUY happens atomically (sell to free room for a * buy). * @@ -83,7 +85,8 @@ export class ShopView extends Phaser.GameObjects.Container { this.sellGrid = null; this.buyGrid = null; this._content = null; // the active content container - this._previewText = null; + this._netLabel = null; // the sub-bar NET label (persistent) + this._netValue = null; // the sub-bar NET value (live) this._confirmBtn = null; this._clearBtn = null; this._tabBuyback = null; @@ -132,6 +135,12 @@ export class ShopView extends Phaser.GameObjects.Container { // signature top rail, holding cyberpunk MenuButtons. It rests ABOVE the // command deck, right-aligned to the deck's "Shop" button (this.anchor), // so it reads as the same family of UI as the Menu drawer. + // + // EXTENDED (data/shopui.json → preview): the live NET credit readout — + // the pending trade's credit delta — sits at the bar's right end past a + // hairline divider. Tabs AND the credit math on ONE console: the readout + // is one glance away from the tabs at every moment (entry state included, + // where it dims to "0 cr"). const a = this.anchor; const H = config.get('save.subBar.height', 58); const pad = config.get('save.subBar.padding', 16); @@ -139,6 +148,7 @@ export class ShopView extends Phaser.GameObjects.Container { const tabs = config.get('shopui.tabs', {}); const c = config.section('save.colors', {}); const btnCfg = config.section('save.subBar.button', {}); + const pv = config.section('shopui.preview', {}); const items = [ { id: 'trade', label: tabs.buySell ?? 'Buy/Sell', accent: tabs.activeAccent ?? c.neon ?? '#00e5ff' }, @@ -165,10 +175,31 @@ export class ShopView extends Phaser.GameObjects.Container { const btns = items.map(mkBtn); const widths = btns.map((b) => b.style?.width ?? 120); - const W = Math.min( - this._W() - 24, - pad * 2 + widths.reduce((s, w) => s + w, 0) + gap * (widths.length - 1), - ); + const tabsW = widths.reduce((s, w) => s + w, 0) + gap * (widths.length - 1); + + // The NET readout — created BEFORE the panel body so its REAL width feeds + // the bar width (a headless fake without text metrics falls back to an + // estimate). The value is RIGHT-aligned at the bar's end, so longer + // numbers ("+1,240 cr") never need the bar to grow. + const labelStr = String(pv.label ?? 'NET'); + const valueFont = Math.max(8, Number(pv.valueFontSize) || 22); + this._netLabel = this._makeText(0, -H / 2, labelStr, { + fontSize: `${Math.max(8, Number(pv.labelFontSize) || 12)}px`, + color: toCss(pv.labelColor ?? c.dim ?? '#7d92c4'), + align: 'left', + }); + this._netValue = this._makeText(0, -H / 2, '0 cr', { + fontSize: `${valueFont}px`, + color: toCss(pv.zeroColor ?? pv.labelColor ?? '#7d92c4'), + align: 'left', + }); + const labelW = (Number(this._netLabel?.width) > 0 ? this._netLabel.width : labelStr.length * 8) || 30; + const valueW = (Number(this._netValue?.width) > 0 ? this._netValue.width : 4 * valueFont * 0.6) || 40; + const divW = Math.max(1, Number(pv.divider?.width) || 2); + const divH = Math.max(10, Number(pv.divider?.height) || Math.round(H * 0.45)); + + // Row: [ BUY/SELL ][ BUYBACK ] │ NET value + const W = Math.min(this._W() - 24, pad * 2 + tabsW + gap + divW + gap + labelW + gap + valueW); // Position: bottom edge on the deck's top rail (a.y), right edge on the // Shop button's right edge — the MenuSubBar recipe. The sub-bar is a @@ -187,15 +218,18 @@ export class ShopView extends Phaser.GameObjects.Container { this.add?.(bar); // The body FIRST (v4 paints children in list order — the opaque panel - // fill must come before the buttons or it would cover them). + // fill must come before the buttons or it would cover them). The hairline + // divider between the tabs and the NET block is part of the body, so it + // paints under both. + const divX = -W / 2 + pad + tabsW + gap + divW / 2; const bodyG = this.scene.add?.graphics?.(); if (bodyG) { bodyG.setScrollFactor?.(0); bar.add?.(bodyG); - this._drawSubBarBody(bodyG, W, H, c); + this._drawSubBarBody(bodyG, W, H, c, divX, divH); } - // The buttons LAST (above the body), centered in the row. + // The buttons LAST (above the body), left-anchored in the row. let bx = -W / 2 + pad + widths[0] / 2; items.forEach((it, i) => { const btn = btns[i]; @@ -206,12 +240,23 @@ export class ShopView extends Phaser.GameObjects.Container { bx += widths[i] / 2 + gap + (i < items.length - 1 ? widths[i + 1] / 2 : 0); }); + // The NET block: the label right after the divider, the value right- + // aligned to the bar's end (so a growing number never shifts the label). + this._netLabel.setPosition?.(divX + divW / 2 + gap + labelW / 2, -H / 2); + this._netValue.setPosition?.(W / 2 - pad - valueW / 2, -H / 2); + bar.add?.(this._netLabel); + bar.add?.(this._netValue); + // Buyback starts grayed (disabled) until a sale exists this visit. this._refreshTabs(); } - /** The sub-bar panel body (the MenuSubBar recipe at full height). */ - _drawSubBarBody(g, W, H, c) { + /** + * The sub-bar panel body (the MenuSubBar recipe at full height). + * `divX`/`divH` (optional) add the hairline divider between the tabs and + * the NET readout — drawn as body so it paints under the buttons + text. + */ + _drawSubBarBody(g, W, H, c, divX, divH) { if (!g || typeof g.clear !== 'function') return; const panelTop = toColor(c.panelTop ?? '#0e1930'); const panelBottom = toColor(c.panelBottom ?? '#050912'); @@ -238,6 +283,10 @@ export class ShopView extends Phaser.GameObjects.Container { g.fillStyle(ink, 0.8); g.fillRect(-W / 2 + cut, -H + 0.5, W - cut * 2, 0.8); g.fillStyle(magenta, 0.5); g.fillRect(-W / 2 + cut, -H + rail, W - cut * 2, 1); g.fillStyle(magenta, 0.35); g.fillRect(-W / 2, -1.5, W, 1.5); // seam + if (typeof divX === 'number' && divH > 0) { + g.fillStyle(toColor(c.border ?? '#22405f'), 0.9); + g.fillRect(divX - 1, -H / 2 - divH / 2, 2, divH); // tabs │ NET + } } } @@ -298,27 +347,17 @@ export class ShopView extends Phaser.GameObjects.Container { content.add?.(sellGrid); content.add?.(buyGrid); - // center column: NET preview + Confirm + Clear — vertically centered with - // the grids (the NET above, the buttons below), so the whole panel is one - // cohesive block near the top of the screen. + // center column: Confirm + Clear — vertically centered with the grids, + // so the whole panel is one cohesive block near the top of the screen. + // (The live NET preview now rides on the tab sub-bar — see _buildTabs.) const cx = leftX + sellGrid.width + centerW / 2; const cy = this.topY + gridH / 2; - this._previewLabel = this._makeText(cx, cy - 52, config.get('shopui.preview.label', 'NET'), { - fontSize: `${config.get('shopui.preview.labelFontSize', 13)}px`, - color: config.get('shopui.preview.labelColor', '#7d92c4'), align: 'center', - }); - this._previewText = this._makeText(cx, cy - 14, 'NET 0 cr', { - fontSize: `${config.get('shopui.preview.valueFontSize', 34)}px`, - color: config.get('shopui.preview.zeroColor', '#7d92c4'), align: 'center', - }); - content.add?.(this._previewLabel); - content.add?.(this._previewText); // Confirm / Clear — the same cyberpunk MenuButton as the tab bar. const c = config.section('save.colors', {}); const btnCfg = config.section('save.subBar.button', {}); this._confirmBtn = new MenuButton( - this.scene, cx, cy + 34, config.get('shopui.buttons.confirmLabel', 'Confirm'), () => this.confirm(), + this.scene, cx, cy + 16, config.get('shopui.buttons.confirmLabel', 'Confirm'), () => this.confirm(), { screenFixed: true, fontSize: btnCfg.fontSize ?? 13, paddingX: 26, paddingY: 12, @@ -331,7 +370,7 @@ export class ShopView extends Phaser.GameObjects.Container { content.add?.(this._confirmBtn); this._clearBtn = new MenuButton( - this.scene, cx, cy + 104, config.get('shopui.buttons.clearLabel', 'Clear'), () => this.clear(), + this.scene, cx, cy + 86, config.get('shopui.buttons.clearLabel', 'Clear'), () => this.clear(), { screenFixed: true, fontSize: btnCfg.fontSize ?? 13, paddingX: 26, paddingY: 12, @@ -390,16 +429,11 @@ export class ShopView extends Phaser.GameObjects.Container { } const cy = y + 24; - this._previewLabel = this._makeText(cx, cy - 8, config.get('shopui.preview.label', 'NET'), { - fontSize: `${config.get('shopui.preview.labelFontSize', 13)}px`, color: config.get('shopui.preview.labelColor', '#7d92c4'), align: 'center' }); - content.add?.(this._previewLabel); - this._previewText = this._makeText(cx, cy + 22, 'NET 0 cr', { - fontSize: `${config.get('shopui.preview.valueFontSize', 34)}px`, color: config.get('shopui.preview.zeroColor', '#7d92c4'), align: 'center' }); - content.add?.(this._previewText); + // (The live NET readout rides on the tab sub-bar — see _buildTabs.) const bw = config.get('shopui.buttons.w', 158), bh = config.get('shopui.buttons.h', 54); this._confirmBtn = this._makeButton(config.get('shopui.buttons.confirmLabel', 'Confirm'), bw, bh, config.get('shopui.buttons.confirmAccent', '#7ce8a4')); - this._confirmBtn.setPosition(cx, cy + 92); + this._confirmBtn.setPosition(cx, cy + 40); this._confirmBtn.tap(() => this.confirm()); this._confirmBtn.setAlpha(0); content.add?.(this._confirmBtn.container); @@ -531,13 +565,16 @@ export class ShopView extends Phaser.GameObjects.Container { _syncPreview() { const pv = this.preview(); const anySel = this._hasAnySelection(); - if (this._previewText && typeof this._previewText.setText === 'function') { - const sign = pv.net >= 0 ? '+' : ''; - this._previewText.setText(`NET ${sign}${Math.round(pv.net)} cr`); - const col = pv.net === 0 + // The live NET readout lives on the tab sub-bar (this._netValue) — one + // glance sees the tabs AND the pending trade's credit delta. Right- + // aligned, so a growing number never shifts the "NET" label. + if (this._netValue && typeof this._netValue.setText === 'function') { + const n = Math.round(pv.net); + this._netValue.setText(n === 0 ? '0 cr' : `${n > 0 ? '+' : ''}${n} cr`); + const col = n === 0 ? config.get('shopui.preview.zeroColor', '#7d92c4') - : (pv.net > 0 ? config.get('shopui.preview.posColor', '#7ce8a4') : config.get('shopui.preview.negColor', '#ff2d6f')); - if (typeof this._previewText.setColor === 'function') this._previewText.setColor(col); + : (n > 0 ? config.get('shopui.preview.posColor', '#7ce8a4') : config.get('shopui.preview.negColor', '#ff2d6f')); + if (typeof this._netValue.setColor === 'function') this._netValue.setColor(col); } const confirmOn = anySel; const confirmEnabled = anySel && pv.valid; @@ -584,8 +621,6 @@ export class ShopView extends Phaser.GameObjects.Container { this.buyGrid?.destroy?.(); this.sellGrid = null; this.buyGrid = null; - this._previewText = null; - this._previewLabel = null; this._confirmBtn = null; this._clearBtn = null; } @@ -594,6 +629,10 @@ export class ShopView extends Phaser.GameObjects.Container { this._destroyContent(); this._tabTrade?.destroy?.(); this._tabBuyback?.destroy?.(); + this._netLabel?.destroy?.(); + this._netValue?.destroy?.(); + this._netLabel = null; + this._netValue = null; if (typeof super.destroy === 'function') super.destroy(); }