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

# PokeSell

Sell Pokémon from your PC for currency or items, with configurable pricing, decay, daily bounties and blacklists.

PokeSell lets players **sell Pokémon straight from their PC** for currency (or items), on Cobblemon Fabric servers. Price is driven by a configurable formula, decays the more a player sells until a daily reset, and a daily bounty species pays a bonus on top.

***

## Features

* **Sell straight from your PC** via NPC or an in-game GUI, for currency or items
* **Configurable pricing formula** with per-trait multipliers (shiny, legendary, mythical, ultra beast, hidden ability) and per-species overrides
* **Sale-count price decay** that drops the price the more a player sells, until a daily reset
* **Daily bounty species** paying a bonus multiplier, with a LuckPerms-based reroll allowance
* **Species and aspect blacklists**, so a blacklisted species or trait (e.g. shiny) is refused rather than sold for nothing
* **Bulk selection mode** to sell multiple Pokémon across boxes in one go
* **Auto-sell on release** toggle per player
* **Per-species sales leaderboard**, tracking normal and shiny sales separately
* **NPC-backed shops**, any entity can be flagged as a sell point
* **MongoDB or per-player JSON storage**
* **Works with Impactor, Pebbles Economy, or a plain item** as the payout currency

***

## How it works

Right-click an NPC (any entity flagged via `/pokesell npc`) or run `/pokesell open` to browse your PC. Clicking a Pokémon opens a confirmation screen; selling removes it from the PC and pays into the configured economy. Toggle **selection mode** to pick several Pokémon across boxes and sell them all at once - a selected Pokémon's icon turns fully green. `/pokesell release` toggles selling Pokémon automatically the moment they're released.

The price for a sale is: the formula's base price, times the decay factor (full price, or `decay.dropTo` once a player crosses `decay.salesUntilDrop` sales), times the daily bounty multiplier if the Pokémon matches today's bounty species - then clamped down to `pricing.maxPrice` if one is set. The same computation backs the lore, the "you will earn" preview and the actual deposit, so what a screen shows is always what gets paid, including partway through a bulk sale where later Pokémon in the same batch can already be priced at the dropped rate.

A blacklisted species, or a Pokémon carrying a blacklisted aspect (shiny, for instance), is refused rather than sold for nothing.

{% hint style="info" %}
**Party Pokémon are never sellable.** Only Pokémon sitting in the PC can be sold - this is intentional, not a bug.
{% endhint %}

### Sales stats

`/pokesell stats` opens a paginated leaderboard GUI of all-time sales, ranked by count sold - total sold, revenue and average price per species, with the current page shown in the title. Name a species instead - `/pokesell stats <species>`, tab-completed - to open a dedicated screen for that species alone, its own icon centered with the same stats.

Every species tracks **normal and shiny sales separately**. A handful of high-value shiny sales would otherwise skew that species' average price, so shiny revenue and average only show up as their own line once at least one shiny has been sold, never folded into the normal figures.

This is an admin tool (`pokesell.admin.stats`) - server-wide sales figures aren't something a player needs or should see.

***

## Commands

`/pokesell`

| Command                     | What it does                                                                                                                             | Permission                |
| --------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------- | ------------------------- |
| `/pokesell open`            | Opens the PC sell browser.                                                                                                               | `pokesell.player.gui`     |
| `/pokesell release`         | Toggles selling Pokémon automatically on release.                                                                                        | `pokesell.player.release` |
| `/pokesell stats [species]` | No argument opens the paginated sales leaderboard. With a species (tab-completed), opens a dedicated screen for that species' own stats. | `pokesell.admin.stats`    |
| `/pokesell npc`             | Toggles NPC-assignment mode; right-click an entity to flag/unflag it.                                                                    | `pokesell.admin.npc`      |
| `/pokesell reset`           | Clears every player's sale counts, bounty and rerolls.                                                                                   | `pokesell.admin.reset`    |
| `/pokesell reload`          | Reloads `config.json`/`lang.json`/`mongo_config.json`, rebuilds the daily bounty pool.                                                   | `pokesell.admin.reload`   |

***

## Permissions

Operator level 0 (everyone) or 2, per node below, when no permission plugin is installed. With LuckPerms or another `fabric-permissions-api` provider, nodes work as normal.

| Node                      | Default | Grants                                          |
| ------------------------- | ------- | ----------------------------------------------- |
| `pokesell.player.gui`     | op 0    | `/pokesell open`                                |
| `pokesell.player.release` | op 0    | `/pokesell release`                             |
| `pokesell.player.use`     | op 0    | Right-clicking a shop NPC                       |
| `pokesell.admin.stats`    | op 2    | `/pokesell stats [species]`                     |
| `pokesell.admin.npc`      | op 2    | `/pokesell npc`                                 |
| `pokesell.admin.reset`    | op 2    | `/pokesell reset`                               |
| `pokesell.admin.reload`   | op 2    | `/pokesell reload`                              |
| `pokesell.admin.alerts`   | op 2    | Receive the duplicate-sale alert                |
| `pokesell.rerolls.<n>`    | -       | Daily reroll allowance; highest valid node wins |

***

## Configuration

Config files live in `config/PokeSell/`. Every file is rewritten in canonical form on load, so missing keys are filled in and unknown ones dropped.

| File                | Purpose                                                                                                            |
| ------------------- | ------------------------------------------------------------------------------------------------------------------ |
| `config.json`       | Pricing, decay, daily bounty, reset, economy, NPC and security settings.                                           |
| `gui.json`          | Every non-Pokémon GUI item - empty slots, filler, navigation, confirm/cancel/back, selection and price-info heads. |
| `mongo_config.json` | Only read when `storage.useMongo` is `true`.                                                                       |
| `lang.json`         | Every player-facing message.                                                                                       |

{% hint style="success" %}
Upgrading from a 1.x install migrates the old config and per-player data automatically on first load - nothing needs moving by hand.
{% endhint %}

### `pricing`

```json
{
  "pricing": {
    "formula": "((catchRate / 100) * level + log(totalIVs)/2 + sqrt(totalEVs) - 1) * shinyMultiplier * legendaryMultiplier * mythicalMultiplier * ultraBeastMultiplier * hiddenAbilityMultiplier * customMultiplier",
    "multipliers": {
      "shiny": 10.0,
      "legendary": 10.0,
      "mythical": 10.0,
      "ultraBeast": 10.0,
      "hiddenAbility": 10.0
    },
    "speciesMultipliers": {},
    "blacklistedSpecies": [],
    "blacklistedAspects": [],
    "maxPrice": null
  }
}
```

| Key                  | Default     | Description                                                                                                                                                                          |
| -------------------- | ----------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `formula`            | see above   | An [exp4j](https://www.objecthunter.net/exp4j/) expression. Variables below. An invalid formula falls back to the default, logged once.                                              |
| `multipliers.*`      | `10.0` each | Bound to the matching formula variable when the Pokémon has that trait, `1.0` otherwise.                                                                                             |
| `speciesMultipliers` | `{}`        | `"pikachu": 2.5` - species id to `customMultiplier`, `1.0` if absent.                                                                                                                |
| `blacklistedSpecies` | `[]`        | Species ids. Refused, not sold for `0`.                                                                                                                                              |
| `blacklistedAspects` | `[]`        | Cobblemon aspect strings, e.g. `shiny`. Refused if the Pokémon has any of them, same as a blacklisted species - this is how you stop players bulk-selling their shinies by accident. |
| `maxPrice`           | `null`      | Uncapped when `null`. Otherwise a sale total over this value is clamped down to it, as the last step of the price computation.                                                       |

**Formula variables:** `catchRate`, `level`, `totalIVs`, `totalEVs` (both floored to `1` when their sum is `0`), `shinyMultiplier`, `legendaryMultiplier`, `mythicalMultiplier`, `ultraBeastMultiplier`, `hiddenAbilityMultiplier`, `customMultiplier`, `size`.

{% hint style="info" %}
**`size`** is the Pokémon's own random size-variance roll (`1.0` for a Pokémon with no size roll applied) - it's not folded into the default formula, so add it yourself if you want size to affect price.
{% endhint %}

### `decay`

| Key              | Default | Description                                                                   |
| ---------------- | ------- | ----------------------------------------------------------------------------- |
| `enabled`        | `true`  | Master switch. When off, sale counts aren't recorded either.                  |
| `perSpecies`     | `false` | `true` counts sales of that species; `false` counts the player's total sales. |
| `salesUntilDrop` | `150`   | Sales before the price drops.                                                 |
| `dropTo`         | `0.4`   | Multiplier once the threshold is reached - a step, not a gradient.            |

### `dailyBounty`

| Key                  | Default | Description                                                                                    |
| -------------------- | ------- | ---------------------------------------------------------------------------------------------- |
| `enabled`            | `true`  | Daily bounty feature switch.                                                                   |
| `multiplier`         | `5.0`   | Applied on top of decay when the sold species matches the bounty.                              |
| `rerolls`            | `1`     | Fallback reroll count when LuckPerms is absent or grants no `pokesell.rerolls.<n>` node.       |
| `excludeLegendaries` | `true`  | Excludes Cobblemon's `legendary`/`mythical`/`ultra_beast` species labels from the bounty pool. |

### `reset`

| Key    | Default   | Description                                                                                                                                         |
| ------ | --------- | --------------------------------------------------------------------------------------------------------------------------------------------------- |
| `time` | `"00:00"` | `HH:mm`, in `zone`. Malformed values fall back to midnight, logged.                                                                                 |
| `zone` | `""`      | An IANA timezone id, e.g. `"America/New_York"`. Empty uses the server's own timezone. An unrecognised zone falls back to the server's, logged once. |

### `economy`

```json
{
  "economy": {
    "provider": "auto",
    "currency": "impactor:dollars",
    "symbol": "$"
  }
}
```

| Key        | Default              | Description                                                                                                                                                                                                                                                                                                                                                      |
| ---------- | -------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `provider` | `"auto"`             | `auto`, `impactor`, `pebbles` or `item`. `auto` prefers Impactor, then Pebbles.                                                                                                                                                                                                                                                                                  |
| `currency` | `"impactor:dollars"` | The Impactor currency key when `provider` is `impactor`/`auto`, unused for `pebbles`, or the **item id** to pay out (e.g. `"minecraft:diamond"`) when `provider` is `item`. One field, reused, rather than a separate item-currency slot - a currency key and an item id are never both in play at once. Unknown Impactor keys or item ids refuse sales, logged. |
| `symbol`   | `"$"`                | Shown in lore and messages, currency providers only.                                                                                                                                                                                                                                                                                                             |

{% hint style="success" %}
**Selling for items instead of currency?** Set `provider` to `"item"` and `currency` to the item id, e.g. `"minecraft:diamond"` - no Impactor or Pebbles Economy install needed at all. Every item counts as `1`, so a 500-price sale simply pays 500 diamonds - there's no separate conversion rate to configure.
{% endhint %}

### `npc` / `storage` / `security`

| Key                                   | Default | Description                                                                                        |
| ------------------------------------- | ------- | -------------------------------------------------------------------------------------------------- |
| `npc.interactionCooldownTicks`        | `10`    | Per-player cooldown between NPC interactions.                                                      |
| `npc.invulnerable` / `npc.showName`   | `true`  | Applied to entities flagged as NPCs.                                                               |
| `storage.useMongo`                    | `false` | Store player data in MongoDB instead of one JSON file per player.                                  |
| `security.alertAdminsOnDuplicateSale` | `true`  | Alerts `pokesell.admin.alerts` in-game when a duplicate sale is caught (always logged regardless). |

***

## Requirements

* Minecraft 1.21.1 (Fabric)
* Cobblemon 1.8.0+
* Fabric API + Fabric Language Kotlin
* One of: [Impactor](https://impactdev.net/), Pebbles Economy, or nothing at all if `economy.provider` is set to `item`
