Rolled Stats & Stamping
Roll stats onto an item a player already owns - an anvil ritual, a stamped quest reward, or anything else that hands back a better version of the thing you walked in with
Gear Stats covers stats an item is authored with: every copy of that item id carries them, and a pack author decides the numbers up front. This page covers the other half - stats rolled onto one particular stack after a player already owns it. An anvil that upgrades the sword in your hand, a quest that hands back your own pickaxe with a luck roll on it, a rare drop that arrives already rolled.
The stamp paradigm: the item is the record
A stamped item is never a new item asset. The stats are written into that one stack's own metadata, which the game persists with the stack itself - so an enhanced sword survives a chest, a trade, a death, and a server restart with nothing tracking it anywhere else. There is no per-player enhancement table to keep in sync, and no ever-growing catalogue of one-off item ids.
Three consequences worth designing around:
- Enhanced stacks never merge with plain ones. The game compares metadata when deciding whether two stacks combine, so a rolled item stays its own stack. Enhancing something players hold in quantity (arrows, throwables) splinters their inventory.
- The item keeps its own name. Nothing renames a stamped item, and there is no tier or quality vocabulary bolted onto it. What changed shows up in the description, under an Enhancements heading.
- Whoever holds the item holds the enhancement. Give it away and the stats go with it. That is usually the point; if a bonus should belong to the player rather than the object, it belongs in a skill tree node or a mastery track, not a stamp.
What a stamp writes
One record on the stack, holding a map of stat id to points, plus a count of how many times that exact stack has been stamped.
| Field | Type | Default | Description |
|---|---|---|---|
Entries | map of stat id to number | optional | The stats the item carries, summed. A percent-family stat is stored in whole percent points - the same convention an authored StatModifiers block uses, so 10 means +10%, never 0.10. A flat stat stores its raw number. |
StampCount | number | optional | How many completed stamps this stack has taken. A never-stamped item reads as 0. This is what repeat-cost scaling measures, and it only advances on a stamp that actually committed. |
Stamping is additive by default
How a stamped stat reaches the player
Stamped entries are not consulted at the moment of a swing. When the item is equipped, its entries are converted into modifiers on the same native MMO_* stat channels an authored gear stat writes to, and removed again when it is unequipped. There is one aggregation authority, so a stamped MMO_Lifesteal and an armour-authored one are the same number in the same place.
| Field | Type | Default | Description |
|---|---|---|---|
Armour slots | applied | optional | Each worn piece contributes its own stamped entries. |
Held item | applied | optional | The item currently in hand contributes its stamped entries, and swaps them out the moment you switch. |
Offhand / utility slot | applied | optional | The active utility-slot item contributes its stamped entries. |
Anything in the bag | not applied | optional | A stamped item sitting in the inventory grants nothing until it is worn or held. |
Because the channel is the same one everything else writes to, a stamped stat behaves exactly like the authored kind: it feeds plain weapon swings, ability damage that resolves to that skill, and damage over time. An enhanced weapon therefore strengthens the bleed it applies as well as the hit that applied it. A single damage-over-time step can opt out of that with GearEnhancement: false, which fixes that one tick at its authored value; it excludes per-stack enhancement only, and leaves an item's own authored stat modifiers untouched.
Which stat ids to roll
A stat id in a roll pool is the id of a real stat channel - the full MMO_* catalog is the vocabulary to draw from, and a vanilla stat like Mana works just as well. There is no separate list of enhanceable stats: if an item could be authored with it, a stamp can roll it.
Naming a stat, and overriding what it is called
A mod that adds a stat does not have to register a name for it. The client already names every stat it can show, and that is the first place a stamped line looks. A stat the game defines is named correctly with nothing authored, and so is a mod's own stat as soon as that mod ships the label it needs anyway for the ordinary item tooltip.
To give one stat different wording or a colour it would not otherwise get, author a file. It wins over whatever any mod decided in code, so a server owner or a pack can reword or recolour any stat without waiting on whoever invented it.
Server/ZiggfreedCommon/StatDisplays/<StatId>.jsonZiggfreedCommon/StatDisplays/The filename is the stat id. Both fields are optional and independent.
{ "Key": "mypack.stats.swing_speed", "Color": "#e0b341" }| Field | Type | Default | Description |
|---|---|---|---|
Key | translation key | optional | The full key naming this stat, handed a {value} argument holding the signed points - so the value reads "Attack Speed {value}". Omit to keep the name the stat already has. |
Color | hex | optional | A six-digit hex colour for the line. Omit to leave it uncoloured. |
A misspelled stat id rolls silently
Roll pools: the shared table a stamp draws from
Server/ZiggfreedCommon/RollPools/<Name>.jsonZiggfreedCommon/RollPools/A shared-library asset type, so any mod on the server that stamps draws from the same named pools. The pool id is the filename, lower-cased. A pack file replaces a pool of the same id outright - Entries is a whole-table replace, never a per-entry merge.
Prefer a content pack: author one file per roll pool under Server/ZiggfreedCommon/RollPools/ inside a pack folder, zip it, and drop the .zip into your server Mods/ folder. Building the zip.
One candidate outcome is one entry:
| Field | Type | Default | Description |
|---|---|---|---|
Stat | string | optional | The stat id this entry awards. |
Points | { Min, Max, Factors? } | optional | The rolled value: uniform between Min and Max, plus an optional weighted Factors sum so a roll can scale with something about the player. Rounded to a whole number, and a roll that rounds to nothing awards nothing. |
Weight | number | optional | This entry's share of the lottery. Omitting Weight and writing 0 mean different things: omitted is an ordinary entry, a written 0 is never drawn, which is how a pool entry is parked without deleting it. |
Always | boolean | optional | Granted on every stamp, outside the lottery. It costs no pick and its weight is never consulted. |
Weight and Always are independent, and between them cover every shape anyone authors: all weights is a lottery, all Always is a fixed set, and mixing them is a guaranteed baseline plus a lucky extra.
{
"Entries": [
{ "Stat": "MMO_CritChance", "Points": { "Min": 2, "Max": 6 }, "Weight": 1 },
{ "Stat": "MMO_CritMultiplier", "Points": { "Min": 5, "Max": 15 }, "Weight": 1 },
{ "Stat": "MMO_Lifesteal", "Points": { "Min": 2, "Max": 5 }, "Weight": 1 },
{ "Stat": "MMO_CooldownReduction", "Points": { "Min": 2, "Max": 6 }, "Weight": 1 },
{ "Stat": "MMO_Bonus_Fire", "Points": { "Min": 3, "Max": 10 }, "Weight": 0.5 },
{ "Stat": "Mana", "Points": { "Min": 5, "Max": 15 }, "Weight": 1 }
]
}Durability is a stat id you can roll
Durability is reserved: rolling it raises the item's maximum durability instead of storing a stat entry, because durability belongs to the item rather than being a stat channel. The roll engine treats it as an ordinary entry throughout - it takes a pick, it costs budget, a per-stat ceiling holds it - and only the write knows the difference. So an anvil can offer durability as one outcome among many, competing for the same budget as everything else.
Repair already exists, natively
Picks, Unique, and the budget model
A stamp names its candidates (a shared Pool, inline Entries, or both at once - the entry shape is identical either way), how many it draws, and the ceilings the result is held under.
| Field | Type | Default | Description |
|---|---|---|---|
Pool | roll pool id | optional | The shared table to draw candidates from. |
Entries | array | optional | Inline candidates, same shape as a pool entry, for a one-off set not worth naming. |
Picks | { Min, Max } | optional | How many weighted entries one stamp draws. Always entries are extra and never count toward this range. |
Unique | boolean | optional | Never pick the same stat id twice in one stamp. |
Name | translation key | optional | Renames the stamped item, handed the item's own name as an {item} argument, so "Honed {item}" reads correctly on anything. Omit and the item keeps its own name. |
Quality | ItemQuality id | optional | Gives the stamped item that rarity. The game colours the name, the tooltip frame and the slot for it natively. Omit and the rarity is left alone. |
Caps.Budgets | array | optional | Total-point ceilings. Each entry is either a flat { Points } or a factor-scaled { PointsPer, Factors }, never both. The lowest one binds, which is what lets a hard maximum sit beside an allowance that grows with a player's skill. |
Caps.PerStat | map of stat id to number | optional | A ceiling on one stat id, layered on top of the total budget. |
No Picks authored draws zero
Picks has no default. A spec built only from Always entries is then fully predictable, and a spec that simply forgot its Picks is visibly inert instead of quietly handing out free rolls.Every ceiling is measured against what the item already carries, so budgets survive re-stamping: an item enhanced up to its budget cannot be pushed past it by going round again. When the ceilings cut away everything that was rolled, that is a denial, distinct from an ordinary miss - it is the signal to stop before charging the player, so a full item can never be farmed for nothing.
Where a stamp happens
A work station ritual
The richest route: a work station step that consumes reagents, can raise the item's maximum durability, and rolls the stats - the shape behind an anvil enhance ritual. The whole attempt is worked out before anything is touched and only then committed, so a failed stamp leaves the reagents and the item exactly as they were.
"Stamp": {
"Reagents": [ { "ResourceTypeId": "Metal_Bars", "Quantity": 2 } ],
"Durability": { "AddMax": 10 },
"Stats": {
"Pool": "myweaponstats",
"Picks": { "Min": 1, "Max": 2 },
"Unique": true,
"Caps": {
"Budgets": [
{ "Points": 30 },
{ "PointsPer": 0.5, "Factors": [ { "Factor": "hytale:stat", "Param": "MMO_Level_SMITHING" } ] }
],
"PerStat": { "MMO_CritChance": 10 }
}
},
"Economics": { "RepeatCostMultiplier": 0.5 }
}Economics.RepeatCostMultiplier sits beside Stats, not inside Caps, and scales the reagent cost by how many times this stack has been stamped before: ceil(quantity * (1 + multiplier * priorStamps)). It never touches the point budget, so the tenth enhancement costs more, it does not roll smaller. Full field reference: the RPG Stations schema.
The scaled budget in that example reads a skill level off the same requirement factor vocabulary every gate uses, so the allowance grows with the smith rather than with the item.
The engine ships before the anvil does
A reward that hands over pre-stamped gear
Any reward the shared library pays out - a quest, an achievement, a shop offer, a bounty - can use the Stamped_Item kind to hand over an item with stats already on it. Name a Pool to roll them fresh, or write them out exactly with Stats.
{ "Kind": "Stamped_Item",
"Params": { "Item": "Weapon_Sword_Steel", "Count": "1", "Pool": "myweaponstats", "Picks": "2" } }
{ "Kind": "Stamped_Item",
"Params": { "Item": "Weapon_Sword_Steel", "Stats": "MMO_CritChance:5,MMO_Lifesteal:2" } }| Field | Type | Default | Description |
|---|---|---|---|
Item | item id | optional | The item asset id to hand over. Required. |
Count | number | optional | How many. Omit for 1. |
Pool | roll pool id | optional | Roll the stats fresh from this pool at the moment the reward pays out. |
Picks | number | optional | How many entries to draw when rolling a Pool. Omit for 1. |
Stats | "Stat:points,Stat:points" | optional | Exact stats instead of a roll. Written out, this wins over Pool. A malformed pair is skipped rather than failing the reward. |
Both routes write through the same stamper, so a rewarded item carries the identical format, and counts against the identical budgets, as one earned at a station. A reward that has to be replayed later (the player's bag was full when they earned it) hands over the bare item: the roll belonged to a moment that has passed, and re-rolling it days later would quietly award something other than what the player was shown.
A rolled drop from a loot table
A loot table grants through the same reward vocabulary, so any roll can hand over a stamped item by id. This is the route for a rare enhanced find from a block, a mob, or a chest, and it reaches bonus drops too, since those name a loot table.
"Grants": {
"Rewards": [
{ "Kind": "Stamped_Item",
"Params": { "Item": "Weapon_Sword_Steel", "Pool": "myweaponstats", "Picks": "2" } }
]
}The admin command on a held item
/mmoitemenhance rolls stats onto whatever you are holding, for checking that a stat, a tooltip, or an equip bonus behaves. It is a diagnostic lever, and deliberately narrow: it takes no item id and no pool argument, and it draws from a built-in pool suited to the held item rather than from your own RollPools. To roll a specific pool onto a specific item id, use one of the two content routes above.
| Field | Type | Default | Description |
|---|---|---|---|
/mmoitemenhance | admin | optional | Roll stats onto the held item, merging with whatever it already carries. |
/mmoitemenhance replace | admin | optional | Wipe the existing enhancement first, then roll. |
/mmoitemenhance refresh | admin | optional | Re-draw the tooltip from the stats the item already has, with no roll and no stat change - the fix for a stamped item whose description has gone stale. |
Handing a rolled item to a named player, by command
There is no console command that takes an item id and a pool directly. What there is instead: author the stamped item as a reward once, then fire that reward at a player by command. An achievement is the usual carrier because both halves of it are command-reachable.
# Rewards under "Auto" pay out the moment the achievement is granted
/zigprogress achievement give <player> <achievementId>
# Rewards under "Claim" pay out on claim
/zigprogress achievement claim <player> <achievementId>
# /mmoachadmin is the MMO's alias over the same runtime
/mmoachadmin give <player> <achievementId>The grant fires the owning mod's toasts and follow-on grants exactly as a real earn would, so the player receives the rolled item the same way they would have earned it. A quest works the same way via /quest complete. See the /zigprogress family.
Rolled at payout, not at authoring
Pool is rolled when the reward actually pays out, so two players granted the same achievement get different stats. Write Stats instead when every recipient must get the identical item.From code
A companion mod rolls and writes through the shared library, never by touching item metadata itself. The four steps are: read what the item already carries, resolve the plan against it, check for a denial, and apply.
StampSpec spec = StampSpec.of("myweaponstats", null, StampSpec.Picks.of(1, 2), true,
StampSpec.Caps.of(new StampSpec.Budget[] { StampSpec.Budget.flat(30.0) }, null));
// What the item already carries - budgets are measured against this, so re-stamping stays capped.
StampInspection carried = StamperRegistry.inspect(stack);
FactorLookup lookup = new FactorSnapshot(factors, FactorContext.about(ref.getStore(), ref));
StampPlan plan = StampCapEngine.resolve(spec, carried, lookup,
() -> ThreadLocalRandom.current().nextDouble());
if (plan.denied()) {
return; // the item is full: abort BEFORE charging anything
}
Stamper stamper = StamperRegistry.get();
if (stamper != null && !plan.entries().isEmpty()) {
stack = stamper.apply(stack, plan.entries()); // returns a NEW stack
}| Field | Type | Default | Description |
|---|---|---|---|
Items are immutable | rule | optional | apply() returns a new stack. Use the return value and write it back into the slot; the stack you passed in is unchanged. |
Nothing and denied differ | rule | optional | An empty plan is an ordinary miss. A denied plan means every rolled point was cut away by a ceiling - stop before charging, or the item can be farmed for nothing. |
Never write the metadata yourself | rule | optional | Go through the registered stamper. A second write path is a second item format, and every budget check would then be reading half the history. |
A fixed set needs no engine | rule | optional | To grant known stats rather than rolled ones, build the StatRoll list yourself and hand it to the same apply() call. |
What the player sees
A stamped item's description is rewritten on that stack: the item's own flavour prose, then an Enhancements heading, then one coloured line per stat - school colours for school damage, gold for luck, blue for bonus XP, red for crit, crimson for lifesteal, cyan for cooldown reduction, steel for defense. Duplicate stats are summed into one line, and every line is resolved in the reading player's own language.
This tooltip surface has no markup parser
<color> or <b> tag written into an item description renders as visible literal text on a stamped item. An item whose own description contains markup keeps that markup in its normal tooltip, and a stamp on it shows the Enhancements section alone rather than printing tags at the player.Anything else that reports an enhancement - a station's end-of-session summary, for instance - asks the stamper for the same line rather than inventing its own wording, so the summary and the item agree word for word and colour for colour.
One format, owned by the shared library
ZiggfreedCommon writes every stamp on the server, and it is the only thing that does. MMO Skill Tree does not write them, and neither does RPG Stations or any content pack. That is deliberate: two writers would mean two formats on the same items, and every budget check would then be reading half the history, so the ceilings meant to keep gear in line would quietly stop holding. Because there is one writer, a station, a reward, and a drop table each leave a record the other two can read, and an item enhanced by one mod counts against the budgets of them all.
What a mod supplies on top is how its stats read. A stat id belongs to whoever invented it, so only that mod can say a channel is "Critical Chance", in gold, in the reading player's language. MMO Skill Tree supplies exactly that for its own MMO_* vocabulary: the enhancement tooltip and the wording any surface reporting an enhancement shares with it. The numbers and the way they read are written in one operation, so a stamped item can never carry stats its tooltip does not show.
A server running the library alone still stamps correctly
A pack author registers nothing for any of this. Authoring a pool and a stamp step is the whole job; where the numbers land is settled before your content loads.
Naming and rarity: what the item becomes
A pool or a stamp can say what an enhanced item is called and how rare it looks. Both are authored or absent - nothing is ever renamed or re-tinted automatically, and there is no built-in tier vocabulary, so an item you did not author a name for is the item it always was with its stats spelled out underneath.
{
"StampName": "mypack.item.honed",
"Quality": "Rare",
"Entries": [ ... ]
}A stamp overrides its pool per field, so naming an item without restating the rarity does not silently drop the rarity. The name key is handed the item's own name, which is what lets one key serve every item it can land on and still localize:
mypack.item.honed = Honed {item}
-> "Honed Steel Sword"
-> "Honed Iron Pickaxe"Quality names one of the game's own quality assets, which is the same per-instance rarity channel vanilla items use. The client renders the name colour, the tooltip frame, the slot and a localized rarity label from it, so an enhanced item looks special with no interface work.
Worked example: a tool enhancement
A pool that gives a pickaxe a guaranteed mining-luck baseline plus one lucky extra, held under a flat budget:
{
"Entries": [
{ "Stat": "MMO_Luck_MINING", "Points": { "Min": 2, "Max": 4 }, "Always": true },
{ "Stat": "MMO_BonusXp_MINING", "Points": { "Min": 3, "Max": 8 }, "Weight": 2 },
{ "Stat": "MMO_Luck", "Points": { "Min": 1, "Max": 3 }, "Weight": 1 },
{ "Stat": "MMO_BonusXp", "Points": { "Min": 2, "Max": 5 }, "Weight": 1 }
]
}"Stats": {
"Pool": "mytoolstats",
"Picks": { "Min": 1, "Max": 1 },
"Unique": true,
"Caps": { "Budgets": [ { "Points": 24 } ], "PerStat": { "MMO_Luck_MINING": 12 } }
}Every stamp grants the mining-luck baseline and exactly one of the three weighted extras. The item can be brought back until it holds 24 points in total, and never carries more than 12 points of mining luck however many times it is enhanced. Nothing about the pickaxe's item asset changes; the player's own pickaxe simply gets better.