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

# CobbleBosses

CobbleBosses adds **tiered boss Pokémon** to Cobblemon. Bosses appear from ordinary wild spawns, tower over everything around them, scale to the player who triggered them, glow in their tier's colour, and drop from a weighted loot table when beaten.

Everything about a tier is config - how many there are, what they're called, what colour they glow, how strong they are, which species they apply to, and what they drop.

***

## Features

* **Tiered boss Pokémon** spawning naturally from ordinary wild spawns, fully config-defined
* **Oversized, over-levelled, maxed-out bosses** - perfect IVs, heavy EV investment, a random ability from a configurable pool
* **Boss re-levels to match your party** the moment the battle starts, so it stays a challenge whether you fight early or come back later
* **Weighted per-tier loot tables** with guaranteed drops and combined economy payouts, delivered straight to your inventory
* **Pinned custom groups** - tie a tier to specific species and aspects for a Megas group, a starters group, or anything else
* **Discord webhook announcements** on spawn and/or defeat, independent of in-game broadcasts
* **Tier-coloured glow and particle aura** so a boss is visible from a distance
* **Blockable player moves**, to stop one-hit-KO moves from trivialising boss fights
* **Bosses aren't saved with the world**, so a crash never leaves one stranded

***

## How bosses work

Every few minutes the server rolls each online player against a spawn chance. A player who wins the roll gets a boss on their **next natural spawn**: the wild Pokémon appears as normal, and a boss is built alongside it from the same species and form.

Bosses are:

* **Oversized** - scaled up by their tier's size modifier, so you can see one coming
* **Over-levelled** - either a fixed level or scaled to your strongest party member, plus the tier's bonus
* **Maxed out** - random perfect IVs and heavy EV investment
* **Uncatchable** - no throwing a ball at them
* **Untouchable outside battle** - immune to non-battle damage, and they can't be ridden
* **Given a random ability** from a configurable pool

{% hint style="info" %}
Bosses are **not saved with the world**. They disappear on restart by design, so a server that crashes mid-fight doesn't come back with a stranded boss in the middle of a field.
{% endhint %}

### Tiers

A tier decides everything about how a boss looks and behaves. The four defaults are Common, Uncommon, Rare and Legendary, but that's just the shipped config - you can add a fifth, delete one, or rename them all.

| Tier property       | What it controls                                                   |
| ------------------- | ------------------------------------------------------------------ |
| **Weight**          | How often this tier is picked. `0` makes it command-only.          |
| **Colour**          | Name colour, glow outline, particle aura and Discord embed colour. |
| **Base/over level** | How strong the boss is, absolute or scaled to the player.          |
| **Size**            | How large it is.                                                   |
| **Drop rolls**      | How many times its loot table is rolled on defeat.                 |
| **Shiny**           | Whether the boss is always shiny.                                  |
| **Last evolution**  | Spawn the species' final evolution instead of what triggered it.   |
| **Species**         | Restrict the tier to specific species, for a custom group.         |
| **Aspects**         | Cobblemon aspects forced onto every boss of the tier.              |

### Levels

Each tier has a **base level** and an **over level**.

* Base level `0` means "scale to the player" - the boss takes the level of the triggering player's strongest Pokémon, plus the over level. A player with a level 60 team facing a tier with `overLevel: 20` gets a level 80 boss.
* Any other base level is absolute and ignores the player entirely.

Boss levels intentionally go **past Cobblemon's level cap**.

### Battling a boss

Walk up and battle it like any wild Pokémon. When it starts, the boss re-levels to match your current party, so it stays a challenge whether you show up early or come back later.

You can block specific moves from boss battles - one-hit KO moves, for instance. A player carrying a blocked move is told which move and on which Pokémon, and the battle doesn't start.

Win and your rewards go **straight into your inventory**, then a summary screen opens showing what you got. Lose or flee and the boss heals back to full, ready for the next attempt.

{% hint style="success" %}
Rewards are granted **before** the summary screen appears. Closing it, logging out, or crashing can't cost you your drop.
{% endhint %}

***

## Commands

All commands live under `/cbosses` (alias: `/cobblebosses`).

| Command                              | What it does                                                    | Default Permission |
| ------------------------------------ | --------------------------------------------------------------- | ------------------ |
| `/cbosses spawn <tier> <properties>` | Spawns a boss at your feet. Tier ids are suggested as you type. | operators          |
| `/cbosses clear <range>`             | Removes every boss within `range` blocks.                       | operators          |
| `/cbosses list`                      | Lists active bosses with tier, dimension and coordinates.       | operators          |
| `/cbosses reload`                    | Reloads every config file and reports any problems found.       | operators          |

The `<properties>` argument takes Cobblemon properties, so `/cbosses spawn legendary charizard` or `/cbosses spawn rare pikachu shiny=yes` both work.

***

## Permissions

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

| Node                   | Grants            | Default   |
| ---------------------- | ----------------- | --------- |
| `cbosses.admin`        | Every subcommand. | operators |
| `cbosses.admin.spawn`  | `/cbosses spawn`  | operators |
| `cbosses.admin.clear`  | `/cbosses clear`  | operators |
| `cbosses.admin.list`   | `/cbosses list`   | operators |
| `cbosses.admin.reload` | `/cbosses reload` | operators |

{% 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.
{% endhint %}

***

## Configuration

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

| File          | Purpose                                         |
| ------------- | ----------------------------------------------- |
| `config.json` | Spawning, broadcasts, Discord, battle, display. |
| `tiers.json`  | The boss tiers themselves.                      |
| `lang.json`   | Every player-facing message.                    |
| `loot/`       | One loot table per tier.                        |

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

{% hint style="success" %}
Configs from older versions load fine and are rewritten in the current format automatically - nothing needs migrating by hand.
{% endhint %}

### `spawns`

| Key                | Default     | Description                                                 |
| ------------------ | ----------- | ----------------------------------------------------------- |
| `intervalMinutes`  | `10`        | Minutes between roll passes.                                |
| `chancePerPlayer`  | `0.005`     | Chance for each online player, per pass.                    |
| `customAspect`     | `""`        | A Cobblemon aspect applied to every boss, e.g. `shiny=yes`. |
| `speciesBlacklist` | `["unown"]` | Species that never become bosses.                           |

{% hint style="info" %}
**Blacklisting a species.** Add its name to `speciesBlacklist` and it is never turned into a boss, whatever the tier weights say. The check runs before a tier is picked, and names are matched case-insensitively.
{% endhint %}

### `broadcast`

| Key          | Default    | Description                                    |
| ------------ | ---------- | ---------------------------------------------- |
| `onSpawn`    | `false`    | Announce boss spawns in chat.                  |
| `onWin`      | `false`    | Announce boss defeats in chat.                 |
| `mode`       | `"global"` | `global`, `local` or `private`.                |
| `localRange` | `1000.0`   | Radius in blocks, used when `mode` is `local`. |

`private` messages only the player involved; `local` messages everyone within `localRange`; `global` messages the whole server.

### `discord`

| Key          | Default | Description                              |
| ------------ | ------- | ---------------------------------------- |
| `webhookUrl` | `""`    | Leave empty to disable Discord entirely. |
| `onSpawn`    | `true`  | Post an embed when a boss spawns.        |
| `onWin`      | `false` | Post an embed when a boss is defeated.   |

Embeds use the tier's colour and show the Pokémon's sprite. Webhooks are sent in the background, so a slow or unreachable Discord never affects the server.

{% hint style="warning" %}
Discord settings are **independent of `broadcast`**. Turning chat broadcasts off no longer silently disables Discord posts - set `discord.onSpawn` / `discord.onWin` to `false` if you don't want them.
{% endhint %}

### `battle`

| Key                         | Default                                                  | Description                                          |
| --------------------------- | -------------------------------------------------------- | ---------------------------------------------------- |
| `abilities`                 | `["unaware", "magicbounce", "magicguard", "goodasgold"]` | One is picked at random for each boss.               |
| `blockedPlayerMoves`        | `[]`                                                     | A player carrying one of these can't start a battle. |
| `invulnerableOutsideBattle` | `true`                                                   | Bosses ignore all damage outside of battle.          |

### `display`

| Key                     | Default | Description                                   |
| ----------------------- | ------- | --------------------------------------------- |
| `particles`             | `true`  | Aura particles in the tier's colour.          |
| `particleIntervalTicks` | `5`     | How often they're emitted. Higher is cheaper. |
| `glow`                  | `true`  | Outline in the tier's colour.                 |

### `rewards`

| Key                    | Default         | Description                                                               |
| ---------------------- | --------------- | ------------------------------------------------------------------------- |
| `economyCommandPrefix` | `"eco deposit"` | Loot commands starting with this are summed per currency and issued once. |

***

## Tiers

`tiers.json` is a list, so you can have as many tiers as you like.

```json
{
  "tiers": [
    {
      "id": "common",
      "displayName": "Common",
      "weight": 50.0,
      "colour": "#00BEBE",
      "baseLevel": 0,
      "overLevel": 20,
      "sizeModifier": 5.0,
      "dropRolls": 2,
      "shiny": true,
      "useLastEvolution": true,
      "species": [],
      "aspects": []
    }
  ]
}
```

| Field              | Description                                                                         |
| ------------------ | ----------------------------------------------------------------------------------- |
| `id`               | Lowercase identifier. Matches the loot filename and the `/cbosses spawn` argument.  |
| `displayName`      | Shown in names and broadcasts.                                                      |
| `weight`           | Relative spawn weight. `0` means the tier only appears via command.                 |
| `colour`           | Hex colour for the name, glow, particles and Discord embed.                         |
| `baseLevel`        | `0` scales to the triggering player's strongest Pokémon. Anything else is absolute. |
| `overLevel`        | Added on top of the base level.                                                     |
| `sizeModifier`     | Entity scale.                                                                       |
| `dropRolls`        | How many times the loot table is rolled on defeat.                                  |
| `shiny`            | Force the boss shiny.                                                               |
| `useLastEvolution` | Spawn the species' final evolution instead of what triggered the spawn.             |
| `species`          | Restrict the tier to these species. Empty means it applies to everything.           |
| `aspects`          | Cobblemon aspects forced onto the boss, e.g. `["mega"]`.                            |

{% hint style="info" %}
Bad values are corrected and logged rather than crashing the server - a negative weight becomes `0`, an unreadable colour becomes white, and a duplicate id is dropped. `/cbosses reload` tells you how many problems it found, and the details go to the server log.
{% endhint %}

### Custom groups

A tier with a non-empty `species` list becomes a **pinned** tier: it only ever applies to those species, and those species always get it in preference to the general pool. Add `aspects` and you have a custom group with its own look, difficulty and loot table - a Megas group, a starters group, a pseudo-legendary group.

```json
{
  "id": "mega",
  "displayName": "Mega",
  "weight": 100.0,
  "colour": "#FF00AA",
  "baseLevel": 0,
  "overLevel": 100,
  "sizeModifier": 6.0,
  "dropRolls": 3,
  "shiny": false,
  "useLastEvolution": true,
  "species": ["charizard", "venusaur", "blastoise"],
  "aspects": ["mega"]
}
```

With that in place, a wild Charizard picked for a boss spawn **always** becomes a Mega boss and draws from `loot/mega.json`. Nothing outside the list can roll the tier, and Charizard can no longer roll Common, Rare or anything else.

How a tier is chosen, in order:

1. Pinned tiers listing the spawning species. If any has a weight above `0`, one is drawn from **only those**.
2. Otherwise the general pool - every tier with an empty `species` list - exactly as before.

A pinned tier is not merely *more likely* for its species; it is the only thing that species can roll. Setting its `weight` to `0` skips step 1, so the species falls back to the general pool and the group becomes command-only.

{% hint style="warning" %}
**The pin matches the species that spawned, not the finished boss.** The tier is chosen from the wild Pokémon that triggered the spawn, *before* `useLastEvolution` is applied. If wild Charmanders are what actually spawn on your server and you rely on `useLastEvolution` to grow them into Charizards, pin `charmander` - or list the whole evolution line.
{% endhint %}

{% hint style="info" %}
**Aspects are not validated.** Whatever strings you list are forced onto the boss, and Cobblemon re-derives the Pokémon's form from them. Base Cobblemon does not implement Mega Evolution - the `mega` aspect comes from an addon, so check the exact name that addon registers. An aspect that does not exist is ignored silently, leaving an ordinary boss wearing the group's colour and loot table.

`/cbosses spawn mega pikachu` still works: the command looks the tier up by id and skips the species check, which keeps a new group easy to test.
{% endhint %}

***

## Loot

Each tier reads `loot/<tier id>.json` - so the `legendary` tier uses `loot/legendary.json`. Filenames are matched case-insensitively. A tier with no file simply drops nothing.

```json
[
  {
    "type": "item",
    "identifier": "minecraft:diamond",
    "name": "<gradient:#00BEBE:#FFFFFF>Boss Shard</gradient>",
    "customModelData": -1,
    "quantityMin": 1,
    "quantityMax": 3,
    "weight": 10
  },
  {
    "type": "command",
    "identifier": "minecraft:gold_ingot",
    "value": ["eco deposit 500 dollars {player}"],
    "name": "500 Dollars",
    "weight": 2
  }
]
```

| Field                         | Description                                                           |
| ----------------------------- | --------------------------------------------------------------------- |
| `type`                        | `item` or `command`.                                                  |
| `identifier`                  | Item id. For `command` entries, the icon shown on the summary screen. |
| `value`                       | Commands to run for `command` entries. `{player}` becomes the winner. |
| `name`                        | Display name. MiniMessage formatting is supported.                    |
| `customModelData`             | `-1` disables.                                                        |
| `quantityMin` / `quantityMax` | Inclusive range, rolled per drop.                                     |
| `weight`                      | Relative weight within the tier.                                      |
| `guaranteed`                  | `true` grants the entry on every kill, outside the weighted rolls.    |

The tier's `dropRolls` decides how many entries are rolled. Weights are relative within the file, so an entry with weight `10` alongside one with weight `2` comes up roughly five times as often.

### Guaranteed rewards

An entry marked `"guaranteed": true` is handed out on **every** kill of that tier, exactly once, and is left out of the weighted draw entirely. It never competes with your rollable loot, and its `weight` is ignored - you can leave it at `0`.

This is the clean way to pay a flat bounty on top of whatever the rolls produce:

```json
[
  {
    "type": "command",
    "identifier": "minecraft:gold_ingot",
    "value": ["eco deposit 1000 dollars {player}"],
    "name": "Boss Bounty",
    "guaranteed": true
  },
  {
    "type": "item",
    "identifier": "minecraft:diamond",
    "name": "Common Drop",
    "weight": 9
  },
  {
    "type": "item",
    "identifier": "minecraft:netherite_ingot",
    "name": "Rare Drop",
    "weight": 1
  }
]
```

Every kill of this tier pays 1000 dollars, then rolls the diamond/netherite pool `dropRolls` times on top. Guaranteed entries can be items as well as commands, and you can have as many as you like.

### Economy rewards

Commands beginning with `rewards.economyCommandPrefix` are treated as currency payouts: they are summed per currency across the whole roll and issued as a single command. Three separate `eco deposit 500 dollars` rolls become one `eco deposit 1500 dollars` - one transaction instead of three.

{% hint style="info" %}
Entries are checked when the file loads. An entry with a non-positive weight, an empty command list, or an unrecognised item id is skipped and the reason written to the server log - so a typo costs you one drop, not a crash mid-fight. Guaranteed entries are exempt from the weight check.
{% endhint %}

***

## 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                                |
| -------------------------- | ---------------------------------------------- |
| `broadcast.spawn`          | A boss spawns.                                 |
| `broadcast.win`            | A boss is defeated.                            |
| `battle.move_blacklisted`  | A player tries to battle with a blocked move.  |
| `rewards.gui_title`        | Title of the rewards summary screen.           |
| `command.reloaded`         | `/cbosses reload` succeeded.                   |
| `command.reload_issues`    | Reload finished with config problems.          |
| `command.bosses_cleared`   | `/cbosses clear` finished.                     |
| `command.boss_spawned`     | `/cbosses spawn` succeeded.                    |
| `command.unknown_tier`     | An unknown tier was given to `/cbosses spawn`. |
| `command.no_bosses`        | `/cbosses list` with nothing active.           |
| `command.boss_list_header` | Header of the `/cbosses list` output.          |
| `command.boss_list_entry`  | One line per boss in `/cbosses list`.          |

Available tokens, depending on the message: `{boss}`, `{biome}`, `{player}`, `{x}`, `{y}`, `{z}`, `{move}`, `{pokemon}`, `{tier}`, `{tiers}`, `{count}`, `{dimension}`.

***

## Requirements

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