> For the complete documentation index, see [llms.txt](https://axiomemu.gitbook.io/docs/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://axiomemu.gitbook.io/docs/data/shop.md).

# Shop System

The Shop system controls what items NPCs sell in their shops. It uses a two-file pattern: a central registry (`ShopManager.xml`) that maps NPCs to shop files, and individual shop files (`Shop/*.xml`) that define item inventories.

See also: [Data overview](/docs/data/data.md) for where this fits in the shared game content.

## File Locations

* **Registry**: `Data/ShopManager.xml`
* **Shop Files**: `Data/Shop/NNN - ShopName.xml`

Where `NNN` is a 3-digit shop index (000-999).

## Configuration Pattern

### Step 1: Register Shop in ShopManager.xml

Define which NPC uses which shop file.

```xml
<ShopList>
    <Shop Index="0" Npc="251" Map="*" X="*" Y="*"/>
</ShopList>
```

| Attribute | Type       | Description                                                                                                                                                                                                          |
| --------- | ---------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `Index`   | int        | Shop index. Must match the shop file name prefix (e.g., `000`, `001`). Index values are stored in a map keyed by Index, so each Index must be unique in ShopManager.xml. Filenames are limited to 3 digits (000-999) |
| `Npc`     | int/string | NPC class ID that opens this shop. Use `"*"` to match any NPC (converted to -1 internally)                                                                                                                           |
| `Map`     | int/string | Map number where this shop is available. Use `"*"` to match any map (converted to -1 internally)                                                                                                                     |
| `X`       | int/string | X coordinate where this shop is available. Use `"*"` to match any X (converted to -1 internally)                                                                                                                     |
| `Y`       | int/string | Y coordinate where this shop is available. Use `"*"` to match any Y (converted to -1 internally)                                                                                                                     |

### Step 2: Create Shop File

Create `Data/Shop/NNN - Description.xml` where `NNN` matches the `Index` from ShopManager.xml.

{% hint style="info" %}
The filename format is strict: `NNN - Description.xml` where the first 3 characters are digits, followed by `-` (space-dash-space).
{% endhint %}

## Shop File Structure

Individual shop files define the items available for purchase. Items can be listed directly under `<Shop>`, or grouped into `<Page>` elements for multi-page shops.

### Flat Layout (Single Page)

```xml
<?xml version="1.0"?>
<Shop>
    <Item Cat="0" Index="4" Level="7" Durability="0" Duration="0" Skill="1" Luck="0" Option="0"
          Excellent="48" Ancient="0" JOH="0" OpEx="0"
          Socket1="255" Socket2="255" Socket3="255" Socket4="255" Socket5="255"/>
    <!-- More items... -->
</Shop>
```

### Multi-Page Layout

Use `<Page>` elements to organize items across multiple pages. Each `<Page>` starts a new shop page. Up to 8 pages are supported.

```xml
<?xml version="1.0"?>
<Shop>
    <Page>
        <!-- Page 1: Weapons -->
        <Item Cat="0" Index="4" Level="7" Durability="0" Duration="0" Skill="1" Luck="0" Option="0"
              Excellent="48" Ancient="0" JOH="0" OpEx="0"
              Socket1="255" Socket2="255" Socket3="255" Socket4="255" Socket5="255"/>
    </Page>
    <Page>
        <!-- Page 2: Armor -->
        <Item Cat="7" Index="0" Level="5" Durability="0" Duration="0" Skill="0" Luck="0" Option="0"
              Excellent="0" Ancient="0" JOH="0" OpEx="0"
              Socket1="255" Socket2="255" Socket3="255" Socket4="255" Socket5="255"/>
    </Page>
</Shop>
```

{% hint style="info" %}
Items placed directly under `<Shop>` (without a `<Page>` wrapper) are assigned to page 0. If a page fills up, subsequent items automatically overflow to the next page.
{% endhint %}

{% hint style="warning" %}
Pages beyond the 8-page limit are silently ignored. If your shop has more items than 8 pages can hold (960 slots total), consider splitting into multiple shops.
{% endhint %}

### Item Attributes

| Attribute    | Type | Default | Description                                                                                     |
| ------------ | ---- | ------- | ----------------------------------------------------------------------------------------------- |
| `Cat`        | int  | -       | Item category (0-15). See [Item.xml](/docs/data/item/item.md) for categories                    |
| `Index`      | int  | -       | Item index within category. Together with `Cat`, identifies the item                            |
| `Level`      | int  | 0       | Item level (0-15). Adds stats and level requirement                                             |
| `Durability` | int  | 0       | For regular items: durability (0 = use default). For stackable items: stack count (0 = 1)       |
| `Duration`   | int  | 0       | Temporary item duration in **seconds** (0 = permanent). See [Temporary Items](#temporary-items) |
| `Skill`      | int  | 0       | Has skill (1 = yes, 0 = no). Adds +skill option and enables special attacks                     |
| `Luck`       | int  | 0       | Has Luck (1 = yes, 0 = no). Adds critical damage chance                                         |
| `Option`     | int  | 0       | Additional option level (0-7). Each level adds 4 to primary stat                                |
| `Excellent`  | int  | 0       | Excellent option flags (bitwise). See Excellent Options section                                 |
| `Ancient`    | int  | 0       | Ancient item ID (0 = none). See Ancient item configuration                                      |
| `JOH`        | int  | 0       | Jewel of Harmony option index (0 = none)                                                        |
| `OpEx`       | int  | 0       | 380 item option index (0 = none)                                                                |
| `Socket1`    | int  | 255     | Socket 1 option index (255 = empty socket)                                                      |
| `Socket2`    | int  | 255     | Socket 2 option index (255 = empty socket)                                                      |
| `Socket3`    | int  | 255     | Socket 3 option index (255 = empty socket)                                                      |
| `Socket4`    | int  | 255     | Socket 4 option index (255 = empty socket)                                                      |
| `Socket5`    | int  | 255     | Socket 5 option index (255 = empty socket)                                                      |

### Excellent Options (Bitwise Flags)

The `Excellent` attribute uses bitwise flags (0-63). Add values together for multiple options:

| Bit | Value | Description        |
| --- | ----- | ------------------ |
| 0   | 1     | Excellent option 1 |
| 1   | 2     | Excellent option 2 |
| 2   | 4     | Excellent option 3 |
| 3   | 8     | Excellent option 4 |
| 4   | 16    | Excellent option 5 |
| 5   | 32    | Excellent option 6 |

**Example**: `Excellent="48"` (32 + 16) = Options 5 and 6 enabled

**Actual option effects** are defined in [ItemOption.xml](/docs/data/item/itemoption.md) and vary by item type:

* **Weapons**: Typically include Mana/HP per kill, attack speed, damage bonuses
* **Armor/Shields**: Typically include HP/Mana boosts, defense, damage reduction
* **Wings**: May include max HP%, attack speed, or other special effects

{% hint style="info" %}
The `Excellent` field in the shop configuration is a bitmask that **enables** option slots. The actual stat bonuses applied depend on the item's category and are looked up in ItemOption.xml at indices 3-8 (corresponding to bits 0-5).
{% endhint %}

{% hint style="warning" %}
Excellent options only apply if the item supports them. Check ItemOption.xml for specific item index ranges and their assigned bonuses before configuring shop items with excellent options.
{% endhint %}

## Temporary Items

The `Duration` attribute creates time-limited items that expire after a set number of seconds. When a player purchases a temporary item, the server tracks its remaining lifetime.

| Duration Value | Meaning                  |
| -------------- | ------------------------ |
| `0`            | Permanent item (default) |
| `3600`         | 1 hour                   |
| `86400`        | 1 day (24 hours)         |
| `604800`       | 1 week (7 days)          |
| `2592000`      | 30 days                  |

**Example**: A 7-day temporary weapon:

```xml
<Item Cat="0" Index="4" Level="9" Durability="0" Duration="604800" Skill="1" Luck="1" Option="4"
      Excellent="63" Ancient="0" JOH="0" OpEx="0"
      Socket1="255" Socket2="255" Socket3="255" Socket4="255" Socket5="255"/>
```

{% hint style="info" %}
Duration is transmitted to the client via the shop price/info packet. The client displays the remaining time in the item tooltip. When the duration expires, the item is removed from the player's inventory.
{% endhint %}

{% hint style="warning" %}
Temporary items are useful for event rewards, premium shops, or trial equipment. Ensure your pricing reflects the temporary nature - players expect time-limited items to cost less than permanent equivalents.
{% endhint %}

## Shop Matching Algorithm

When a player clicks an NPC, the server uses a two-pass algorithm to find the correct shop:

### Pass 1: Exact Match

Finds the first shop where **ALL** four fields match exactly:

* `Npc` == NPC class ID
* `Map` == map number
* `X` == X coordinate
* `Y` == Y coordinate

Wildcards (`"*"`) are converted to `-1` during loading, so a shop with `Map="*"` will only match if the actual map number is `-1` (invalid), not "any map".

### Pass 2: NPC-Only Match

If Pass 1 fails, finds the first shop where:

* `Npc` == NPC class ID
* `Npc` is NOT `-1` (not a wildcard)
* `Map`, `X`, `Y` are completely ignored

### No Match

If neither pass succeeds, the NPC has no shop and the interaction fails.

{% hint style="info" %}
**How to use wildcards correctly**:

* Use `Map="*" X="*" Y="*"` to create a **global NPC shop** that works anywhere
* This works because Pass 2 ignores position entirely
* Pass 1 will fail (map/position don't match -1), then Pass 2 succeeds (NPC class matches)
  {% endhint %}

{% hint style="warning" %}
**Position-specific shops**: To have the same NPC with different shops at different locations:

1. Create shop entries with exact Map/X/Y coordinates
2. Do NOT create a wildcard entry for that NPC
3. Pass 1 will match the exact location
4. If player clicks the NPC at a non-registered location, Pass 2 will fail (no wildcard fallback)
   {% endhint %}

{% hint style="warning" %}
The algorithm returns the **first match** found when iterating through registered shops. If multiple shops match the criteria, only the first one is used. Shop registration order in ShopManager.xml matters.
{% endhint %}

## Item Pricing

Shop items use pricing defined in [ItemValue.xml](/docs/data/item/itemvalue.md). The currency system works as follows:

**Currency Selection** (priority order, highest first):

1. **GoblinPoints** - If `Coin3` toggle is set in ItemValue.xml
2. **WCoinP** (Premium coins) - If `Coin2` toggle is set
3. **WCoinC** (Cash shop coins) - If `Coin1` toggle is set
4. **Zen** (default) - If no coin toggles are set

{% hint style="info" %}
The `Coin1`, `Coin2`, and `Coin3` fields in ItemValue.xml are **toggles** (0 or 1), not currency amounts. They select which currency to use. The actual price is stored in the `BuyPrice` and `SellPrice` fields.
{% endhint %}

**Price Multipliers**:

* **Stackable items**: Buy/sell prices multiply by stack count automatically
* **Non-stackable items**: Use base prices from ItemValue.xml
* Items without ItemValue.xml entries use calculated prices from item properties

See [ItemValue documentation](/docs/data/item/itemvalue.md) for details on configuring custom prices and currency types.

## Dependencies

| File                                                        | Purpose                                                                  |
| ----------------------------------------------------------- | ------------------------------------------------------------------------ |
| [Item.xml](/docs/data/item/item.md)                         | Defines all available items, categories, and base stats                  |
| [ItemValue.xml](/docs/data/item/itemvalue.md)               | Defines custom buy/sell prices and currency types (Zen/WCoinC/WCoinP/GP) |
| [ItemStack.xml](/docs/data/item/itemstack.md)               | Defines which items are stackable and their maximum stack sizes          |
| [SocketItemType.xml](/docs/data/item/socketitemtype.md)     | Defines which items can have sockets and maximum socket count            |
| [SocketItemOption.xml](/docs/data/item/socketitemoption.md) | Defines socket option effects                                            |
| [SetItemOption.xml](/docs/data/item/setitemoption.md)       | Defines ancient set bonuses                                              |

## Examples

{% tabs %}
{% tab title="Basic Weapon Shop" %}

```xml
<?xml version="1.0"?>
<Shop>
    <!-- Basic sword with skill, no options -->
    <Item Cat="0" Index="4" Level="0" Durability="0" Duration="0" Skill="1" Luck="0" Option="0"
          Excellent="0" Ancient="0" JOH="0" OpEx="0"
          Socket1="255" Socket2="255" Socket3="255" Socket4="255" Socket5="255"/>

    <!-- Enhanced sword: +7, +Luck, +2 option, Excellent (Mana/kill + Attack speed) -->
    <Item Cat="0" Index="4" Level="7" Durability="0" Duration="0" Skill="1" Luck="1" Option="2"
          Excellent="7" Ancient="0" JOH="0" OpEx="0"
          Socket1="255" Socket2="255" Socket3="255" Socket4="255" Socket5="255"/>
</Shop>
```

{% endtab %}

{% tab title="Potion Shop" %}

```xml
<?xml version="1.0"?>
<Shop>
    <!-- Stackable potions: Durability = stack count -->
    <Item Cat="14" Index="0" Level="0" Durability="10" Duration="0" Skill="0" Luck="0" Option="0"
          Excellent="0" Ancient="0" JOH="0" OpEx="0"
          Socket1="255" Socket2="255" Socket3="255" Socket4="255" Socket5="255"/>

    <Item Cat="14" Index="1" Level="0" Durability="10" Duration="0" Skill="0" Luck="0" Option="0"
          Excellent="0" Ancient="0" JOH="0" OpEx="0"
          Socket1="255" Socket2="255" Socket3="255" Socket4="255" Socket5="255"/>

    <!-- Mana potions -->
    <Item Cat="14" Index="4" Level="0" Durability="10" Duration="0" Skill="0" Luck="0" Option="0"
          Excellent="0" Ancient="0" JOH="0" OpEx="0"
          Socket1="255" Socket2="255" Socket3="255" Socket4="255" Socket5="255"/>
</Shop>
```

{% endtab %}

{% tab title="Position-Specific Shop" %}

```xml
<!-- ShopManager.xml -->
<ShopList>
    <!-- Event vendor only appears at specific location -->
    <Shop Index="25" Npc="620" Map="0" X="124" Y="143"/>

    <!-- Same NPC at different location has different shop -->
    <Shop Index="26" Npc="620" Map="0" X="130" Y="150"/>
</ShopList>
```

Each shop index (25, 26) has its own shop file with different items.
{% endtab %}

{% tab title="Ancient Item Shop" %}

```xml
<?xml version="1.0"?>
<Shop>
    <!-- Ancient item (full set bonus when equipped) -->
    <Item Cat="0" Index="19" Level="9" Durability="0" Duration="0" Skill="1" Luck="1" Option="4"
          Excellent="0" Ancient="5" JOH="0" OpEx="0"
          Socket1="255" Socket2="255" Socket3="255" Socket4="255" Socket5="255"/>
</Shop>
```

`Ancient="5"` refers to an ancient set defined in SetItemOption.xml.
{% endtab %}

{% tab title="Multi-Page Shop" %}

```xml
<?xml version="1.0"?>
<Shop>
    <Page>
        <!-- Page 1: Swords -->
        <Item Cat="0" Index="0" Level="3" Durability="0" Duration="0" Skill="1" Luck="0" Option="0"
              Excellent="0" Ancient="0" JOH="0" OpEx="0"
              Socket1="255" Socket2="255" Socket3="255" Socket4="255" Socket5="255"/>
        <Item Cat="0" Index="4" Level="5" Durability="0" Duration="0" Skill="1" Luck="0" Option="2"
              Excellent="0" Ancient="0" JOH="0" OpEx="0"
              Socket1="255" Socket2="255" Socket3="255" Socket4="255" Socket5="255"/>
    </Page>
    <Page>
        <!-- Page 2: Armor -->
        <Item Cat="7" Index="0" Level="3" Durability="0" Duration="0" Skill="0" Luck="0" Option="0"
              Excellent="0" Ancient="0" JOH="0" OpEx="0"
              Socket1="255" Socket2="255" Socket3="255" Socket4="255" Socket5="255"/>
        <Item Cat="8" Index="0" Level="3" Durability="0" Duration="0" Skill="0" Luck="0" Option="0"
              Excellent="0" Ancient="0" JOH="0" OpEx="0"
              Socket1="255" Socket2="255" Socket3="255" Socket4="255" Socket5="255"/>
    </Page>
    <Page>
        <!-- Page 3: Consumables -->
        <Item Cat="14" Index="0" Level="0" Durability="20" Duration="0" Skill="0" Luck="0" Option="0"
              Excellent="0" Ancient="0" JOH="0" OpEx="0"
              Socket1="255" Socket2="255" Socket3="255" Socket4="255" Socket5="255"/>
    </Page>
</Shop>
```

Each `<Page>` creates a separate page in the shop UI. The client displays page navigation (1/3, 2/3, 3/3). Up to 8 pages supported.
{% endtab %}

{% tab title="Temporary Item Shop" %}

```xml
<?xml version="1.0"?>
<Shop>
    <!-- 7-day trial weapon: full options, temporary -->
    <Item Cat="0" Index="19" Level="11" Durability="0" Duration="604800" Skill="1" Luck="1" Option="4"
          Excellent="63" Ancient="0" JOH="0" OpEx="0"
          Socket1="255" Socket2="255" Socket3="255" Socket4="255" Socket5="255"/>

    <!-- 30-day wings -->
    <Item Cat="12" Index="0" Level="0" Durability="0" Duration="2592000" Skill="0" Luck="0" Option="0"
          Excellent="0" Ancient="0" JOH="0" OpEx="0"
          Socket1="255" Socket2="255" Socket3="255" Socket4="255" Socket5="255"/>

    <!-- Permanent item (Duration=0) for comparison -->
    <Item Cat="14" Index="0" Level="0" Durability="20" Duration="0" Skill="0" Luck="0" Option="0"
          Excellent="0" Ancient="0" JOH="0" OpEx="0"
          Socket1="255" Socket2="255" Socket3="255" Socket4="255" Socket5="255"/>
</Shop>
```

`Duration="604800"` = 7 days in seconds. `Duration="2592000"` = 30 days. `Duration="0"` = permanent (default).
{% endtab %}
{% endtabs %}

## Important Behavior

### Filename Parsing

The server only loads shop files matching the pattern `NNN - Name.xml`:

* First 3 characters MUST be digits (000-999)
* Must have space-dash-space (`-`) separator after digits (character positions \[3] = space, \[4] = dash, \[5] = space)
* Filename must contain `.xml`

Files like `1 - Shop.xml`, `01 - Shop.xml`, or `Shop001.xml` will NOT load.

{% hint style="info" %}
Filenames must start with exactly 3 digits (000-999). The numeric prefix is parsed with `atoi`, then matched against the `Index` registered in ShopManager.xml. Any 3-digit prefix that has a matching ShopManager.xml entry will load.
{% endhint %}

### Shop Capacity and Layout

Each shop page has 120 slots arranged in a grid. A shop can have up to 8 pages, for a maximum of 960 total slots.

**Per-Page Grid**:

* **8 columns** (X: 0-7)
* **15 rows** (Y: 0-14)
* **Total per page**: 120 slots

**Item Placement**:

* Items auto-arrange left-to-right, top-to-bottom during loading
* Items occupy multiple slots based on their size (width × height from Item.txt)
* Example: A 2×3 item occupies 6 adjacent slots in the grid
* The server validates that items fit within grid boundaries (8 columns wide, 15 rows tall)
* If a page fills up, items automatically overflow to the next page
* When using `<Page>` elements, each `<Page>` explicitly starts a new page

**Slot Calculation**: `Slot = (Y × 8) + X`

* Slot 0 = position (0, 0) - top-left
* Slot 7 = position (7, 0) - top-right
* Slot 119 = position (7, 14) - bottom-right

### Stackable Items

For stackable items (potions, jewels, etc.), the `Durability` attribute controls the stack count:

* `Durability="0"` = 1 item (default)
* `Durability="10"` = stack of 10 items
* `Durability="255"` = stack of 255 items (maximum)

**Price behavior**:

* Buy price: `ItemValue.xml price × stack count`
* Sell price: `ItemValue.xml sell price × stack count`
* Prices automatically scale when buying/selling stacked items from shops

{% hint style="info" %}
The server detects stackable items using `ItemStack.xml` configuration. Items not defined as stackable will use `Durability` as actual durability value instead of stack count.
{% endhint %}

### Socket Slots

Set `Socket1`-`Socket5` to `255` for empty sockets.

**Important behavior**:

* The server validates socket support using `SocketItemType.xml` before assigning sockets
* Only items that support sockets (defined in SocketItemType.xml) will have socket slots allocated
* Socket slots are only assigned up to the maximum socket count for that item type
* Example: If an item supports 3 sockets, only `Socket1`-`Socket3` are used; `Socket4` and `Socket5` are ignored

If an item doesn't support sockets, all socket attributes are ignored regardless of configuration values.

### NPC Matching Priority

Always use specific Map/X/Y when possible. Wildcard shops (`Map="*"`) can cause unexpected behavior if multiple NPCs share the same class ID.

## Common Issues

{% hint style="warning" %}
**Shop not appearing**: Check these common mistakes:

* Filename doesn't match `NNN - Name.xml` pattern
* Index in ShopManager.xml doesn't match filename prefix
* NPC class ID mismatch between ShopManager.xml and actual NPC in map
  {% endhint %}

{% hint style="warning" %}
**Items appear but can't be purchased**: Check [ItemValue.xml](/docs/data/item/itemvalue.md) for pricing. Items without prices default to 0 Zen.
{% endhint %}

{% hint style="warning" %}
**Wrong shop opens**: Multiple shops with same NPC ID will use first match. Use Map/X/Y coordinates to differentiate position-specific shops.
{% endhint %}

{% hint style="info" %}
**Testing shops**: Use `/move` command to teleport to NPC location, or spawn NPC in test map to verify shop contents before deploying to production.
{% endhint %}

## Adding a New Shop

1. **Choose Shop Index**: Pick an unused index. The filename prefix supports 000-999
2. **Register in ShopManager.xml**:

   ```xml
   <Shop Index="50" Npc="999" Map="0" X="100" Y="100"/>
   ```

   Use `Map="*" X="*" Y="*"` for global shops (any location).
3. **Create Shop File**: `Data/Shop/050 - My Custom Shop.xml`
   * Filename MUST be exactly 3 digits, space-dash-space, description
   * Shop index (50) must match filename prefix (050)
4. **Add Items**: Use examples above as templates
5. **Configure Pricing** (optional): Add entries to ItemValue.xml for custom prices/currencies
6. **Configure Stackables** (optional): Ensure stackable items are defined in ItemStack.xml
7. **Restart GameServer**: Shops load at startup only
8. **Test**: Click NPC to verify shop opens with correct items and prices

{% hint style="info" %}
Shops only load during server startup. Hot reload is not exposed as an admin command in the current build; restart the GameServer after editing shop files.
{% endhint %}

{% hint style="warning" %}
A shop file is only loaded if its 3-digit prefix matches an `Index` registered in ShopManager.xml. Files whose prefix has no matching entry are silently skipped.
{% endhint %}
