Stat Caps & Clamps
Where every stacking ceiling lives, and how to retune one
Overview
Stat ceilings are authored data, not numbers baked into the jar. One file per capped key under Server/MMOSkillTree/ModifierClamps/ says how far that key can stack, a content pack can overlay it, and the server owner has the last word in mods/mmoskilltree/ability-clamps.json. Nothing has to be recompiled to move a cap.
mods/mmoskilltree/ability-clamps.jsonServer/MMOSkillTree/ModifierClamps/*.jsonThe jar ships one clamp file per capped key. A pack can overlay a partial body; the owner file always wins. Every bound folds independently, so a file that mentions one bound inherits the other three.
The name says ability, the reach is wider
ability-clamps.json because the table started as the ability-modifier bound list. It is read at four other places besides a cast: the weapon swing, the gathering luck roll, the defense calculation, and the stat breakdown screen. Two of the shipped entries never touch an ability at all.The four bounds
A clamp file carries up to four numbers. Two bound the value itself; two bound the summed percent bucket before it multiplies anything. Every one is optional, and an absent bound means that axis is unbounded.
| Field | Type | Default | Description |
|---|---|---|---|
Min | number | unbounded | Floor on the folded value. Most shipped files set 0, so a stack of reductions cannot invert a stat into its opposite. |
Max | number | unbounded | Ceiling on the folded value. This is the runaway stop for a repeatable purchase. |
MinTotalPercent | number | unbounded | Floor on the summed percent bucket, as a fraction. -0.9 means the most that stacked reductions can take off is 90%. |
MaxTotalPercent | number | unbounded | Ceiling on the summed percent bucket, as a fraction. 30 means percent bonuses stop compounding at 30x. |
IsParamKey | boolean | true | Set false on a file that only bounds an engine calculation. The bounds still apply, but an ability modifier naming that id is reported by the content audit instead of quietly doing nothing. |
Widen by writing a bigger number, not by deleting a line
Max out inherits whatever the layer below it set, so removing a line does not lift a cap. To raise a ceiling, author the larger number.Retuning a cap
Create or edit mods/mmoskilltree/ability-clamps.json. Each entry is keyed by the clamp id and takes the same fields the shipped file uses. Mention only what you are changing.
{
"schemaVersion": 1,
"clamps": {
"DefenseReduction": { "Max": 0.75 },
"Lifesteal": { "Max": 40.0 },
"Damage": { "MaxTotalPercent": 12.0 }
}
}That file lowers the damage-reduction ceiling from 90% to 75%, stops lifesteal stacking at 40 health per swing, and caps compounding damage percent at 12x. Everything the entries did not mention keeps its shipped bound. Run /mmoconfig reload to pick the file up; an entry you deleted outright needs a restart, since the fold merges rather than replaces.
The two whole-pipeline caps
Two clamp files bound a calculation rather than a modifier key, and they are the ones most worth knowing about, because they bound every source at once: skill tree, mastery, gear, potions, and pack content together.
| File | What it bounds | Shipped |
|---|---|---|
HitDamage | The summed percent damage bonus on any weapon swing, from every source at once | MinTotalPercent -0.9, MaxTotalPercent 30 |
DefenseReduction | The total percent damage reduction a defender gets from defense, after armor penetration | Max 0.9 |
Defense reduction
The 90% ceiling is the convention every percent reduction in the mod follows: stacked defense can take a hit down to a tenth, never to nothing. Armor penetration is applied first, so penetration shrinks what defense grants and this ceiling is the outer bound nothing gets past. If the clamp file is missing entirely the mod falls back to 90% rather than running uncapped.
Per-school resistance carries its own matching 0.9 ceiling on the SchoolResist file, so no single damage school can be stacked into immunity either.
Hit damage
Every percent damage bonus that reaches a weapon swing lands in one bucket, and HitDamage bounds that bucket before it multiplies. Lower MaxTotalPercent here to flatten late-game damage across the board without touching a single skill tree node.
A separate ceiling sits above this one
maxPreDefenseMultiplier in skill-config.json (default 4.0) caps the compounded multiplier from armed buffs, marks, crits, and damage bonuses together, after the percent bucket has already been bounded. See the config reference.Every shipped clamp
One file per key, ordered by where it folds: ability level (patched once per cast), body level (folded at the leaf that declares it), and seam (never touches an ability at all).
| Key | Group | What it controls | Shipped bounds |
|---|---|---|---|
cooldownMs | ability | An ability cooldown, in milliseconds | Min 0, Max 600000 |
cost | ability | Mana or stamina an ability spends | Min 0, Max 100000 |
damage | body | Damage a body leaf deals | Min 0, Max 100000, MinTotalPercent -0.9, MaxTotalPercent 30 |
damageMultiplier | body | A multiplier applied to that damage | Min 0, Max 50, MinTotalPercent -0.9, MaxTotalPercent 20 |
armMultiplier | body | Weapon-scaling multiplier on an armed hit | Min 0, Max 50, MinTotalPercent -0.9, MaxTotalPercent 20 |
armFlatBonus | body | Flat bonus added to an armed hit | Min 0, Max 1000, MinTotalPercent -0.9, MaxTotalPercent 10 |
damageRadius | body | Radius of a damaging area, in blocks | Min 0, Max 100, MinTotalPercent -0.9, MaxTotalPercent 10 |
radius | body | Radius of a non-damaging area, in blocks | Min 0, Max 100, MinTotalPercent -0.9, MaxTotalPercent 10 |
distance | body | Range, dash length, or projectile reach | Min 0, Max 500, MinTotalPercent -0.9, MaxTotalPercent 10 |
durationMs | body | How long an effect or zone lasts | Min 0, Max 3600000, MinTotalPercent -0.9, MaxTotalPercent 10 |
iframesDurationMs | body | Invulnerability window on a dodge or dash | Min 0, Max 10000, MinTotalPercent -0.9, MaxTotalPercent 10 |
pierceCount | body | How many targets a projectile passes through | Min 0, Max 50 |
onHitHealPercent | body | Share of damage returned as healing | Min 0, Max 1, MinTotalPercent -0.9, MaxTotalPercent 10 |
lifesteal | seam | Health returned per weapon swing | Min 0, Max 100, MinTotalPercent -0.9, MaxTotalPercent 10 |
comboFinisherBonus | seam | Extra damage on a combo finisher | Min 0, Max 500, MinTotalPercent -0.9, MaxTotalPercent 10 |
lootMultiplier | seam | Multiplier on a bonus-drop roll | Min 0, Max 5 |
schoolResist | seam | Per-school damage resisted, as a fraction | Min 0, Max 0.9 |
The file name is the key with a capital first letter: lifesteal is bounded by ModifierClamps/Lifesteal.json. For what each key means to an ability author, and how a pack declares a brand new one, see the ability authoring page.
Where each bound is enforced
The same table is read at five separate points in play.
| Field | Type | Default | Description |
|---|---|---|---|
Ability cast | per cast | optional | Every modifier folded into a cast is bounded by its key before the ability body fires. |
Weapon swing | per hit | optional | The summed percent damage bonus is bounded by HitDamage, and lifesteal and combo finisher totals by their own keys. |
Incoming damage | per hit | optional | The defender's total reduction is bounded by DefenseReduction after armor penetration, and per-school resistance by SchoolResist. |
Gathering | per roll | optional | A bonus-drop multiplier is bounded by LootMultiplier. |
Stat breakdown screen | display | optional | The displayed mastery total is bounded the same way the hit is, so the number a player reads is the number they get. |
Caps that live elsewhere
A handful of ceilings sit outside the clamp table, in their own config files.
| Field | Type | Default | Description |
|---|---|---|---|
maxPreDefenseMultiplier | number | 4.0 | In skill-config.json. The compounded pre-defense damage multiplier ceiling. |
maxBoostMultiplier | number | 5.0 | In skill-config.json. The hard ceiling on stacked XP boosts. See XP boost stacking. |
maxPersonalBoosts | number | 3 | In skill-config.json. How many personal boosts one player can hold at once. |
maxGlobalBoosts | number | 3 | In skill-config.json. How many server-wide boosts can run at once. |
maxActiveQuests | number | 10 | In skill-config.json. How many quests a player can carry. |
Clamp.MinPower / MaxPower | number | 1 / 200 | In power-level.json. The Power Level range. See Power Level. |
Per-skill max level | number | 100 | In custom-skills.json or skill-config.json. See per-skill max levels. |
What carries no cap
Not every stat has a ceiling. These families are summed and read with nothing bounding the total, and there is no owner-facing setting that changes that today. Adding a clamp file for one of them declares the name but does not bound it, because nothing reads a clamp at those points.
| Stat family | Behaviour |
|---|---|
| Critical chance | Sums freely; at 100% every hit crits |
| Critical multiplier | Sums freely on top of the 1.5x base |
| Luck | Feeds the bonus-drop roll with no ceiling of its own |
| Bonus XP | Separate from the XP boost stacking cap, and unbounded |
| Cooldown reduction | The stat is unbounded; the resulting cooldown still floors at 0ms |
| Effect duration | Sums freely |
The stat files under Server/Entity/Stats are not a cap surface
Min and a Max, which reads like a bound and is not one: the Max slot is where the folded value is stored. Editing it shifts the starting value of the stat instead of capping it. Caps live in ModifierClamps, and nowhere else.Checking your work
/mmoconfig validate runs the content audit, which reports a modifier naming an unknown key, a modifier naming one of the two reserved ids, and per-school resistance authored at or above the ceiling the reconciler enforces. A cap you have lowered shows up there as soon as shipped content exceeds it.
The clamp table shipped in 1.6.0 Since 1.6.0 and the owner layer alongside it.