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. Several 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 raises the damage-reduction ceiling from 80% to 75% (expect to lower mob damage to match), 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; a bound you delete from the file goes back to the shipped value on the same reload. The Modifier Clamps page in /mmoadmin edits the same bounds in game and writes this file for you.
The whole-pipeline caps
The clamp files below bound a calculation rather than a modifier key, and they are the ones most worth knowing about, because each one bounds every source that feeds it at once: skill tree, mastery, gear, potions, and pack content together.
| File | What it bounds | Shipped |
|---|---|---|
HitDamage | The summed global, per-skill and per-ability percent damage bonus on any weapon swing (a per-school bonus is not in it) | MinTotalPercent -0.9, MaxTotalPercent 60 |
DefenseReduction | The total percent damage reduction a defender gets from defense, after armor penetration | Max 0.8 |
Defense reduction
The defense stat takes at most 80% off a hit. Since 1.7.1 the shipped caps are safety rails, set well above what shipped content reaches, so they stop a runaway stack rather than set the balance. Worn armor's own resistance applies first and multiplies with it. Base-game armor resists nothing elemental, so on an elemental hit the defense stat alone leaves at least 20% of it, before any school resistance. Armor penetration is applied before the cap, so penetration shrinks what defense grants and this ceiling is the outer bound nothing gets past. If the clamp file is missing, the mod falls back to a built-in ceiling and never runs uncapped.
School resistance
Resistance to any one damage type stops at 80% (Max 0.8, a fraction, in the SchoolResist file). Since 1.7.2 a skill-tree node, mastery, enhanced gear and any item, from this mod or another, all add onto the DamageResist_<Type> stats from Ziggfreed's CommonLib, one total per type. CommonLib mirrors each total into one of the game's own resistance effects, so it adds to your armor's resistance to that type. The cap bounds each type on its own, never the sum of two, and worn armor's own resistance is not counted against it.
CommonLib ships its own per-type ceiling, the DamageResistMax stat. On a server running MMO Skill Tree the SchoolResist clamp takes its place, so retune the cap here or in ability-clamps.json: a DamageResistMax file or an item that raises it changes nothing while this mod is installed.
Critical chance
Critical chance has no clamp since 1.7.1: a hit rolls once, so a chance past 1 already crits every hit, and the old CritChance file is retired. An owner's CritChance override in ability-clamps.json now does nothing. Critical damage is a separate stat with no cap of its own.
Hit damage
Every global, per-skill and per-ability 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. Since 1.7.1 a per-school damage bonus is not in that bucket: it applies after armor, through the per-type damage step in Ziggfreed's CommonLib, multiplies with the bucket, and has no cap.
A separate ceiling sits above this one
maxPreDefenseMultiplier in skill-config.json (default 4.0) caps the compounded multiplier from an armed buff and a mark on the same hit. It does not touch crits or damage bonuses: the percent bucket has its own bound in HitDamage, and a school bonus has no cap. 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 1200000 |
cost | ability | Mana or stamina an ability spends | Min 0, Max 200000 |
damage | body | Damage a body leaf deals | Min 0, Max 200000, MinTotalPercent -0.9, MaxTotalPercent 60 |
damageMultiplier | body | A multiplier applied to that damage | Min 0, Max 100, MinTotalPercent -0.9, MaxTotalPercent 40 |
armMultiplier | body | Weapon-scaling multiplier on an armed hit | Min 0, Max 100, MinTotalPercent -0.9, MaxTotalPercent 40 |
armFlatBonus | body | Flat bonus added to an armed hit | Min 0, Max 2000, MinTotalPercent -0.9, MaxTotalPercent 20 |
damageRadius | body | Radius of a damaging area, in blocks | Min 0, Max 200, MinTotalPercent -0.9, MaxTotalPercent 20 |
radius | body | Radius of a non-damaging area, in blocks | Min 0, Max 200, MinTotalPercent -0.9, MaxTotalPercent 20 |
distance | body | Range, dash length, or projectile reach | Min 0, Max 1000, MinTotalPercent -0.9, MaxTotalPercent 20 |
durationMs | body | How long an effect or zone lasts | Min 0, Max 7200000, MinTotalPercent -0.9, MaxTotalPercent 20 |
iframesDurationMs | body | Invulnerability window on a dodge or dash | Min 0, Max 20000, MinTotalPercent -0.9, MaxTotalPercent 20 |
pierceCount | body | How many targets a projectile passes through | Min 0, Max 100 |
onHitHealPercent | body | Share of damage returned as healing | Min 0, Max 2, MinTotalPercent -0.9, MaxTotalPercent 20 |
lifesteal | seam | Health returned per weapon swing | Min 0, Max 200, MinTotalPercent -0.9, MaxTotalPercent 20 |
comboFinisherBonus | seam | Extra damage on a combo finisher | Min 0, Max 1000, MinTotalPercent -0.9, MaxTotalPercent 20 |
lootMultiplier | seam | Multiplier on a bonus-drop roll | Min 0, Max 10 |
schoolResist | seam | Per-school damage resisted, as a fraction | Min 0, Max 0.8 |
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 each damage type's resistance by SchoolResist, which CommonLib holds the player to as it keeps their resistance in step. |
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 | No clamp since 1.7.1: a chance past 100% already crits every hit |
| School damage bonus | No cap since 1.7.1: applies after armor and multiplies with the global and per-skill bonus |
| 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, but a cooldown never drops below 5% of its base |
| 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 reserved ids above, and skill-tree School Resistance nodes for one school that add up to the SchoolResist ceiling or past it. 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.