> 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/plugins/achievements.md).

# Achievements

Categorized, tiered achievements with configurable rewards - triggers, tiers, points, the achievement window and the unlock toast

The Achievement plugin tracks what players do (kills, levels, resets, upgrades, chaos mixes, events, quests, play time and more) and turns it into a card grid of achievements. Every achievement has one or more **tiers**; reaching a tier's target completes it, and the player clicks **Claim** on the card to receive that tier's rewards and achievement points.

{% hint style="info" %}
**Paid add-on** (module `achievement-plugin`). Without the module the plugin loads nothing, the client receives no catalog, and the Achievements entry does not appear in the Window Menu.
{% endhint %}

## Opening the Window

Press **U** (Window Menu) and pick **Achievements** - it is the last entry. The entry is only listed when the server shipped an achievement catalog, so an unlicensed server never shows it. There is no dedicated letter hotkey (every letter is already taken). When a tier completes, a toast appears under the top bar and a gold chat line is printed.

## File Location

```
Data/Plugins/Achievements/
  Settings.xml                                  optional global settings
  01 - Combat.xml                               one file per category
  02 - Progression.xml
  ...
  _TEMPLATE - Achievement Format Reference.xml  reference, never loaded
```

## Dependencies

| Depends On                                          | Purpose                                                                                                   |
| --------------------------------------------------- | --------------------------------------------------------------------------------------------------------- |
| [Monster.xml](/docs/data/monster/monster.md)        | `MonsterClass` filters                                                                                    |
| [Item.xml](/docs/data/item/item.md)                 | Item filters and Item rewards (`ItemType` / `ItemIndex`)                                                  |
| [EventItemBag](/docs/data/eventitembag.md)          | `ItemBag` rewards                                                                                         |
| [Effect.xml](/docs/data/effect.md)                  | `Buff` rewards (`BuffIndex`)                                                                              |
| [Custom Quests](/docs/data/plugins/customquests.md) | `QuestComplete` trigger counts custom quest turn-ins                                                      |
| [Lucky Spin](/docs/data/plugins/luckyspin.md)       | `LuckySpin` trigger                                                                                       |
| [Reset System](/docs/data/plugins/resetsystem.md)   | `Reset` trigger (reset / grand / master counts)                                                           |
| DataServer                                          | Table `dbo.AchievementProgress` (Name varchar(10), Blob varbinary(1254)). Run the shipped SQL script once |

## How Files Are Loaded

* Every `*.xml` in the folder is loaded in alphabetical order. `Settings.xml` and files starting with `_` are skipped.
* One file = one `<Category>`; its `<Achievement>` children belong to it.
* Category `Id` must be 1 to 16 and unique. Achievement `Id` must be 0 to 249 and **globally unique across all files**.
* Progress is stored by achievement `Id`. Never renumber a live achievement; removing one leaves harmless orphan progress in the database.
* A bad entry (duplicate id, missing trigger, no tiers) is logged and skipped; the rest of the file still loads.
* Reload with `$achreload` (GM) or the plugin reload. Players already online keep the old catalog until they relog.

## Category

```xml
<Category Id="1" Name="Combat" Order="1" Icon="ach/cat_combat">
  <Achievement .../>
</Category>
```

| Attribute | Type              | Default    | Effect                                   |
| --------- | ----------------- | ---------- | ---------------------------------------- |
| `Id`      | 1-16              | -          | Stable category id                       |
| `Name`    | string (31 chars) | `Category` | Tab label in the window                  |
| `Order`   | int               | `Id`       | Tab order, ascending                     |
| `Icon`    | string            | -          | Client sprite name (see Icons). Optional |

## Achievement

```xml
<Achievement Id="2" Name="Dragon Slayer" Icon="ach/dragon" Text="Slay Budge Dragons in Lorencia." Enabled="1">
  <Trigger Type="KillMonster" MonsterClass="2" Map="0"/>
  <Tier Target="10"   Points="5">  <Reward Type="Zen" Amount="50000"/> </Tier>
  <Tier Target="100"  Points="10"> <Reward Type="Item" ItemType="14" ItemIndex="13" Count="1"/> </Tier>
  <Tier Target="1000" Points="15"> <Reward Type="Item" ItemType="14" ItemIndex="14" Count="2"/> </Tier>
</Achievement>
```

| Attribute | Type               | Default | Effect                                                           |
| --------- | ------------------ | ------- | ---------------------------------------------------------------- |
| `Id`      | 0-249              | -       | Global unique id                                                 |
| `Name`    | string (31 chars)  | `""`    | Card title                                                       |
| `Icon`    | string             | `""`    | Client sprite name; missing = `ach/default`, then an empty plate |
| `Text`    | string (127 chars) | `""`    | One-line description on the card                                 |
| `Enabled` | 0/1                | 1       | 0 = not loaded                                                   |

### Trigger

Exactly one `<Trigger>` per achievement. Every achievement owns one **counter**. The counter mode is fixed by the trigger type:

* **Count** types add 1 per matching event.
* **Max** types keep the highest value seen (level, reset count, item level).

Filters default to `-1` = any. Generic `A` and `B` attributes override the friendly names below.

| Type            | Filters                                                             | Mode  | Counts                                                                                                                                                                           |
| --------------- | ------------------------------------------------------------------- | ----- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `KillMonster`   | `MonsterClass`, `Map`                                               | Count | Monster kills                                                                                                                                                                    |
| `KillBoss`      | `MonsterClass`, `Map`                                               | Count | Boss kills. Only classes listed in `Data\Plugins\BossClasses.xml` count (shared with the Bridge exporter)                                                                        |
| `KillPlayer`    | -                                                                   | Count | PvP kills (killer side)                                                                                                                                                          |
| `ReachLevel`    | -                                                                   | Max   | Highest character level reached                                                                                                                                                  |
| `Reset`         | `Kind` (0 reset, 1 grand, 2 master)                                 | Max   | Highest reset count of that kind                                                                                                                                                 |
| `TalkNpc`       | `NpcClass`                                                          | Count | NPC talks. Only NPCs that open a dialog count (shops, warehouse, quest givers). Guards (247, 249) are monsters with no dialog and never fire                                     |
| `VisitMap`      | `Map`                                                               | Count | Map entries (use `Target="1"` for "visit once")                                                                                                                                  |
| `WinEvent`      | `EventIndex` (0 Blood Castle, 2 Chaos Castle), `EventLevel`         | Count | Event wins                                                                                                                                                                       |
| `ChaosMix`      | `MixType`, `Success` (1 success, 0 fail, -1 any)                    | Count | Chaos Machine results. `MixType` is the mix id: 3/4/22/23/49/50 = +10..+15, 7 Wings 1, 11 Wings 2, 24 Wings 3, 5 Dinorant, 6 Fruit, 8 Blood Castle ticket, 13/14 pets, 27 Fenrir |
| `ItemUpgrade`   | `Method` (0 Bless, 1 Soul, 2 Chaos Machine), `ItemType`+`ItemIndex` | Max   | Highest item level reached by an upgrade the player performed                                                                                                                    |
| `ItemPickup`    | `ItemType`+`ItemIndex`, `Level`, or `Group="Jewel"`                 | Count | Ground pickups that landed in the inventory. `Group="Jewel"` = Bless, Soul, Life, Creation, Guardian, Gemstone, Harmony, Chaos, Lower/Higher Refining Stone                      |
| `LuckySpin`     | `WheelId`, `Won` (1 won, -1 any)                                    | Count | Paid spins                                                                                                                                                                       |
| `QuestComplete` | `QuestId`                                                           | Count | Custom quest turn-ins                                                                                                                                                            |
| `PlayTime`      | -                                                                   | Count | Full minutes online                                                                                                                                                              |
| `Trade`         | -                                                                   | Count | Completed trades (both sides)                                                                                                                                                    |
| `PShopSell`     | -                                                                   | Count | Personal store sales (seller)                                                                                                                                                    |
| `RareDrop`      | `ItemType`+`ItemIndex`                                              | Count | Rare drops the player owns                                                                                                                                                       |

### Tier

1 to 8 tiers per achievement. `Target` values must strictly ascend.

| Attribute | Type    | Default | Effect                                                |
| --------- | ------- | ------- | ----------------------------------------------------- |
| `Target`  | int     | 1       | Counter value that completes the tier                 |
| `Points`  | 0-65535 | 0       | Achievement points added when the tier is **claimed** |

### Reward

0 to 4 per tier. Same grammar as [Custom Quests](/docs/data/plugins/customquests.md#rewards).

| Type       | Attributes                                                                        | Effect                                                                      |
| ---------- | --------------------------------------------------------------------------------- | --------------------------------------------------------------------------- |
| `Zen`      | `Amount`                                                                          | Zen added                                                                   |
| `Exp`      | `Amount`                                                                          | Experience, capped by `MaxLevelUpQuest`                                     |
| `Item`     | `ItemType`, `ItemIndex`, `Level`, `Skill`, `Luck`, `Option`, `Excellent`, `Count` | Item into the inventory. One free slot per item reward is required to claim |
| `Points`   | `PointType` (`Normal` / `Master`), `Amount`                                       | Stat points or master points                                                |
| `Currency` | `CurrencyType` (`WCoin` / `Goblin`), `Amount`                                     | Cash-shop currency                                                          |
| `Buff`     | `BuffIndex`, `Value1`, `Value2`, `DurationMin`                                    | Server effect for `DurationMin` minutes                                     |
| `ItemBag`  | `BagIndex`                                                                        | Drops the bag at the player's feet (not inventory-checked)                  |

## Settings.xml

```xml
<Achievements AutosaveSec="180"/>
```

| Attribute     | Type | Range | Default | Effect                                                                                                    |
| ------------- | ---- | ----- | ------- | --------------------------------------------------------------------------------------------------------- |
| `AutosaveSec` | int  | 30+   | 180     | Seconds between background saves of changed counters. Tier completions and claims always save immediately |

## Icons

Icons are client sprites from the atlas `Data/Interface/UI/atlas/achievements.png` + `achievements.xml`, referenced as `ach/<name>`. The same atlas serves both UI skins. To add icons (48x48 PNG), use `Tools\AtlasPacker` (see [Tools](/docs/tools/tools.md)): `AtlasPacker.exe -unpack <client>\Data\Interface\UI\atlas\achievements.xml MyIcons`, add your PNGs to `MyIcons` (keep `default.png`), then `AtlasPacker.exe MyIcons achievements ach -out <client>\Data\Interface\UI\atlas`. The file name is the sprite name: `slayer.png` becomes `ach/slayer`. Any `Icon` that is missing from the atlas falls back to `ach/default`; if that is missing too, the card shows an empty plate. Text and progress are unaffected.

## Important Behavior

* Tier completion is derived from the counter (`Counter >= Target`), not stored. Raising a `Target` after players progressed can un-complete a tier they have not claimed yet; lowering one completes it on their next progress tick or relog.
* Claimed tiers are stored as a bitmask and never revert.
* Progress before the DataServer answered the load request is dropped (never saved over the database). Players see this only on a slow DataServer.
* Achievement points are display-only (window header, per tier in the tooltip). They are the sum of claimed tiers' `Points`.
* The Bridge plugin can export the same events; achievements do not depend on it.

## GM Commands

GM authority (`Authority & 32`) required.

| Command                  | Effect                                                                          |
| ------------------------ | ------------------------------------------------------------------------------- |
| `$achstate [id]`         | List non-zero counters (or one achievement) with claimed bits and tiers reached |
| `$achset <id> <counter>` | Set a counter directly; newly reached tiers unlock like live progress           |
| `$achreset`              | Wipe the character's achievement progress                                       |
| `$achreload`             | Reload the folder and refresh the catalog cache                                 |

## Examples

{% tabs %}
{% tab title="Play time ladder" %}

```xml
<?xml version="1.0" encoding="UTF-8"?>
<Category Id="2" Name="Progression" Order="2">
  <Achievement Id="14" Name="Devoted" Icon="ach/playtime" Text="Spend time in the world (minutes online).">
    <Trigger Type="PlayTime"/>
    <Tier Target="60"   Points="5">  <Reward Type="Zen" Amount="100000"/> </Tier>
    <Tier Target="600"  Points="10"> <Reward Type="Item" ItemType="14" ItemIndex="13" Count="2"/> </Tier>
    <Tier Target="6000" Points="25"> <Reward Type="Currency" CurrencyType="Goblin" Amount="300"/> </Tier>
  </Achievement>
</Category>
```

{% endtab %}

{% tab title="Item +9 with Jewel of Soul only" %}

```xml
<Achievement Id="26" Name="Soul Gambler" Icon="ach/upgrade" Text="Reach +9 using Jewels of Soul.">
  <Trigger Type="ItemUpgrade" Method="1"/>
  <Tier Target="7" Points="5">  <Reward Type="Item" ItemType="14" ItemIndex="14" Count="2"/> </Tier>
  <Tier Target="9" Points="15"> <Reward Type="Item" ItemType="12" ItemIndex="15" Count="2"/> </Tier>
</Achievement>
```

{% endtab %}

{% tab title="Wings + a specific quest" %}

```xml
<Achievement Id="23" Name="Wingmaker" Icon="ach/wings" Text="Craft first-class wings.">
  <Trigger Type="ChaosMix" MixType="7" Success="1"/>
  <Tier Target="1" Points="20"> <Reward Type="Item" ItemType="14" ItemIndex="22" Count="2"/> </Tier>
</Achievement>
<Achievement Id="27" Name="Kundun's Contract" Icon="ach/quest" Text="Complete quest 15.">
  <Trigger Type="QuestComplete" QuestId="15"/>
  <Tier Target="1" Points="10"> <Reward Type="Zen" Amount="500000"/> </Tier>
</Achievement>
```

{% endtab %}
{% endtabs %}

## Common Issues

{% hint style="warning" %}
No Achievements entry in the Window Menu: the module is not licensed, or no file loaded. Check the GameServer log for `[Achievement] Loaded N achievements in M categories`.
{% endhint %}

{% hint style="warning" %}
Claim rejected with "Not enough inventory space": every `Item` reward in that tier needs a free slot. The rewards are not granted partially.
{% endhint %}

{% hint style="warning" %}
Progress vanished after a restart: the DataServer table `dbo.AchievementProgress` is missing. Apply the shipped SQL script.
{% endhint %}
