Shops

The key words MUST, MUST NOT, and MAY below are used as in RFC 2119.

Shops are list-driven menus in the city. There are eight canonical counters: six vendor/service counters, plus the shared Storage Counter bank and the Guild Counter (a quest desk, not a vendor). Each is its own scene.

The shared shopping loop

Every vendor counter follows the same shape (the diagram above): on open it builds its lists and draws them, the player navigates a selection with the d-pad (with a hold-to-repeat), confirm opens a yes/no (or quantity) modal, and a successful buy/sell runs the transaction and redraws. A successful transaction MUST update meseta and inventory before the display refreshes.

Counters

CounterWhat it doesTabsScene
Item Shop Consumables, materials, technique disks, and buy-back. Items · Materials · Disks · Sell scenes/2d/shops/item_shop.tscn
Weapon Shop Weapons / armor / units by class & requirement. Weapons · Armor · Units · Sell scenes/2d/shops/weapon_shop.tscn
Tekker Identify unknown weapons & grind weapons. Grind · Identify scenes/2d/shops/tekker.tscn
Photon Collector Trade photon drops (not meseta) for grinders / crystals / materials. scenes/2d/shops/photon_shop.tscn
Synthesis Shop Craft items from recipes / materials. Craft · Boards scenes/2d/shops/crafting_shop.tscn
Blackjack Gambling table. scenes/2d/blackjack/blackjack_table.tscn
Storage Counter The shared bank (GameState.shared_storage), cross-character. Deposit / Withdraw × Items / Meseta storage screen
Guild Counter Accept / report quests (not a vendor). guild_counter

Naming notes. The Photon Collector's scene/script is named photon_shop (file-level drift only — the player-facing name is "Photon Collector"). The Synthesis Shop's code identifier is crafting_shop, but the player-facing name is "Synthesis Shop".

Technique disks are sold in the Item Shop's Disks tab — this is canonical. A separate standalone tech shop is redundant and slated for removal.

Buy affordance

The player MAY select any item on a counter to read its details. For any item they can't afford, can't fit, or don't meet the requirements for, the Buy action MUST be disabled — rendered greyed out with a reason on the row ("No room" / "Lv N req" / "Can't afford"). Because the unaffordable path is gated before confirm, the player MUST NOT be able to reach a rejection dialog at all: there is no "you can't buy that" pop-up to dismiss, only a disabled control with an inline explanation.

This shared check — a single _can_buy(item) -> {ok, reason} predicate plus the disabled-row rendering it drives — is slated to live on a planned ShopBase so every counter renders affordance identically. See docs/shop-dedup.md.

Implemented by