> For the complete documentation index, see [llms.txt](https://lethium.gitbook.io/lethium-docs/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://lethium.gitbook.io/lethium-docs/dev-api/stats.md).

# Stats and contributors

StatBlock, StatType, StatContributor, and the StatCalculateEvent.

The stat system is how every module gives bonuses. If you understand it, you can give players stats from your own plugin. First read [Stats and limits](/lethium-docs/configuration/stats-and-limits.md) for the owner-facing explanation.

### `StatType`

An enum of every channel. **Percent channels** use a percent (a bonus of `50` means +50%). **Flat channels** are plain numbers.

```
CURRENCY, VAULT, ESSENCE, DAMAGE, DEFENSE, LUCK, XP, ATTACK_SPEED, CRIT_CHANCE, CRIT_DAMAGE,
HEALTH_REGEN, MAX_HEALTH, MOVEMENT_SPEED, LIFESTEAL, BOSS_DAMAGE,
POWER_FLAT, ATTACK_SPEED_FLAT, BOSS_DAMAGE_FLAT, HEALTH_FLAT, HP_REGEN_FLAT,
DEFENSE_FLAT, MOVEMENT_SPEED_FLAT, LIFESTEAL_FLAT
```

`DAMAGE` is shown to players as **Power**, `CURRENCY` as **Coins**. `type.isFlat()` tells flat channels apart.

### `StatBlock`

An immutable, sparse map from `StatType` to a value.

```java
StatBlock block = StatBlock.builder()
        .add(StatType.DAMAGE, 25.0)        // +25% Power
        .add(StatType.CURRENCY, 10.0)      // +10% Coins
        .build();

block.percent(StatType.DAMAGE);            // 25.0
block.multiplier(StatType.DAMAGE);         // 1.25  (1 + percent / 100)
```

| Method                                 | Meaning                                               |
| -------------------------------------- | ----------------------------------------------------- |
| `StatBlock.ZERO`                       | No bonus.                                             |
| `StatBlock.of(type, percent)`          | A single stat.                                        |
| `StatBlock.builder()`                  | `.set(type, value)`, `.add(type, value)`, `.build()`. |
| `percent(type)` and `multiplier(type)` | Read.                                                 |
| `nonZero()`                            | The non-zero entries as a map.                        |
| `plus(StatBlock)`                      | Combine (adds).                                       |
| `scaled(double factor)`                | Multiply every value.                                 |
| `scaledPerType(Map<StatType, Double>)` | Multiply each stat by its own factor.                 |
| `capped(Map<StatType, Double>)`        | Apply ceilings (like `limit-stats`).                  |

### How stats combine

ClickEm combines contributors so that **Power, Coins, Essence and Boss Damage multiply** (each contributor is one factor) and **every other stat adds**. So a contributor of `+100% DAMAGE` **doubles** the player's Power, it does not add 100 points to a sum. Design your bonus accordingly. After combining, `limit-stats` from `config.yml` caps the total.

### `StatContributor`

```java
public interface StatContributor {
    StatBlock contribution(Player player);
}
```

```java
ClickEmAPI api = ClickEmAPI.get();
StatContributor guild = player -> {
    int rank = myGuildRank(player);
    return StatBlock.builder().add(StatType.LUCK, rank * 2.0).build();
};
api.addStatContributor(this, guild);      // `this` is your plugin; removed automatically when you disable
```

Rules:

* `contribution` is called **often** (every combat hit and every placeholder read). Make it **fast and thread-safe**, and cache what you can. No database or I/O.
* Return `StatBlock.ZERO` for "no bonus". Never return `null`.
* Changes you want to apply right away need nothing special. The value is read the next time stats are calculated (there is a one-tick cache for placeholders).
* Every contributor also shows up in `/profile` totals, leaderboards and `%clickem_stat_*%`.

### `StatCalculateEvent`

A **notify-only** event fired when the combined stats of a player are calculated, **only while someone is listening**. You can change the result:

```java
@EventHandler
public void onStats(StatCalculateEvent event) {
    if (event.player().hasPermission("myplugin.boost")) {
        event.setStats(event.stats().plus(StatBlock.of(StatType.CRIT_CHANCE, 5)));
    }
}
```

Prefer a `StatContributor` for normal bonuses. Use this event when you need to post-process the final total.

{% hint style="warning" %}
Do not call `api.stats(player)` or `api.stat(...)` inside this handler. Use `event.stats()`.
{% endhint %}


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation by asking a question.

Perform an HTTP GET request on the following URL with the `ask` and `goal` query parameters:

```
GET https://lethium.gitbook.io/lethium-docs/dev-api/stats.md?ask=<question>&goal=<user_goal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is what the user is ultimately trying to achieve, the reason they need the answer. Sharing it helps GitBook give you a better, more relevant answer. A goal is most helpful when it describes the outcome the user wants rather than restating the question. For example, with `ask=how do I create an API token`, a goal like `automate deployments from our CI pipeline` lets GitBook tailor the answer to that use case.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
