> For the complete documentation index, see [llms.txt](https://xpressdev.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://xpressdev.gitbook.io/docs/mods/ttms.md).

# TTMs

Usable TMs and TRs for Cobblemon - buy or give unlimited-use TMs and consumable TRs, taught by right-click, with a paginated shop and NPC vendors.

TTMs adds **usable TMs and TRs** to Cobblemon. Right-click one onto your own Pokémon to add a move to its benched moves. TMs are unlimited-use by default; TRs are consumed on a successful teach. Both come from a paginated shop, an NPC vendor, or `/ttms give`.

|           |                                                                                                        |
| --------- | ------------------------------------------------------------------------------------------------------ |
| Minecraft | 1.21.1 (Fabric)                                                                                        |
| Requires  | Cobblemon, Fabric API, Fabric Language Kotlin                                                          |
| Optional  | Impactor or Pebbles Economy (for currency other than a vanilla item), LuckPerms (for permission nodes) |

***

## Features

* **Unlimited-use TMs and consumable TRs**, taught by right-clicking onto your Pokémon
* **Coexists with Cobblemon's own official TM** - a deliberately different product, not a replacement
* **Convert to/from the official Cobblemon TM**, opt-in per kind through config
* **Paginated, type-filterable shop**, opened via command or an NPC vendor
* **Any entity can become a shop vendor**, tagged in-game with no coding required
* **312 preloaded moves per catalog** (TMs and TRs), fully editable pricing including a "not for sale" option
* **Fully configurable item appearance** per elemental type via custom model data, for TMs and TRs separately
* **Works with Impactor, Pebbles Economy, or a plain item** as currency
* **LuckPerms permission nodes** per command, with sensible operator defaults

***

## How it works

A TM or TR is an ordinary item - a base item carrying custom model data and a tag recording its move, catalog number, and whether it's a TM or a TR. Right-clicking one onto a Pokémon **you own** adds the move to its **benched moves**, provided the species can learn it and doesn't already know it.

{% hint style="info" %}
Moves are added to **benched moves**, not the active moveset - the same place Cobblemon puts moves it can't fit onto a full moveset. Swap them in from there.
{% endhint %}

Cobblemon ships its own official TM item, which is unlock-based and single-use. TTMs' TMs and TRs are a deliberately different product - unlimited-use TMs and consumable TRs - so the two coexist rather than one replacing the other. Right-clicking a TTMs item in the air, or running `/ttms convert`, converts it to the official Cobblemon TM instead, for owners who'd rather move over. This is opt-in per kind through config.

### The shop

Opened with `/ttms shop <tm|tr>`, or by right-clicking an NPC an admin has tagged with `/ttms npc`. It opens on an **elemental-type picker** - pick a type, or "All Types", to get a paginated, sortable list of that shop's moves priced in the configured currency. Selecting one opens a confirmation screen before anything is charged.

{% hint style="success" %}
A failed purchase - not enough funds, no inventory space - leaves the confirmation screen open with the error visible, instead of silently dropping you back to the move list.
{% endhint %}

### Shop NPCs

Any entity can be turned into a shop vendor. Run `/ttms npc <tm|tr>` to enter tagging mode, then click an entity to tag it - it becomes invulnerable and keeps its name visible so it reads clearly as a vendor. Click it again (still in tagging mode) to untag it, which restores whatever `invulnerable`/`name visible` state it had **before** it was tagged. Click a tagged NPC while *not* in tagging mode to open its shop.

***

## Commands

All commands live under `/ttms`.

| Command                                       | What it does                                                                                        | Default Permission |
| --------------------------------------------- | --------------------------------------------------------------------------------------------------- | ------------------ |
| `/ttms shop <tm\|tr>`                         | Opens your own shop.                                                                                | see below          |
| `/ttms shop <tm\|tr> <player>`                | Opens another player's shop.                                                                        | operators          |
| `/ttms npc <tm\|tr>`                          | Toggles tagging mode: the next entity you click becomes a shop NPC of this kind.                    | operators          |
| `/ttms give <player> <tm\|tr> <move\|random>` | Gives a TM/TR directly, bypassing the shop and its price.                                           | operators          |
| `/ttms convert`                               | Converts every TTMs TM/TR in your inventory to the official Cobblemon TM, per the migration config. | everyone           |
| `/ttms reload`                                | Re-reads every config file, including tagged NPCs, and reports how many issues it found.            | operators          |

***

## Permissions

TTMs uses [LuckPerms' Fabric permissions API](https://github.com/lucko/fabric-permissions-api).

| Node                | Grants                         | Default   |
| ------------------- | ------------------------------ | --------- |
| `ttms.shop.self.tm` | `/ttms shop tm`                | -         |
| `ttms.shop.self.tr` | `/ttms shop tr`                | -         |
| `ttms.shop.other`   | `/ttms shop <tm\|tr> <player>` | operators |
| `ttms.admin.npc`    | `/ttms npc`                    | operators |
| `ttms.admin.give`   | `/ttms give`                   | operators |
| `ttms.admin.reload` | `/ttms reload`                 | operators |
| `ttms.convert`      | `/ttms convert`                | everyone  |

{% hint style="info" %}
Operators (permission level 2) always pass regardless of node. Grant `ttms.shop.self.tm` / `.tr` to let regular players buy TMs or TRs for themselves without any other admin access.
{% endhint %}

***

## Configuration

Config files live in `config/TTMs/` and are created on first run:

| File                    | Purpose                                        |
| ----------------------- | ---------------------------------------------- |
| `config.json`           | Economy, item behaviour, migration, GUI icons. |
| `lang.json`             | Every player-facing message.                   |
| `tms.json` / `trs.json` | The two shop catalogs - move, number, price.   |
| `items.json`            | Per-type item appearance for TMs and TRs.      |
| `npcs.json`             | Tagged shop NPCs.                              |

Edit a file and run `/ttms reload`, or restart the server, to apply changes.

### `economy`

| Key        | Default              | Description                                                                                                                                                              |
| ---------- | -------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `currency` | `"impactor:dollars"` | `"pebbles"` (case-insensitive) → Pebbles Economy. A registered Impactor currency key → Impactor. Anything else is parsed as a vanilla item id and used as item currency. |

### `items`

| Key           | Default | Description                                 |
| ------------- | ------- | ------------------------------------------- |
| `reusableTms` | `true`  | A TM is not consumed when a move is taught. |
| `reusableTrs` | `false` | A TR is not consumed when a move is taught. |

### `migration`

| Key               | Default | Description                                                                                                                                                                                        |
| ----------------- | ------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `migrateTms`      | `true`  | Converting a TM to the official Cobblemon TM (right-click in air, or `/ttms convert`) is enabled.                                                                                                  |
| `migrateTrs`      | `true`  | Same, for TRs.                                                                                                                                                                                     |
| `migrateTrsToTms` | `true`  | `true` → a converted TR becomes a plain official TM. `false` → it keeps its own identity as a "Technical Record", still teachable, still a TTMs item, just built from the official TM's move data. |

### `gui.icons`

Every GUI element - the background/filler panes and every button - is an `{item, customModelData}` pair.

```json
{
  "gui": {
    "icons": {
      "border": { "item": "minecraft:black_stained_glass_pane", "customModelData": null },
      "filler": { "item": "minecraft:gray_stained_glass_pane", "customModelData": null },
      "previousPage": { "item": "minecraft:player_head", "customModelData": null },
      "nextPage": { "item": "minecraft:player_head", "customModelData": null },
      "back": { "item": "minecraft:player_head", "customModelData": null },
      "confirm": { "item": "minecraft:player_head", "customModelData": null },
      "cancel": { "item": "minecraft:player_head", "customModelData": null },
      "sort": { "item": "minecraft:player_head", "customModelData": null },
      "allTypes": { "item": "minecraft:nether_star", "customModelData": null }
    }
  }
}
```

| Key                         | Description                                                                                   |
| --------------------------- | --------------------------------------------------------------------------------------------- |
| `border`                    | Fills every otherwise-empty slot in every screen.                                             |
| `filler`                    | Fills the leftover item slots on a move-list page that has fewer entries than the page holds. |
| `previousPage` / `nextPage` | Pagination arrows on the move list.                                                           |
| `back`                      | Returns to the previous screen.                                                               |
| `confirm` / `cancel`        | The purchase confirmation buttons.                                                            |
| `sort`                      | Toggles sorting the move list by name or by number.                                           |
| `allTypes`                  | Opens the unfiltered, all-types move list from the type picker.                               |

Leave `customModelData` as `null` to just use the plain item. Set it once you have a resource pack with a matching model - the same way `items.json` drives the TM/TR item appearance below.

{% hint style="success" %}
The six buttons that default to `minecraft:player_head` (`previousPage`, `nextPage`, `back`, `confirm`, `cancel`, `sort`) ship with a built-in skin texture, so a fresh install has working icons with **no resource pack and no config needed**. That texture only applies while the icon stays a plain player head with no `customModelData` set - pick a different `item`, or set `customModelData`, and your value takes over completely.
{% endhint %}

Type-picker icons aren't separately configurable - they're built from the matching `items.json` entry, so reskinning a type's TM/TR appearance also reskins its picker button.

***

## Catalogs - `tms.json` / `trs.json`

```json
{
  "moves": [
    { "move": "fireblast", "number": 1, "price": 8000 },
    { "move": "hiddenpower", "number": 82, "price": -1 }
  ]
}
```

| Field    | Description                                                                                                               |
| -------- | ------------------------------------------------------------------------------------------------------------------------- |
| `move`   | A Cobblemon move id, lowercase, no spaces.                                                                                |
| `number` | Shown in the item name (`TM1: Fire Blast`). Doesn't need to be contiguous or unique across the two files.                 |
| `price`  | Shop price in the configured currency. `-1` means "not sold" - hidden from the shop, still giveable through `/ttms give`. |

Ships with 312 entries per file by default. A blank or duplicate move name is dropped at load with a logged warning. A move Cobblemon no longer recognises is skipped when building a shop page instead of breaking it.

***

## Item appearance - `items.json`

```json
{
  "tm": {
    "fire": { "baseItem": "minecraft:popped_chorus_fruit", "customModelData": 2520 }
  },
  "tr": {
    "fire": { "baseItem": "minecraft:popped_chorus_fruit", "customModelData": 3520 }
  }
}
```

One entry per elemental type (plus `unknown`, for a move Cobblemon doesn't tag with a type), per kind. TTMs ships no models, textures or item JSONs - `customModelData` only means something to a resource pack you provide. A type missing from the file falls back to its `unknown` entry.

{% hint style="info" %}
TMs and TRs get **separate** custom-model-data ranges by default, so they look different out of the box - TR values mirror the TM scheme under a different numeric prefix.
{% endhint %}

***

## Shop NPCs - `npcs.json`

```json
{
  "3f2d1a90-1234-4a2b-8f3c-9e1c8e1c8e1c": { "kind": "tm", "wasInvulnerable": false, "wasCustomNameVisible": false }
}
```

Written automatically by `/ttms npc`. `wasInvulnerable` / `wasCustomNameVisible` are the entity's flags from just before it was tagged, restored when it's untagged. Not meant to be hand-edited.

***

## Messages

Every player-facing message is in `lang.json` and uses [MiniMessage](https://docs.advntr.dev/minimessage/format.html) formatting - legacy `&` and `§` colour codes work too. Delete a key to fall back to its default.

| Key                                                                                | Placeholders         |
| ---------------------------------------------------------------------------------- | -------------------- |
| `error.insufficient_funds`                                                         | `{move_category}`    |
| `error.insufficient_inventory_space`                                               |                      |
| `error.already_learned`                                                            | `{pokemon}` `{move}` |
| `error.cannot_learn`                                                               | `{pokemon}` `{move}` |
| `error.no_permission`                                                              |                      |
| `error.player_not_found`                                                           | `{player}`           |
| `error.invalid_type`                                                               | `{move_category}`    |
| `error.invalid_move`                                                               | `{move}`             |
| `success.learned`                                                                  | `{pokemon}` `{move}` |
| `success.purchased`                                                                | `{move}`             |
| `success.received_move`                                                            | `{move}`             |
| `success.gave_move`                                                                | `{player}` `{move}`  |
| `info.npc_mode_enabled` / `info.npc_mode_disabled`                                 | `{move_category}`    |
| `info.npc_added` / `info.npc_removed`                                              | `{move_category}`    |
| `command.reloaded`                                                                 |                      |
| `command.reload_issues`                                                            | `{count}`            |
| `command.converted`                                                                | `{count}`            |
| `title.tm_shop` / `title.tr_shop`                                                  |                      |
| `shop.confirm_title` / `shop.price_label` / `shop.all_types`                       |                      |
| `button.confirm` / `cancel` / `back` / `next` / `previous`                         |                      |
| `button.sort_by_name` / `sort_by_number` / `sorting_by_name` / `sorting_by_number` |                      |
| `item.tm_name_prefix` / `tr_name_prefix` / `technical_record`                      |                      |

***

## Requirements

* Minecraft 1.21.1 (Fabric)
* Cobblemon
* Fabric API + Fabric Language Kotlin
