Skip to main content

Shop Definition

Defines a shop

Declarative definition of a shop and its items. Uses layout pool + mainLayoutId for full declarative control via GUI Extension, using shop_button: prefixed placeholders for navigation and item slots. Sub-menus (buy/sell options) are another layout in the same [layoutPool], picked by [subMenuLayoutId].

Fields

FieldTypeDefaultDescription
titleString—Inventory title displayed to players. Supports color codes and placeholders.
rowsInt6Number of inventory rows (1-6). Used to determine inventory size.
fillerItemVar<Item>—Item used to fill empty slots.
priceLoreList<String>—Lore lines appended to each item to display prices. Tokens: {buy}, {sell}, {item_price} (one line per requested item, for priceMode ITEMS). A line asking for an unavailable price is dropped. Supports color codes and placeholders.
currencyCurrencyTypeVAULTEconomy provider used for pricing.Values:VAULTPLACEHOLDERPOINTS
balancePlaceholderString—Placeholder used to fetch player balance when currency is placeholder.
addCommandString—Command to add currency. {player} and {amount} will be replaced.
removeCommandString—Command to remove currency. {player} and {amount} will be replaced.
defaultItemPriceList<ItemCost>emptyPrice in items applied to articles in priceMode ITEMS that declare none of their own. Leave empty when every such article carries its own itemPrice.
ItemCost (2)
FieldTypeDefaultDescription
itemVar<Item>—Item requested as payment. A catalogue item is matched by its identity, not only by its material.
amountInt1Quantity requested per unit bought. Zero or negative makes the line invalid and the price is refused.
cannotAffordItemsMessageString<red>You are missing: {missing}Message sent when the player lacks the requested items. {missing} is replaced by the list of what is missing, {amount} by the quantity bought.
taxRateDouble0.0Tax percentage applied on transactions (0.0 = no tax, 10.0 = 10% tax).
taxModeTaxModePER_TRANSACTIONHow taxes are applied: PER_TRANSACTION (global rate) or PER_ITEM (per-item override via taxRateOverride).
resetResetPolicy—Policy determining when stock resets. Supports Cron, Interval, Daily, Weekly, and Monthly.Variants:crondailyintervalmonthlynoneweekly
cron (1)
FieldTypeDefaultDescription
expressionString0 0 * * *Cron expression (e.g., '0 0 * * *').
daily (2)
FieldTypeDefaultDescription
hourInt0Hour of day (0-23).
minuteInt0Minute of hour (0-59).
interval (1)
FieldTypeDefaultDescription
secondsLong3600Interval in seconds.
monthly (3)
FieldTypeDefaultDescription
dayInt1Day of month (1-31).
hourInt0Hour of day (0-23).
minuteInt0Minute of hour (0-59).
none (1)
FieldTypeDefaultDescription
_dummyBooleanfalse—
weekly (3)
FieldTypeDefaultDescription
dayInt1Day of week (1-7, where 1 is Monday).
hourInt0Hour of day (0-23).
minuteInt0Minute of hour (0-59).
itemsList<ShopItemConfig>emptyItems available in the shop.
ShopItemConfig (20)
FieldTypeDefaultDescription
itemVar<Item>—Item displayed for this shop entry.
loreList<String>emptyAdditional lore lines for this item.
criteriaList< Criteria>emptyCriteria required for this item to be displayed.
buyEnabledBooleantrueWhether players can buy this item.
sellEnabledBooleantrueWhether players can sell this item.
priceModePriceModeDYNAMICHow the price is determined: FIXED, DYNAMIC or ITEMS.
itemPriceList<ItemCost>emptyPrice paid in items for THIS article. When set it replaces the shop's defaultItemPrice; when empty the shop provides its own. Only used when priceMode is ITEMS.
ItemCost (2)
FieldTypeDefaultDescription
itemVar<Item>—Item requested as payment. A catalogue item is matched by its identity, not only by its material.
amountInt1Quantity requested per unit bought. Zero or negative makes the line invalid and the price is refused.
fixedBuyPriceDouble0.0Fixed buy price per unit. Only used when priceMode is FIXED.
fixedSellPriceDouble0.0Fixed sell price per unit. Only used when priceMode is FIXED.
dynamicPricingPriceStrategy—Dynamic pricing strategy. Only used when priceMode is DYNAMIC.
PriceStrategy (9)
FieldTypeDefaultDescription
priceDouble0.0Base price when stock is full.
sellPriceDouble0.0Base sell price when stock is full.
sellPriceMinDouble0.0Minimum sell price limit.
sellPriceMaxDouble0.0Maximum sell price limit. 0 = unlimited.
stockMaxInt0Maximum stock available.
minDouble0.0Minimum buy price limit.
maxDouble0.0Maximum buy price limit. 0 = unlimited.
stepDouble0.1Fraction of the maximum stock required to trigger a price change. Applies to buy and sell prices.
roundingDouble0.01Round result to the nearest multiple of this value. Set to 0 to disable rounding.
primaryActionShopClickActionBUY_1Action triggered by the primary click (default: left click).
secondaryActionShopClickActionSELL_1Action triggered by the secondary click (default: right click).
shiftActionShopClickActionBUY_SUBMENUAction triggered by shift-click.
playerLimitInt0Maximum amount a single player can purchase per reset cycle. 0 = unlimited.
globalLimitInt0Maximum amount that can be purchased globally (all players combined) per reset cycle. 0 = unlimited.
refreshTicksInt0How often, in ticks, the shop checks whether the stock of this item has changed while it is open; the shop is redrawn only when it has. Only used when the item has a finite stock. 0 = the shop's defaultRefreshTicks.
guiSlotInt-1Fixed slot for this item, counted row by row from 0 inside the shop's content area (9 columns wide, or contentColumns in tab mode). The item stays there on every page and leaves the SHOP_ITEM markers to the others. Keep the slot free of any other layout item. -1, or a slot outside the area = placed on the next free SHOP_ITEM marker.
buyMaxEnabledBooleantrueWhether buy max is enabled for this item.
sellAllEnabledBooleantrueWhether the sell-all action is enabled for this item. When off, sell all is refused with the shop's cannotSellMessage; selling a chosen amount is unaffected.
taxRateOverrideDouble-1.0Per-item tax rate override (percentage). Set to -1 to use the shop's global taxRate. Only effective when shop taxMode is PER_ITEM.
itemPoolsList<Ref<ShopPoolEntry>>emptyItem pools referenced by this shop. Pool items are merged into the shop display.
layoutPoolList<SimpleLayoutData>emptyLayout pool for declarative menu design. Holds every layout this shop uses — the main menu and the buy/sell sub-menu are both selected out of this pool by id. Shop menus are simple grids: shop items are placed at SHOP_ITEM markers and buttons are shop_button: tagged slots.
mainLayoutIdString—ID of the main layout within the layout pool to display.
baseMenuIdString—open_gui entry used as a shared chassis (e.g. menu_shell). Empty = the shop opens as a standalone inventory, exactly as before. Set = the shop becomes a tab of that menu: its layout pool and views merge underneath the chassis.
viewsList<MenuViewData>emptyAddressable screens of this shop when baseMenuId is set. The inherited chassis' '@view' frames resolve against the active view. Ignored in standalone mode.
defaultViewIdString?—View opened first when baseMenuId is set. Empty = the first declared view.
contentXInt0X origin of the content (SHOP_ITEM markers and filler) inside the inherited chassis. Ignored in standalone mode.
contentYInt2Y origin of the content inside the inherited chassis. Ignored in standalone mode.
contentColumnsInt9Width in columns of the content area inside the inherited chassis. Ignored in standalone mode.
contentRowsInt3Height in rows of the content area inside the inherited chassis. Ignored in standalone mode.
subMenuLayoutIdString—ID of the buy/sell sub-menu layout within the layout pool. Uses shop_button: prefixed placeholders. Leave empty for no sub-menu.
subMenuTitleString<yellow>{item_name} OptionsTitle for the sub-menu. Supports color codes and placeholders. {item_name} placeholder available.
interactionConfigShopInteractionConfig—Click-type configuration for all shop interactions. Customize which mouse buttons trigger buy/sell/submenu actions.
ShopInteractionConfig (11)
FieldTypeDefaultDescription
buy1ClickInteractionTypeLEFT_CLICKClick type to buy 1 unit of the selected item
buyStackClickInteractionTypeLEFT_CLICKClick type to buy a full stack of the selected item
buyMaxClickInteractionTypeLEFT_CLICKClick type to buy the maximum possible (limited by balance, inventory space, and stock)
buySubmenuClickInteractionTypeSHIFT_LEFT_CLICKClick type to open the buy & sell sub-menu
buyCustomClickInteractionTypeLEFT_CLICKClick type to open the custom buy amount dialog
sell1ClickInteractionTypeRIGHT_CLICKClick type to sell 1 unit of the selected item
sellStackClickInteractionTypeLEFT_CLICKClick type to sell a full stack of the selected item
sellAllClickInteractionTypeLEFT_CLICKClick type to sell all matching items from the player's inventory
sellSubmenuClickInteractionTypeSHIFT_LEFT_CLICKClick type to open the sell sub-menu
sellCustomClickInteractionTypeLEFT_CLICKClick type to open the custom sell amount dialog
navigationClickInteractionTypeLEFT_CLICKClick type for navigation buttons (next page, previous page, close, info, back)
globalPlayerLimitInt0Global limit per player across all items in this shop. 0 = unlimited.
globalBuyLimitInt0Global buy limit across all items (all players combined). 0 = unlimited.
globalSellLimitInt0Global sell limit across all items (all players combined). 0 = unlimited.
defaultRefreshTicksInt20How often, in ticks, an open shop checks whether the stock it shows has changed (20 = 1 second); the shop is redrawn only when it has. Applies to items with a finite stock; an item's own refreshTicks replaces it. 0 = never check by itself.
perPlayerBooleanfalseIf true, every player has their own stock of each item: a purchase only lowers the buyer's stock, and a reset refills everyone's. Limits keep their own scope (playerLimit per player, globalLimit and the global buy/sell limits shared).
buySoundSound?—Sound played on successful purchase.
sellSoundSound?—Sound played on successful sale.
deniedSoundSound?—Sound played when a transaction is denied.
infoMessageList<String>emptyMessages shown when the information button is clicked. Supports color codes, placeholders and multiline.
buyAmountPromptString<yellow>Enter amount to buy:Message shown when entering a custom buy amount. Supports color codes and placeholders.
sellAmountPromptString<yellow>Enter amount to sell:Message shown when entering a custom sell amount. Supports color codes and placeholders.
amountConfirmButtonString<green>ConfirmLabel used for the confirmation button in amount dialogs. Supports color codes and placeholders.
amountInputLabelString<white>AmountLabel for the custom amount input. Supports color codes and placeholders.
amountInputPlaceholderString—Placeholder text inside the custom amount input. Supports color codes and placeholders.
amountInputWidthInt4Width of the custom amount input field.
amountInputMaxLengthInt10Maximum allowed length for the custom amount input.
invalidAmountMessageString<red>Please enter a valid amountMessage shown when the entered custom amount is not a positive whole number.
cannotAffordMessageString<red>You cannot afford this purchaseMessage when the player cannot afford a purchase. Placeholders: {amount}, {price}.
inventoryFullMessageString<red>Not enough inventory spaceMessage when the player's inventory is full. Placeholders: {amount}, {price}.
purchaseMessageString<green>Purchased x{amount} for {price}Message sent after a successful purchase. Placeholders: {amount}, {price}.
cannotSellMessageString<red>This item cannot be soldMessage when the item cannot be sold. Placeholders: {amount}, {price}.
notEnoughItemsMessageString<red>You don't have enough items to sellMessage when the player doesn't have enough items to sell. Placeholders: {amount}, {price}.
sellMessageString<green>Sold x{amount} for {price}Message sent after a successful sale. Placeholders: {amount}, {price}.
noEconomyMessageString<red>No economy availableMessage when no economy provider is available.
criteriaFailMessageString<red>You cannot access this shopMessage when the required criteria are not met.
lockedItemVar<Item>—Item displayed when a shop item is locked.
limitReachedMessageString<red>Purchase limit reachedMessage when a player reaches the purchase limit for an item.
defaultNameFormatString—Display name applied to items that declare none. Tokens: {item} = the item's own name, {buy}, {sell}, {stock}, {stock_max}, {currency}. Empty = keep the item's own name.
defaultLoreFormatList<String>emptyLore applied to items that declare none. Same tokens as defaultNameFormat; a line asking for an unavailable price is dropped. Empty = no default lore.
trendUpIconString<red>▲</red>What the trend_icon item placeholder of this shop shows when the price of an item went up since the last record. Supports color codes.Colors
trendDownIconString<green>▼</green>What the trend_icon placeholder shows when the price of an item went down since the last record. Supports color codes.Colors
trendStableIconString<gray>●</gray>What the trend_icon placeholder shows when the price of an item did not move, or when no earlier price is known. Supports color codes.Colors
itemPriceLineFormatString- {amount} × {name}One line of the {item_price} lore token (a price paid in items); there is one line per requested item. {amount} is the quantity asked, {name} the item name. Supports color codes.Colors
unknownItemNameStringunknown itemName shown for a price item whose own name cannot be read, in the {item_price} lore and in the list of missing items. Supports color codes.Colors
missingItemFormatString{amount} × {name}One entry of the {missing} list of cannotAffordItemsMessage. {amount} is the quantity that is lacking, {name} the item name. Supports color codes.Colors
missingItemSeparatorString, What goes between two entries of the {missing} list of cannotAffordItemsMessage. Supports color codes.Colors
defaultItemNameStringItemName given to the item in the {item_name} token of subMenuTitle when the item has no name of its own. Supports color codes.Colors
noPriceTextString—Shown instead of a number where a price paid in items has none: the buy_price and sell_price placeholders, and {price} in the inventory full message of such an item. Supports color codes.Colors
cooldownFormatString{d}d:{h}h:{m}m:{s}sPattern of the cooldown placeholder (time until the next reset) when the placeholder gives no format of its own. {d} days, {h} hours, {m} minutes, {s} seconds; hours, minutes and seconds are padded to 2 digits.
notificationWebhookIdString—Id of the webhook destination announcing this shop's transactions. Empty = no announcement. Shops does not resolve it: it travels on ShopTransactionEvent for whichever extension handles Discord.
priceFormatString{amount}Price format template. Placeholders: {amount} = formatted price, {currency} = currency symbol. Default: {amount}
currencySymbolString—Currency symbol used in price display. Example: $, €, pts.
artifactRef<ShopArtifact>—Artifact used for persistent storage and synchronisation.
ShopArtifact (1)
FieldTypeDefaultDescription
artifactIdStringshops—
Common TypeWriter fields (criteria)
FieldTypeDefaultDescription
criteriaList< Criteria>emptyCriteria that must be met to access the shop.

How to use it​

Work through the entry in this order. The full workflow is in Shops.

  1. Basics. title, rows (1 to 6, only for a shop that opens on its own), currency and fillerItem.
  2. Items. Each element of items has the item to sell, an optional name and lore, a priceMode, the three click actions, and its limits. The position of an item in this list (from 0) is its index in commands, placeholders and promotions.
  3. Menu. A simple layout in layoutPool selected by mainLayoutId, containing SHOP_ITEM markers where products appear and shop_button: buttons. A second layout, named in subMenuLayoutId, is the quantity sub-menu.
  4. Messages. Every refusal and confirmation has its own field. {amount} and {price} work in the purchase, sale and refusal messages.

Prices​

priceModeFields usedNotes
FIXEDfixedBuyPrice, fixedSellPricePrice per unit. A price of 0 cannot be sold back.
DYNAMICdynamicPricingprice, sellPrice, min, max, sellPriceMin, sellPriceMax, stockMax, step, rounding.
ITEMSitemPrice, defaultItemPriceBarter, see below.
Set max with dynamic pricing

In dynamicPricing, max is the price reached when the stock is empty. A max of 0 means no upper bound, so the price can climb far above price as stock falls. Set max (and sellPriceMax) whenever stockMax is above 0.

Barter: prices paid in items​

An item whose priceMode is ITEMS is sold against items instead of currency, for example one enchanted book for 12 copper ingots and 3 kelp.

FieldWhereRole
priceMode ITEMSitemEnables barter for this item.
itemPriceitemList of costs. Each line is an item plus an amount per unit bought. All lines are required together, and buying N units multiplies every line.
defaultItemPriceshopUsed by every ITEMS item that has no itemPrice of its own. An item's list replaces the shop's, it is never added to it.
cannotAffordItemsMessageshopRefusal when items are missing. {missing} lists them, for example 3 × Kelp, 12 × Copper Ingot, and {amount} is the quantity.

Rules enforced by the extension:

  • An ITEMS item with no price anywhere, or a line asking for zero items, is refused and logged. An empty price is never a free item.
  • What is missing is computed on the whole inventory before anything is removed, so a refused purchase never consumes part of the price.
  • Space for the bought item is checked before the payment is taken, because a barter cannot be refunded.
  • A catalogue item used as payment is matched by identity, not only by material.
  • BUY_MAX buys as many units as the inventory can pay for.
  • A barter item cannot be sold back for money.

Show the price with {item_price} in priceLore, defaultLoreFormat or the item's lore. {buy} and {sell} lines are dropped on barter items.

Taxes and resets​

taxRate is a percentage; taxMode PER_ITEM lets an item override it with taxRateOverride. reset restores stock and clears limit counters on its schedule. See Stock, limits and resets.

Tab of a shared menu​

Set baseMenuId to a shared Open GUI shell to turn the shop into a tab. views and defaultViewId work as in the GUI, and contentX, contentY, contentColumns and contentRows give the rectangle where products and the filler are drawn. Left empty, the shop opens as its own inventory.

Notifications​

notificationWebhookId is only carried on the transaction event. The Discord extension resolves it.

Per-player stock, refresh, fixed slots and sell all​

  • perPlayer. Each player has their own stock of each item: a purchase only lowers the buyer's stock, and a reset (the reset policy or /tw shop admin reset) refills everyone's. Only the stock changes scope: playerLimit stays per player, and globalLimit, globalPlayerLimit, globalBuyLimit and globalSellLimit keep their own scope. Stocks are not shared between servers. Only the gap to the maximum is stored, so a player whose stock is full leaves no entry in the artifact.
  • defaultRefreshTicks and the item refreshTicks. While a shop is open, it checks every that many ticks whether the stock shown has changed and redraws only when it has. This only applies to items with a finite stock (stockMax above 0). An item's refreshTicks replaces the shop default, 0 on the item means "use the shop default", and a shop default of 0 means no automatic check.
  • guiSlot. Pins an item at a slot counted row by row from 0 inside the content area (9 columns wide, or contentColumns in tab mode), on every page. The other items keep flowing through the remaining SHOP_ITEM markers. A slot outside the area, or already taken by an earlier item, sends the item back to the markers instead of hiding it. If pinned items hold every marker, the other items go on the free cells of the content area and a warning is logged once per shop; an item that fits nowhere is not shown and the warning names it.
  • sellAllEnabled. When off, the sell-all action is refused with cannotSellMessage. Selling a chosen amount is not affected.