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

# TreasureTrails

OSRS-style clue scrolls for Cobblemon on Fabric.

TreasureTrails adds **OSRS-style clue scrolls** to Cobblemon. Players complete a chain of steps - mine a block, catch a fish, defeat a Pokémon, evolve one, breed animals, and more - to open a reward casket.

Every tier, step and reward is config - how many tiers there are, what steps exist, what triggers them, and what they pay out.

***

## Features

* **Chained clue scrolls** - each scroll steps through a random sequence of tasks from its tier before turning into a reward casket
* **Eleven action types**, from mining and fishing to defeating or evolving a Pokémon, each config-driven with optional biome, gear, value and coordinate requirements
* **Reward caskets** that open a GUI and hand out weighted item and command rewards
* **Event / custom clue scrolls** with a fixed step order and their own reward pool, for time-limited content
* **Passive scroll drops** from mob kills, wild Pokémon kills and block breaking, with independent chances and a shared per-player cooldown
* **Per-player completion stats**, viewable by every player for themselves
* **Fully config-driven content** - tiers, steps and event scrolls all live in JSON and hot-reload with `/ttrails reload`

***

## How clue scrolls work

A clue scroll starts on a random step from its tier. Performing the right in-game action while holding the scroll - with any biome, gear, value or coordinate requirement the step has satisfied - advances it to another random step of the same tier. Once every step is done, the scroll turns into a **reward casket**.

Opening a reward casket opens a GUI and hands out its rewards - a mix of items and commands, weighted and rolled per the tier's `minRewards`/`maxRewards` range.

### Steps

Each step declares an **action type** and, optionally, a biome, a value, required gear, and exact coordinates. Anything left empty matches everything, so the same action type covers both "do X" and "do X specifically with/at Y":

| Action            | Triggered by                                 | What `value` means                      |
| ----------------- | -------------------------------------------- | --------------------------------------- |
| `MINE`            | Breaking a block                             | The block's item id                     |
| `BUILD`           | Right-clicking a block while holding an item | The item id in hand                     |
| `KILL`            | Killing an entity                            | The entity type id                      |
| `KILL_POKEMON`    | Winning a wild Pokémon battle                | The fainted Pokémon's type, e.g. `fire` |
| `EVOLVE_POKEMON`  | A Pokémon evolving                           | The evolved species' name               |
| `INTERACT_ENTITY` | Right-clicking an entity                     | The entity type id                      |
| `INTERACT_NPC`    | Right-clicking a named entity                | The entity's custom name                |
| `FISH`            | Catching a fish                              | The caught item id                      |
| `BREED`           | Breeding two animals                         | The animal's entity type id             |
| `CRAFT`           | Crafting an item                             | The crafted item id                     |
| `SMELT`           | Taking a smelted item out of a furnace       | The smelted item id                     |

Every action also carries the biome and coordinates it happened at, and the gear the player was wearing/holding at the time, so a step can layer any combination of requirements on top of the action itself.

{% hint style="warning" %}
Adding a new action type means hooking a new Minecraft/Fabric/Cobblemon event in code - the trigger mechanism itself isn't something config alone can extend.
{% endhint %}

### Event / custom clue scrolls

Files under `ClueScrolls/` define scrolls with a **fixed** step order instead of a random one, and their own reward pool independent of the tier's. They're handed out with `/ttrails event <id>` - useful for holiday events or one-off quests. A scroll referencing an event that no longer exists tells the player it's unavailable instead of failing silently.

### Passive drops

Scrolls can also drop on their own - on a mob kill, a wild Pokémon kill, or breaking a block - each with its own chance, subject to a shared per-player cooldown so drops can't stack up. Block-break drops are further restricted to a configurable set of tiers, so rarer tiers stay scroll-only or event-only if you want.

***

## Commands

All commands live under `/ttrails` (alias: `/treasuretrails`).

| Command                | What it does                                                                                    | Default Permission |
| ---------------------- | ----------------------------------------------------------------------------------------------- | ------------------ |
| `/ttrails reload`      | Re-reads config, tiers, steps, lang and event scrolls.                                          | operators          |
| `/ttrails clue <tier>` | Gives yourself a random-step clue scroll of the given tier. Tier ids are suggested as you type. | operators          |
| `/ttrails event <id>`  | Gives yourself the named event/custom clue scroll. Ids are suggested as you type.               | operators          |
| `/ttrails reroll`      | Rerolls the current step of the clue scroll in your hand. Random-step scrolls only.             | operators          |
| `/ttrails complete`    | Instantly completes the clue scroll in your hand.                                               | operators          |
| `/ttrails stats`       | Shows your own completed-clue counts, per tier and total.                                       | everyone           |

***

## Permissions

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

| Node                     | Grants                  | Default   |
| ------------------------ | ----------------------- | --------- |
| `ttrails.admin`          | Every admin subcommand. | operators |
| `ttrails.admin.reload`   | `/ttrails reload`       | operators |
| `ttrails.admin.clue`     | `/ttrails clue`         | operators |
| `ttrails.admin.event`    | `/ttrails event`        | operators |
| `ttrails.admin.reroll`   | `/ttrails reroll`       | operators |
| `ttrails.admin.complete` | `/ttrails complete`     | operators |
| `ttrails.stats`          | `/ttrails stats`        | everyone  |

{% hint style="info" %}
Granting one of the finer nodes lets a staff member use just that command without being opped. Operators (permission level 2) always pass regardless of node. `stats` always shows only the caller's own progress, whoever runs it.
{% endhint %}

***

## Configuration

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

| File                 | Purpose                                   |
| -------------------- | ----------------------------------------- |
| `config.json`        | Debug logging and passive drop settings.  |
| `data/tiers.json`    | The clue tiers themselves.                |
| `data/steps.json`    | Every step a random scroll can pick from. |
| `ClueScrolls/*.json` | Event/custom scrolls, one file each.      |
| `lang.json`          | Every player-facing message.              |

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

### `config.json`

```json
{
  "debug": false,
  "drops": {
    "enabled": true,
    "cooldownSeconds": 300,
    "mobKillChance": 0.02,
    "pokemonKillChance": 0.03,
    "blockBreakChance": 0.001,
    "blockBreakTiers": ["easy", "medium"]
  }
}
```

| Key                       | Default              | Description                                                                                                     |
| ------------------------- | -------------------- | --------------------------------------------------------------------------------------------------------------- |
| `debug`                   | `false`              | Broadcasts a per-step match/mismatch breakdown to the server as players act. Noisy - leave off outside testing. |
| `drops.enabled`           | `true`               | Master switch for passive scroll drops.                                                                         |
| `drops.cooldownSeconds`   | `300`                | Minimum time between passive drops for a player, shared across all drop sources.                                |
| `drops.mobKillChance`     | `0.02`               | Roll chance on any mob kill.                                                                                    |
| `drops.pokemonKillChance` | `0.03`               | Roll chance on winning a wild Pokémon battle.                                                                   |
| `drops.blockBreakChance`  | `0.001`              | Roll chance on breaking a block.                                                                                |
| `drops.blockBreakTiers`   | `["easy", "medium"]` | Tiers eligible to drop from block breaking.                                                                     |

{% hint style="info" %}
A dropped scroll's tier is chosen from the eligible tiers, weighted by each tier's `dropWeight` - the same weighting used to pick a random step for a scroll already in progress.
{% endhint %}

### `tiers.json`

A list, so you can have as many tiers as you like.

```json
{
  "tiers": [
    {
      "tier": "easy",
      "scrollDisplayName": "<yellow>Easy Clue Scroll",
      "scrollItem": "minecraft:paper",
      "scrollCustomModelData": -1,
      "casketDisplayName": "<yellow>Easy Clue Casket",
      "casketItem": "minecraft:chest",
      "casketCustomModelData": -1,
      "minSteps": 1,
      "maxSteps": 2,
      "minRewards": 1,
      "maxRewards": 2,
      "dropWeight": 100,
      "rewards": []
    }
  ]
}
```

| Field                                             | Description                                                                                              |
| ------------------------------------------------- | -------------------------------------------------------------------------------------------------------- |
| `tier`                                            | Lowercase identifier. Matches `steps.json` entries and the `/ttrails clue` argument.                     |
| `scrollDisplayName` / `casketDisplayName`         | MiniMessage-formatted display names.                                                                     |
| `scrollItem` / `casketItem`                       | Base item id for the scroll / reward casket.                                                             |
| `scrollCustomModelData` / `casketCustomModelData` | `-1` disables.                                                                                           |
| `minSteps` / `maxSteps`                           | How many random steps a scroll of this tier chains through.                                              |
| `minRewards` / `maxRewards`                       | How many reward rolls a casket of this tier grants.                                                      |
| `dropWeight`                                      | Relative weight when a tier needs to be picked at random - passive drops and step selection both use it. |
| `rewards`                                         | This tier's reward pool - see reward objects below.                                                      |

{% hint style="warning" %}
A tier with no matching entries in `steps.json` is logged as an orphan on load - check for a typo in either file if you see that warning.
{% endhint %}

### `steps.json`

```json
{
  "steps": [
    {
      "id": "mine_dirt",
      "description": "<white>Mine some dirt with a golden shovel.",
      "tier": "easy",
      "action": "MINE",
      "biome": "ANY",
      "value": "minecraft:dirt",
      "gear": ["minecraft:golden_shovel"]
    },
    {
      "id": "evolve_pokemon",
      "description": "<white>Evolve a Pokémon.",
      "tier": "easy",
      "action": "EVOLVE_POKEMON"
    },
    {
      "id": "dig_coordinates_1_2_3",
      "description": "<white>Dig at coordinates (1, 2, 3).",
      "tier": "easy",
      "action": "MINE",
      "coordinates": { "x": 1, "y": 2, "z": 3 }
    }
  ]
}
```

| Field         | Description                                                                             |
| ------------- | --------------------------------------------------------------------------------------- |
| `id`          | Unique identifier, referenced by event scrolls and used internally to advance a scroll. |
| `description` | MiniMessage-formatted, shown in the scroll's lore.                                      |
| `tier`        | Must match a `tier` in `tiers.json`.                                                    |
| `action`      | One of the action types above.                                                          |
| `biome`       | Biome id, or empty/`ANY` to match any biome.                                            |
| `value`       | Meaning depends on `action` - see the action type table above. Empty matches any value. |
| `gear`        | Item ids the player must be wearing or holding. Empty means no requirement.             |
| `coordinates` | Optional `{x, y, z}`. Requires the action within about a block of this point.           |

### `ClueScrolls/*.json`

One file per event/custom scroll.

```json
{
  "id": "example_scroll",
  "tier": "easy",
  "displayName": "<gold>Event Clue Scroll",
  "steps": ["mine_dirt", "kill_zombie_jungle", "fish_pufferfish", "place_beacon"],
  "rewards": [
    {
      "type": "ITEM",
      "identifier": "minecraft:dirt",
      "quantityMin": 1,
      "quantityMax": 1,
      "weight": 1
    }
  ]
}
```

`steps` is a fixed, ordered list of step ids from `steps.json` - unknown ids are dropped with a warning, and a scroll left with no valid steps is ignored entirely.

### Reward objects

Used in both `tiers.json` and `ClueScrolls/*.json`.

```json
{
  "type": "COMMAND",
  "identifier": "minecraft:dirt",
  "value": "give {player} minecraft:dirt 1",
  "name": "Special Item",
  "customModelData": -1,
  "quantityMin": 1,
  "quantityMax": 1,
  "weight": 1
}
```

| Field                         | Description                                                                             |
| ----------------------------- | --------------------------------------------------------------------------------------- |
| `type`                        | `ITEM` or `COMMAND`.                                                                    |
| `identifier`                  | Item id - the reward itself for `ITEM`, the icon shown in the casket GUI for `COMMAND`. |
| `value`                       | Command to run for `COMMAND` entries. `{player}` is replaced with the winner's name.    |
| `name`                        | Optional display name override. MiniMessage supported.                                  |
| `customModelData`             | `-1` disables.                                                                          |
| `quantityMin` / `quantityMax` | Inclusive range, rolled per reward.                                                     |
| `weight`                      | Relative weight within the reward pool.                                                 |

***

## Messages

Every message players see 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                            | When it's shown                                                                 |
| ------------------------------ | ------------------------------------------------------------------------------- |
| `step.current`                 | Shown in a scroll's lore alongside its next step.                               |
| `step.completed`               | A step is completed.                                                            |
| `scroll.completed`             | A random-step scroll finishes and turns into a casket.                          |
| `scroll.dropped`               | A passive scroll drop is received.                                              |
| `scroll.event_completed`       | An event/custom scroll finishes.                                                |
| `scroll.event_unavailable`     | A held event scroll's definition no longer exists.                              |
| `command.reloaded`             | `/ttrails reload` succeeded.                                                    |
| `command.invalid_tier`         | An unknown tier was given to `/ttrails clue`.                                   |
| `command.no_steps_for_tier`    | A tier has no steps configured for it.                                          |
| `command.gave_clue`            | `/ttrails clue` succeeded.                                                      |
| `command.invalid_event_scroll` | An unknown id was given to `/ttrails event`.                                    |
| `command.gave_event_scroll`    | `/ttrails event` succeeded.                                                     |
| `command.stats_header`         | Header of the `/ttrails stats` output.                                          |
| `command.stats_line`           | One line per tier in `/ttrails stats`.                                          |
| `command.stats_total`          | Final total line of `/ttrails stats`.                                           |
| `command.no_clue_scroll_held`  | `/ttrails reroll` or `/ttrails complete` was run without a clue scroll in hand. |
| `command.reroll_fixed_order`   | `/ttrails reroll` was run on an event/custom scroll.                            |
| `command.step_rerolled`        | `/ttrails reroll` succeeded.                                                    |

Available tokens, depending on the message: `{step}`, `{tier}`, `{scroll}`, `{count}`.

***

## Requirements

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