> ## Documentation Index
> Fetch the complete documentation index at: https://docs.rpg-leveling.zuxaw.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Custom passives

> Add a passive in JSON, show it on the class sheet, and run your own effect from a command, a key, a potion, or combat.

# Custom passives

A custom passive is an id you add to `PassivesConfig.json` and to a class `Passives` array. It shows on the class sheet (name, icon, and the text for the tier on screen). It runs only when the player has that class and the tier is high enough.

Built-in passives (High Guard, Thick Skin, and the rest) keep their current combat behavior. Registering those ids does nothing.

## JSON

`mods/Zuxaw_RPGLeveling/Classes/PassivesConfig.json`:

```json theme={null}
{
  "Id": "ExecuteLow",
  "Unlock": 0,
  "Name": "Execute",
  "Icon": "Weapon_Sword_Iron",
  "Template": "Target below {HpThresholdPercent}% HP: +{DamagePercent}% damage",
  "T": {
    "0": { "DamagePercent": 20, "HpThresholdPercent": 30 },
    "1": { "DamagePercent": 25 },
    "2": { "DamagePercent": 30 },
    "3": { "DamagePercent": 35 },
    "4": { "DamagePercent": 40 }
  }
}
```

On the class file:

```json theme={null}
"Passives": ["HighGuard", "ExecuteLow"]
```

* **Name**, **Icon**, and **Template** are what the class sheet shows. **Icon** is a Hytale item id.
* **Template** replaces `{Key}` with the numbers in **T** for the tier tab the player is looking at (T0 through T4). A later tier only needs the keys that change.
* **Unlock** hides the row until that tier.
* A language key `passives.<id>.name` or `passives.<id>.template` overrides **Name** and **Template** when you want a translation. Otherwise the JSON is enough.

Reload with `/lvl reload` or a restart. The sheet draws four passives. Handlers still run for every passive on the class.

## Code

Register once when your mod starts.

A command, a key, a potion, or any other moment your code already handles:

```java theme={null}
RPGLevelingAPI api = RPGLevelingAPI.get();

api.registerPassive("GuardPulse", ctx -> {
    apply(ctx.player(), ctx.get("Radius"));
});
```

From that command, key, or potion:

```java theme={null}
api.triggerPassive(player, "GuardPulse");
```

That handler does not run when the player hits something. `triggerPassive` does nothing if the player does not have the class, the tier is too low, or the id is a built-in passive.

Damage dealt, damage taken, or a kill:

```java theme={null}
api.registerPassive("ExecuteLow", PassiveWhen.HIT, ctx -> {
    if (ctx.targetHpPercent() < ctx.get("HpThresholdPercent")) {
        ctx.addDamagePercent(ctx.get("DamagePercent"));
    }
});
```

`PassiveWhen.HURT` is damage taken. `PassiveWhen.KILL` is a kill. Those handlers do not run from `triggerPassive`.

`ctx.get("DamagePercent")` is the merged **T** value for the player's tier. `ctx.player()` is the player. On a hit, `ctx.addDamagePercent` and `ctx.addHeal` change that hit. Several handlers for the same id and the same moment add together.

`api.hasPassive(player, "GuardPulse")` is true only when the selected class lists it and the tier has unlocked it.

`api.unregisterPassive("GuardPulse")` removes every handler for that id.

A new moment (a potion, a key, another mod's event) does not need a RPG Leveling update. Call `triggerPassive` from the code that already sees that moment.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.